Add A4 carrier print mode (paper=a4) and document it in README + Swagger
- packing_list renders A5 content into top region of an A4 portrait page when paper=a4, so A5 paper loaded horizontally in an A4 tray prints upright - run.generate/print_document/print_api thread the optional paper param through - README: new Print Service section with A4 carrier usage + curl example - Swagger: POST /api/print description documents the A4 carrier mode
This commit is contained in:
@@ -46,6 +46,9 @@ app = FastAPI(
|
||||
"when `printer` is omitted.\n"
|
||||
"* Paper size and orientation are chosen automatically per report type: "
|
||||
"packing_list → A5 landscape, sign_receipt → A4 portrait.\n"
|
||||
"* Packing list supports an A4 carrier mode via `paper=\"a4\"`: the A5 content is "
|
||||
"rendered on an A4 portrait page (top region) and printed as A4, so it comes out "
|
||||
"correctly on A5 paper loaded horizontally in an A4 tray.\n"
|
||||
"* A JSON line is appended to `logs/print.log` for every request."
|
||||
),
|
||||
version="1.0.0",
|
||||
@@ -83,6 +86,17 @@ class PrintRequest(BaseModel):
|
||||
description="Target printer name. Falls back to `printers.default` in settings.yaml when omitted.",
|
||||
examples=["Canon G1030 series (网络 USB1)"],
|
||||
)
|
||||
paper: Optional[Literal["a4"]] = Field(
|
||||
None,
|
||||
description=(
|
||||
"Paper carrier mode. Omit (null) for the default A5 landscape packing list. "
|
||||
"Set to \"a4\" to render the packing list on an A4 portrait page with the A5 "
|
||||
"content placed in the top region, so it prints correctly on A5 paper loaded "
|
||||
"horizontally in an A4 tray (saves re-adjusting the paper clamps). Only "
|
||||
"applies to `packing_list`; ignored for other report types."
|
||||
),
|
||||
examples=["a4"],
|
||||
)
|
||||
dry_run: bool = Field(
|
||||
False,
|
||||
description="When true, generate the PDF and build the print command but do NOT send it to the printer.",
|
||||
@@ -140,8 +154,9 @@ def _log(result: dict) -> None:
|
||||
f.write(json.dumps(entry, ensure_ascii=False, default=str) + "\n")
|
||||
|
||||
|
||||
@api_router.post(
|
||||
"/print",
|
||||
@app.post(
|
||||
"/api/print",
|
||||
tags=["print"],
|
||||
summary="Generate and print a shipping document",
|
||||
description=(
|
||||
"Generate the requested document as a PDF using the existing report "
|
||||
@@ -154,7 +169,20 @@ def _log(result: dict) -> None:
|
||||
"(skipped when `dry_run=true`).\n"
|
||||
"4. Append a JSON line to `logs/print.log`.\n"
|
||||
"5. Return the job result.\n\n"
|
||||
"Set `dry_run=true` to generate the PDF without consuming paper."
|
||||
"Set `dry_run=true` to generate the PDF without consuming paper.\n\n"
|
||||
"**A4 Carrier Mode (packing_list only)**\n"
|
||||
"To avoid re-adjusting the printer's paper clamps, A5 paper can be loaded "
|
||||
"**horizontally** in an A4 tray. But horizontal loading would otherwise print "
|
||||
"the A5 content upside-down. This mode fixes it: pass `paper=\"a4\"` and the "
|
||||
"A5 content is rendered into the **top 148mm region of an A4 portrait page**, "
|
||||
"then printed as A4 — the horizontally loaded A5 sheet receives the top region "
|
||||
"and comes out upright and complete. Verified on a real printer.\n\n"
|
||||
"Both conditions are required:\n"
|
||||
"1. Request carries `paper=\"a4\"` (only affects `packing_list`; ignored for others).\n"
|
||||
"2. Physically, the A5 paper is loaded **horizontally** in the A4 tray.\n\n"
|
||||
"For a standard A5 tray (vertical loading), omit `paper` to fall back to normal "
|
||||
"A5 landscape direct print. The A4-carrier PDF is named with an `_a4` suffix; "
|
||||
"always read the returned `pdf` field rather than hard-coding the path."
|
||||
),
|
||||
response_description="Print job result, including the generated PDF path.",
|
||||
response_model=PrintResponse,
|
||||
@@ -184,6 +212,7 @@ def print_endpoint(req: PrintRequest):
|
||||
params=req.params,
|
||||
printer=req.printer,
|
||||
dry_run=req.dry_run,
|
||||
paper=req.paper,
|
||||
)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
@@ -26,6 +26,7 @@ def print_document(
|
||||
params: dict,
|
||||
printer: str | None = None,
|
||||
dry_run: bool = False,
|
||||
paper: str | None = None,
|
||||
) -> dict:
|
||||
"""生成报表 PDF 并打印。
|
||||
|
||||
@@ -33,6 +34,8 @@ def print_document(
|
||||
:param params: 报表参数(如 paichan_no/box_no 或 receipt_no)
|
||||
:param printer: 指定打印机名;为 None 时用 settings.printers.default
|
||||
:param dry_run: True 时只构造打印命令不真正执行(用于验证链路)
|
||||
:param paper: 可选纸张承载模式。packing_list 支持 "a4" —— 渲染到 A4 纵向页面顶部,
|
||||
配合横向装载的 A5 纸正常出纸;其余报表/取值忽略。
|
||||
:return: 结构化结果(含 pdf 路径、打印命令、ok 状态)
|
||||
"""
|
||||
if report_type not in VALID_REPORTS:
|
||||
@@ -40,13 +43,20 @@ def print_document(
|
||||
|
||||
# 1. 生成 PDF(run.generate 内部已做 required 参数校验与 DB 查询)
|
||||
output = run._default_output(report_type, params)
|
||||
out_path = run.generate(report_type, params, output)
|
||||
# A4 承载模式的 PDF 用独立文件名,避免覆盖 A5 直打结果
|
||||
if report_type == "packing_list" and paper == "a4":
|
||||
output = output[:-4] + "_a4.pdf" if output.lower().endswith(".pdf") else output + "_a4"
|
||||
out_path = run.generate(report_type, params, output, paper=paper)
|
||||
|
||||
# 2. 选打印机与纸张(装箱单 A5 横向 / 签收单 A4 纵向)
|
||||
target_printer = printer or settings.printers.default
|
||||
paper_settings = settings.print.settings_by_report.get(
|
||||
report_type, settings.print.default_settings
|
||||
)
|
||||
if report_type == "packing_list" and paper == "a4":
|
||||
# A4 承载:物理发送 A4 纵向指令,横向装载的 A5 纸自动承接页面顶部 148mm 内容区
|
||||
paper_settings = "paper=A4,orientation=portrait,color=bw"
|
||||
else:
|
||||
paper_settings = settings.print.settings_by_report.get(
|
||||
report_type, settings.print.default_settings
|
||||
)
|
||||
|
||||
# 3. 打印
|
||||
result = printing.print_pdf(
|
||||
|
||||
Reference in New Issue
Block a user