diff --git a/Dockerfile b/Dockerfile index c446db3..63b18e9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,8 +2,10 @@ FROM alpine:3.20 AS ptouch-build RUN apk add --no-cache build-base cmake gd-dev gettext-dev git libusb-dev pkgconf RUN git clone --depth 1 --branch farix-main https://github.com/farixembedded/ptouch-print.git /src/ptouch +COPY patches/ptouch-print-cut-options.patch /tmp/ptouch-print-cut-options.patch WORKDIR /src/ptouch -RUN cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \ +RUN patch -p1 < /tmp/ptouch-print-cut-options.patch \ + && cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \ && cmake --build build \ && install -m 0755 build/ptouch-print /usr/local/bin/ptouch-print diff --git a/README.md b/README.md index 7a02afd..efd8274 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Die Weboberflaeche bildet die wichtigsten P-touch-Editor-Funktionen nach: - Layout speichern/laden im Browser - serverseitige Vorschau und Druck ueber `ptouch-print` - vereinfachte Mobile-Webansicht unter `/mobile` +- Schnittmodi: normal, Halbschnitt ein/aus, Kettendruck, Schnittmarke, Kettendruck mit Schnittmarke ## Wichtig fuer den PT-P700 @@ -103,7 +104,9 @@ Umgebungsvariablen: - `LABEL_DPI=180` passt zum PT-P700. - `LABEL_DIR=/data/labels` speichert erzeugte PNG-Dateien. -Wenn `ptouch-print` auf deinem Setup andere Optionen braucht, nur `PRINT_COMMAND` anpassen. Der Platzhalter `{image}` wird von der App ersetzt. Wenn dein Kommando selbst Kopien unterstuetzt, kannst du zusaetzlich `{copies}` verwenden; sonst druckt die App mehrere Kopien durch mehrere Druckaufrufe. +Wenn `ptouch-print` auf deinem Setup andere Optionen braucht, nur `PRINT_COMMAND` anpassen. Der Platzhalter `{image}` wird von der App ersetzt. Wenn dein Kommando selbst Kopien unterstuetzt, kannst du zusaetzlich `{copies}` verwenden; sonst druckt die App mehrere Kopien durch mehrere Druckaufrufe. Fuer Schnittoptionen kannst du optional `{cut_args}` im Kommando platzieren; ohne Platzhalter haengt die App die Schnittargumente automatisch an. + +Hinweis: Der PT-P700-Halbschnitt ist in upstream `ptouch-print` nicht als CLI-Option vorhanden. Dieses Projekt patcht `ptouch-print` beim Containerstart um `--halfcut` und `--no-halfcut`. ## USB testen diff --git a/app/app.py b/app/app.py index ed2c4ed..20e9d58 100644 --- a/app/app.py +++ b/app/app.py @@ -2,10 +2,12 @@ from __future__ import annotations import base64 import io +import signal import os import re import shlex import subprocess +import threading import uuid from pathlib import Path from typing import Any @@ -34,17 +36,40 @@ DEFAULT_PRINT_COMMAND = "ptouch-print --image {image}" PRINT_COMMAND = os.environ.get("PRINT_COMMAND", DEFAULT_PRINT_COMMAND) PRINT_ENABLED = os.environ.get("PRINT_ENABLED", "1").lower() in {"1", "true", "yes", "on"} DPI = int(os.environ.get("LABEL_DPI", "180")) +PTOUCH_SUPPORTS_HALFCUT: bool | None = None TAPE_WIDTHS_MM = [3.5, 6, 9, 12, 18, 24] +PRINTABLE_TAPE_PX = { + 3.5: 24, + 6.0: 32, + 9.0: 52, + 12.0: 76, + 18.0: 120, + 24.0: 128, +} app = Flask(__name__) app.secret_key = os.environ.get("SECRET_KEY", "change-me") +PRINT_LOCK = threading.Lock() def mm_to_px(mm: float) -> int: return max(1, round(mm / 25.4 * DPI)) +def printable_px_for_tape(tape_width_mm: float) -> int: + normalized = float(tape_width_mm) + return PRINTABLE_TAPE_PX.get(normalized, mm_to_px(normalized)) + + +def render_scale_for_tape(tape_width_mm: float) -> float: + return printable_px_for_tape(tape_width_mm) / float(tape_width_mm) + + +def scaled_mm_to_px(mm: float, scale: float) -> int: + return max(1, round(mm * scale)) + + def clamp(value: float, low: float, high: float) -> float: return max(low, min(high, value)) @@ -67,11 +92,11 @@ def new_label_image(width_px: int, height_px: int) -> Image.Image: return image -def object_box(obj: dict[str, Any]) -> tuple[int, int, int, int]: - x = mm_to_px(float(obj.get("x", 0))) - y = mm_to_px(float(obj.get("y", 0))) - w = mm_to_px(float(obj.get("w", 10))) - h = mm_to_px(float(obj.get("h", 5))) +def object_box(obj: dict[str, Any], scale: float) -> tuple[int, int, int, int]: + x = scaled_mm_to_px(float(obj.get("x", 0)), scale) + y = scaled_mm_to_px(float(obj.get("y", 0)), scale) + w = scaled_mm_to_px(float(obj.get("w", 10)), scale) + h = scaled_mm_to_px(float(obj.get("h", 5)), scale) return x, y, max(1, w), max(1, h) @@ -92,10 +117,10 @@ def wrap_text(draw: ImageDraw.ImageDraw, text: str, font, max_width: int) -> lis return lines -def draw_text(draw: ImageDraw.ImageDraw, obj: dict[str, Any]) -> None: - x, y, w, h = object_box(obj) +def draw_text(draw: ImageDraw.ImageDraw, obj: dict[str, Any], scale: float) -> None: + x, y, w, h = object_box(obj, scale) text = str(obj.get("text", "Text")) - font_size = mm_to_px(float(obj.get("fontSizeMm", 5))) + font_size = scaled_mm_to_px(float(obj.get("fontSizeMm", 5)), scale) font = get_font(font_size, bool(obj.get("bold", False))) align = obj.get("align", "left") valign = obj.get("valign", "middle") @@ -122,9 +147,9 @@ def draw_text(draw: ImageDraw.ImageDraw, obj: dict[str, Any]) -> None: cy += line_h + gap -def draw_shape(draw: ImageDraw.ImageDraw, obj: dict[str, Any]) -> None: - x, y, w, h = object_box(obj) - line = max(1, mm_to_px(float(obj.get("lineMm", 0.25)))) +def draw_shape(draw: ImageDraw.ImageDraw, obj: dict[str, Any], scale: float) -> None: + x, y, w, h = object_box(obj, scale) + line = max(1, scaled_mm_to_px(float(obj.get("lineMm", 0.25)), scale)) shape = obj.get("shape", "rect") fill = 1 if obj.get("fill", False) else None box = [x, y, x + w, y + h] @@ -150,10 +175,10 @@ def decode_data_image(data_url: str) -> Image.Image: return Image.open(io.BytesIO(base64.b64decode(data_url))) -def draw_qr(image: Image.Image, obj: dict[str, Any]) -> None: +def draw_qr(image: Image.Image, obj: dict[str, Any], scale: float) -> None: if qrcode is None: raise RuntimeError("qrcode ist nicht installiert.") - x, y, w, h = object_box(obj) + x, y, w, h = object_box(obj, scale) qr = qrcode.QRCode(border=1, box_size=10) qr.add_data(str(obj.get("text", "https://example.local"))) qr.make(fit=True) @@ -162,10 +187,10 @@ def draw_qr(image: Image.Image, obj: dict[str, Any]) -> None: paste_mono(image, qr_img, x, y, side, side) -def draw_barcode(image: Image.Image, obj: dict[str, Any]) -> None: +def draw_barcode(image: Image.Image, obj: dict[str, Any], scale: float) -> None: if barcode is None or ImageWriter is None: raise RuntimeError("python-barcode ist nicht installiert.") - x, y, w, h = object_box(obj) + x, y, w, h = object_box(obj, scale) writer = ImageWriter() code = barcode.get("code128", str(obj.get("text", "1234567890")), writer=writer) buffer = io.BytesIO() @@ -174,11 +199,11 @@ def draw_barcode(image: Image.Image, obj: dict[str, Any]) -> None: paste_mono(image, Image.open(buffer), x, y, w, h) -def draw_table(draw: ImageDraw.ImageDraw, obj: dict[str, Any]) -> None: - x, y, w, h = object_box(obj) +def draw_table(draw: ImageDraw.ImageDraw, obj: dict[str, Any], scale: float) -> None: + x, y, w, h = object_box(obj, scale) rows = max(1, int(obj.get("rows", 2))) cols = max(1, int(obj.get("cols", 2))) - line = max(1, mm_to_px(float(obj.get("lineMm", 0.2)))) + line = max(1, scaled_mm_to_px(float(obj.get("lineMm", 0.2)), scale)) draw.rectangle([x, y, x + w, y + h], outline=1, width=line) for col in range(1, cols): cx = x + round(w * col / cols) @@ -188,6 +213,16 @@ def draw_table(draw: ImageDraw.ImageDraw, obj: dict[str, Any]) -> None: draw.line((x, cy, x + w, cy), fill=1, width=line) +def draw_cutmark(draw: ImageDraw.ImageDraw, width_px: int, height_px: int, scale: float) -> None: + x = max(0, width_px - scaled_mm_to_px(1.5, scale)) + dash = max(2, scaled_mm_to_px(1, scale)) + gap = max(2, scaled_mm_to_px(0.7, scale)) + y = 0 + while y < height_px: + draw.line((x, y, x, min(height_px, y + dash)), fill=1, width=1) + y += dash + gap + + def render_design(design: dict[str, Any]) -> Path: tape_width = float(design.get("tapeWidthMm", 24)) if tape_width not in TAPE_WIDTHS_MM: @@ -199,7 +234,14 @@ def render_design(design: dict[str, Any]) -> Path: else: canvas_w_mm, canvas_h_mm = length, tape_width - image = new_label_image(mm_to_px(canvas_w_mm), mm_to_px(canvas_h_mm)) + scale = render_scale_for_tape(tape_width) + image = new_label_image(scaled_mm_to_px(canvas_w_mm, scale), scaled_mm_to_px(canvas_h_mm, scale)) + max_height_px = printable_px_for_tape(tape_width) + if image.height > max_height_px: + raise ValueError( + f"Gerendertes Etikett ist zu hoch ({image.height}px). " + f"Maximal fuer {tape_width:g} mm Band sind {max_height_px}px." + ) draw = ImageDraw.Draw(image) for obj in design.get("objects", []): @@ -207,35 +249,93 @@ def render_design(design: dict[str, Any]) -> Path: continue obj_type = obj.get("type") if obj_type == "text" or obj_type == "symbol": - draw_text(draw, obj) + draw_text(draw, obj, scale) elif obj_type == "shape": - draw_shape(draw, obj) + draw_shape(draw, obj, scale) elif obj_type == "line": - draw_shape(draw, {**obj, "shape": "line"}) + draw_shape(draw, {**obj, "shape": "line"}, scale) elif obj_type == "table": - draw_table(draw, obj) + draw_table(draw, obj, scale) elif obj_type == "image" and obj.get("dataUrl"): - x, y, w, h = object_box(obj) + x, y, w, h = object_box(obj, scale) paste_mono(image, decode_data_image(str(obj["dataUrl"])), x, y, w, h) elif obj_type == "qr": - draw_qr(image, obj) + draw_qr(image, obj, scale) elif obj_type == "barcode": - draw_barcode(image, obj) + draw_barcode(image, obj, scale) + + if design.get("cutMode") in {"cutmark", "cutmark-chain"}: + draw_cutmark(draw, image.width, image.height, scale) output = LABEL_DIR / f"label-{uuid.uuid4().hex}.png" image.save(output) return output -def run_print_command(image_path: Path, copies: int) -> subprocess.CompletedProcess[str]: - command = PRINT_COMMAND.format(image=shlex.quote(str(image_path)), copies=copies) - return subprocess.run(shlex.split(command), capture_output=True, check=False, text=True, timeout=90) +def ptouch_supports_halfcut() -> bool: + global PTOUCH_SUPPORTS_HALFCUT + if PTOUCH_SUPPORTS_HALFCUT is not None: + return PTOUCH_SUPPORTS_HALFCUT + result = subprocess.run( + ["ptouch-print", "--help"], + capture_output=True, + check=False, + text=True, + timeout=10, + ) + help_text = f"{result.stdout}\n{result.stderr}" + PTOUCH_SUPPORTS_HALFCUT = "--no-halfcut" in help_text and "--halfcut" in help_text + return PTOUCH_SUPPORTS_HALFCUT -def print_label(image_path: Path, copies: int) -> list[subprocess.CompletedProcess[str]]: +def cut_args_for_design(design: dict[str, Any]) -> str: + args = [] + if design.get("cutMode") in {"chain", "cutmark-chain"}: + args.append("--chain") + halfcut = design.get("halfcut", "on") + if halfcut == "off": + if not ptouch_supports_halfcut(): + raise RuntimeError( + "Halbschnitt Aus braucht die gepatchte ptouch-print-Version. " + "Bitte start.sh und patches/ptouch-print-cut-options.patch nach Unraid kopieren, " + "ptouch-print im Container entfernen oder Container neu erstellen und danach neu starten." + ) + args.append("--no-halfcut") + elif halfcut == "on" and ptouch_supports_halfcut(): + args.append("--halfcut") + return " ".join(args) + + +def run_print_command(image_path: Path, copies: int, design: dict[str, Any]) -> subprocess.CompletedProcess[str]: + cut_args = cut_args_for_design(design) + command = PRINT_COMMAND.format(image=shlex.quote(str(image_path)), copies=copies, cut_args=cut_args) + if cut_args and "{cut_args}" not in PRINT_COMMAND: + command = f"{command} {cut_args}" + process = subprocess.Popen( + shlex.split(command), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + start_new_session=True, + ) + try: + stdout, stderr = process.communicate(timeout=90) + except subprocess.TimeoutExpired: + os.killpg(process.pid, signal.SIGKILL) + stdout, stderr = process.communicate() + return subprocess.CompletedProcess( + shlex.split(command), + 124, + stdout, + f"{stderr}\nDruckprozess wurde nach Timeout beendet.".strip(), + ) + return subprocess.CompletedProcess(shlex.split(command), process.returncode, stdout, stderr) + + +def print_label(image_path: Path, copies: int, design: dict[str, Any]) -> list[subprocess.CompletedProcess[str]]: if "{copies}" in PRINT_COMMAND: - return [run_print_command(image_path, copies)] - return [run_print_command(image_path, 1) for _ in range(copies)] + return [run_print_command(image_path, copies, design)] + return [run_print_command(image_path, 1, design) for _ in range(copies)] def detect_tape_width() -> tuple[float | None, str]: @@ -278,6 +378,20 @@ def format_print_error(result: subprocess.CompletedProcess[str]) -> str: "In Unraid pruefen: Privileged an, /dev/bus/usb Read/Write gemountet, usblp nicht geladen. " f"Details: {details}" ) + if "timeout while waiting for status response" in details: + return ( + "Drucker wurde gefunden, antwortet aber nicht auf die Statusabfrage. " + "PT-P700 kurz ausschalten oder USB abziehen, usblp auf dem Host erneut pruefen/entladen " + "und danach den Container neu starten. " + f"Details: {details}" + ) + if result.returncode == 124 or "Druckprozess wurde nach Timeout beendet" in details: + return ( + "Druckprozess hing und wurde beendet. " + "Bitte auf dem Host pruefen, ob kein ptouch-print mehr das USB-Geraet blockiert, " + "danach Drucker kurz neu verbinden und erneut versuchen. " + f"Details: {details}" + ) return f"Druck fehlgeschlagen: {details}" @@ -326,10 +440,15 @@ def api_print(): design, copies = design_from_request() image_path = render_design(design) if PRINT_ENABLED: - results = print_label(image_path, copies) - failed = next((result for result in results if result.returncode != 0), None) - if failed: - raise RuntimeError(format_print_error(failed)) + if not PRINT_LOCK.acquire(blocking=False): + raise RuntimeError("Es laeuft bereits ein Druckjob. Bitte warten oder haengenden ptouch-print-Prozess beenden.") + try: + results = print_label(image_path, copies, design) + failed = next((result for result in results if result.returncode != 0), None) + if failed: + raise RuntimeError(format_print_error(failed)) + finally: + PRINT_LOCK.release() message = "Etikett wurde an den Drucker gesendet." else: message = f"Testmodus: Etikett erstellt, nicht gedruckt: {image_path.name}" diff --git a/app/static/editor.js b/app/static/editor.js index 130c242..d053cb1 100644 --- a/app/static/editor.js +++ b/app/static/editor.js @@ -1,4 +1,12 @@ const MM_TO_SCREEN = 6; +const PRINTABLE_TAPE_PX = { + 3.5: 24, + 6: 32, + 9: 52, + 12: 76, + 18: 120, + 24: 128, +}; const STORAGE_KEY = "ptouch-web-layout"; const state = { @@ -10,6 +18,8 @@ const state = { lengthMm: 70, marginMm: 2, orientation: "landscape", + cutMode: "auto", + halfcut: "on", objects: [], }, }; @@ -22,6 +32,8 @@ const controls = { tapeWidth: document.getElementById("tapeWidth"), lengthMm: document.getElementById("lengthMm"), marginMm: document.getElementById("marginMm"), + cutMode: document.getElementById("cutMode"), + halfcut: document.getElementById("halfcut"), copies: document.getElementById("copies"), zoomLabel: document.getElementById("zoomLabel"), previewImage: document.getElementById("previewImage"), @@ -64,6 +76,11 @@ function pxToMm(v) { return v / scale(); } +function printablePxPerMm() { + const tape = Number(state.design.tapeWidthMm); + return (PRINTABLE_TAPE_PX[tape] || (tape * 180) / 25.4) / tape; +} + function selected() { return state.design.objects.find((obj) => obj.id === state.selectedId) || null; } @@ -77,6 +94,8 @@ function syncDesignControls() { state.design.tapeWidthMm = Number(controls.tapeWidth.value); state.design.lengthMm = Number(controls.lengthMm.value); state.design.marginMm = Number(controls.marginMm.value); + state.design.cutMode = controls.cutMode.value; + state.design.halfcut = controls.halfcut.value; state.design.orientation = document.querySelector("input[name='orientation']:checked").value; } @@ -84,6 +103,8 @@ function applyDesignControls() { controls.tapeWidth.value = state.design.tapeWidthMm; controls.lengthMm.value = state.design.lengthMm; controls.marginMm.value = state.design.marginMm; + controls.cutMode.value = state.design.cutMode || "auto"; + controls.halfcut.value = state.design.halfcut || "on"; document.querySelector(`input[name='orientation'][value='${state.design.orientation}']`).checked = true; } @@ -114,21 +135,25 @@ function drawRulers() { function drawTextObject(obj) { ctx.save(); - ctx.fillStyle = "#000"; - ctx.font = `${obj.bold ? "700 " : ""}${mmToPx(obj.fontSizeMm || 5)}px system-ui, sans-serif`; - ctx.textBaseline = "middle"; - ctx.textAlign = obj.align || "left"; const x = mmToPx(obj.x); const y = mmToPx(obj.y); const w = mmToPx(obj.w); const h = mmToPx(obj.h); + ctx.beginPath(); + ctx.rect(x, y, w, h); + ctx.clip(); + ctx.fillStyle = "#000"; + const fontPx = (obj.fontSizeMm || 5) * printablePxPerMm() * state.zoom; + ctx.font = `${obj.bold ? "700 " : ""}${fontPx}px DejaVu Sans, Arial, system-ui, sans-serif`; + ctx.textBaseline = "middle"; + ctx.textAlign = obj.align || "left"; const lines = String(obj.text || "").split("\n"); - const lineHeight = mmToPx((obj.fontSizeMm || 5) * 1.15); + const lineHeight = fontPx * 1.15; const total = lineHeight * lines.length; let cy = y + h / 2 - total / 2 + lineHeight / 2; const tx = obj.align === "center" ? x + w / 2 : obj.align === "right" ? x + w : x; for (const line of lines) { - ctx.fillText(line, tx, cy, w); + ctx.fillText(line, tx, cy); cy += lineHeight; } ctx.restore(); @@ -212,6 +237,18 @@ function draw() { ctx.setLineDash([]); for (const obj of state.design.objects) drawObject(obj); + + if (state.design.cutMode === "cutmark" || state.design.cutMode === "cutmark-chain") { + ctx.strokeStyle = "#111"; + ctx.setLineDash([4, 4]); + const x = canvas.width - mmToPx(1.5); + ctx.beginPath(); + ctx.moveTo(x, 0); + ctx.lineTo(x, canvas.height); + ctx.stroke(); + ctx.setLineDash([]); + } + drawRulers(); controls.zoomLabel.textContent = `${Math.round(state.zoom * 100)}%`; updateProps(); @@ -376,7 +413,7 @@ window.addEventListener("keydown", (event) => { } }); -["tapeWidth", "lengthMm", "marginMm"].forEach((id) => document.getElementById(id).addEventListener("input", draw)); +["tapeWidth", "lengthMm", "marginMm", "cutMode", "halfcut"].forEach((id) => document.getElementById(id).addEventListener("input", draw)); document.querySelectorAll("input[name='orientation']").forEach((input) => input.addEventListener("change", draw)); Object.values(controls).forEach((control) => { if (control && control.id && control.id.startsWith("prop")) control.addEventListener("input", applyProps); diff --git a/app/static/mobile.js b/app/static/mobile.js index fbbd34b..8ace9d5 100644 --- a/app/static/mobile.js +++ b/app/static/mobile.js @@ -6,6 +6,8 @@ const fields = { font: document.getElementById("mobileFont"), copies: document.getElementById("mobileCopies"), align: document.getElementById("mobileAlign"), + cutMode: document.getElementById("mobileCutMode"), + halfcut: document.getElementById("mobileHalfcut"), bold: document.getElementById("mobileBold"), frame: document.getElementById("mobileFrame"), preview: document.getElementById("mobilePreview"), @@ -53,6 +55,8 @@ function mobileDesign() { lengthMm: length, marginMm: margin, orientation: "landscape", + cutMode: fields.cutMode.value, + halfcut: fields.halfcut.value, objects, }; } diff --git a/app/static/style.css b/app/static/style.css index a72fc42..24fcdbe 100644 --- a/app/static/style.css +++ b/app/static/style.css @@ -269,14 +269,16 @@ textarea { .preview-box { min-height: 120px; display: grid; - place-items: center; + align-items: center; + justify-items: start; border: 1px dashed #555; background: #222; padding: 12px; + overflow: auto; } .preview-box img { - max-width: 100%; + max-width: none; image-rendering: crisp-edges; } diff --git a/app/templates/index.html b/app/templates/index.html index bb4cca5..8e267d1 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -68,6 +68,22 @@ Kopien + +
diff --git a/app/templates/mobile.html b/app/templates/mobile.html index a90a4ed..ebb2f49 100644 --- a/app/templates/mobile.html +++ b/app/templates/mobile.html @@ -67,6 +67,23 @@ + + +
diff --git a/patches/ptouch-print-cut-options.patch b/patches/ptouch-print-cut-options.patch new file mode 100644 index 0000000..c16c077 --- /dev/null +++ b/patches/ptouch-print-cut-options.patch @@ -0,0 +1,52 @@ +--- a/src/ptouch-print.c ++++ b/src/ptouch-print.c +@@ -58,6 +58,7 @@ + int fontsize = 0; + bool debug = false; + bool chain = false; ++int precut = 1; + int forced_tape_width = 0; + + /* -------------------------------------------------------------------- +@@ -123,8 +124,8 @@ + } + } + if ((ptdev->devinfo->flags & FLAG_HAS_PRECUT) == FLAG_HAS_PRECUT) { +- ptouch_send_precut_cmd(ptdev, 1); +- if (debug) { ++ if (precut) ptouch_send_precut_cmd(ptdev, 1); ++ if (debug && precut) { + printf(_("send precut command\n")); + } + } +@@ -439,6 +440,8 @@ + printf("\t--cutmark\t\tPrint a mark where the tape should be cut\n"); + printf("\t--pad \t\tAdd n pixels padding (blank tape)\n"); + printf("\t--chain\t\t\tSkip final feed of label and any automatic cut\n"); ++ printf("\t--halfcut\t\tEnable half cut / precut when supported\n"); ++ printf("\t--no-halfcut\t\tDisable half cut / precut when supported\n"); + printf("other commands:\n"); + printf("\t--version\t\tshow version info (required for bug report)\n"); + printf("\t--info\t\t\tshow info about detected tape\n"); +@@ -484,6 +487,10 @@ + continue; /* not done here */ + } else if (strcmp(&argv[i][1], "-chain") == 0) { + chain=true; ++ } else if (strcmp(&argv[i][1], "-halfcut") == 0) { ++ precut=1; ++ } else if (strcmp(&argv[i][1], "-no-halfcut") == 0) { ++ precut=0; + } else if (strcmp(&argv[i][1], "-debug") == 0) { + debug=true; + } else if (strcmp(&argv[i][1], "-info") == 0) { +@@ -634,6 +641,10 @@ + im = NULL; + } else if (strcmp(&argv[i][1], "-chain") == 0) { + chain = true; ++ } else if (strcmp(&argv[i][1], "-halfcut") == 0) { ++ precut = 1; ++ } else if (strcmp(&argv[i][1], "-no-halfcut") == 0) { ++ precut = 0; + } else if (strcmp(&argv[i][1], "-debug") == 0) { + debug = true; + } else if (strcmp(&argv[i][1], "-copies") == 0) { diff --git a/start.sh b/start.sh index e8aa905..19a47e1 100644 --- a/start.sh +++ b/start.sh @@ -56,9 +56,12 @@ if ! python3 -c "import qrcode, barcode" >/dev/null 2>&1; then python3 -m pip install --root-user-action=ignore --no-cache-dir qrcode python-barcode fi -if ! command -v ptouch-print >/dev/null 2>&1; then +if ! command -v ptouch-print >/dev/null 2>&1 || ! ptouch-print --help 2>&1 | grep -q -- "--no-halfcut"; then rm -rf "$PTOUCH_SRC_DIR" git clone --depth 1 --branch farix-main https://github.com/farixembedded/ptouch-print.git "$PTOUCH_SRC_DIR" + if [ -f "$APP_DIR/patches/ptouch-print-cut-options.patch" ]; then + patch -d "$PTOUCH_SRC_DIR" -p1 < "$APP_DIR/patches/ptouch-print-cut-options.patch" + fi cmake -S "$PTOUCH_SRC_DIR" -B "$PTOUCH_SRC_DIR/build" -DCMAKE_BUILD_TYPE=Release cmake --build "$PTOUCH_SRC_DIR/build" install -m 0755 "$PTOUCH_SRC_DIR/build/ptouch-print" /usr/local/bin/ptouch-print