Instructions to use smjai/printless-1.7b with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use smjai/printless-1.7b with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-1.7B") model = PeftModel.from_pretrained(base_model, "smjai/printless-1.7b") - Notebooks
- Google Colab
- Kaggle
Printless 1.7B (v2.1): PDF → .printless v2.0
Turns the text of a PDF into a .printless v2.0 document: the structured, signed, paperless print format used by Printless, made by SMJ AI. It was built for "Start from a PDF" in the Printless business portal, where a business uploads an existing receipt, invoice, ticket or form and gets an editable template. That feature is not live yet, and Printless does not serve this model today.
- Version: v2.1 (2026-11-03, tag
v2.1). v2.0 (2026-11-02) is the previous commit. - Type: LoRA adapter (r 32, α 64, all projections) on Qwen/Qwen3-1.7B
- Languages: English, Arabic, Japanese (plus Indonesian receipts from CORD)
- Output format: .printless v2.0, documented at https://docs.printless.app. JSON Schema: https://docs.printless.app/schemas/printless-v2.schema.json
What it does
Input: the text and layout of a PDF, written as plain "layout text" (format under Use) in a chat prompt. Output: one JSON document in the .printless v2.0 format: the print type (receipt, invoice, ticket, card, contract and others), labelled fields, line items, totals, and content blocks (headers, paragraphs, tables, lists, callouts, quotes, signature lines, pictures).
Validate every answer against the public .printless JSON Schema before you use it, and treat it as a draft for a person to check. The model can be wrong, and on some documents it writes facts the page does not show; see Limitations and "Before you use an answer".
Never let this model fill money, tax or time fields on its own. Every amount, currency, tax number, date and time it writes needs a person to check it against the page before it is used.
Results
The model is scored on its own: no rules fallback. Our scoring decoder is greedy, but at each step it takes the most likely token that keeps the output valid JSON; after generation it cuts a repeating tail and closes an answer that stops early (it only ever adds closing brackets), which applied to 2 of the 188 test answers below. Plain greedy decoding, as in the Use section, gives the same answer as long as the model's top token stays valid JSON at every step; on three test PDFs rerun with the Use code, the answers were identical.
Held-out test, 150 PDFs the model never saw: 100 synthetic documents in two renderers (English 50, Arabic 25,
Japanese 25), 25 CORD receipts and 25 DocLayNet pages. v2.1 labels: a date the page prints without a time is a
plain date (YYYY-MM-DD), since a converter can only read what is printed.
| Converter | Schema-valid | Exact | Block F1 | Field accuracy | Text F1 |
|---|---|---|---|---|---|
| v2.0 (with the v2.1 front end and decoder) | 0.987 | 0.327 | 0.757 | 0.873 | 0.894 |
| v2.1 (this commit) | 0.987 | 0.527 | 0.810 | 0.893 | 0.921 |
Under the old v2.0 labels (every date as a UTC instant) v2.1 scores block F1 0.773 against v2.0's 0.792: it now writes plain dates where the page shows no time, which is the intended change.
Hand-checked real-world-style test, 38 PDFs (26 generic documents; 12 invoices, receipts, work orders and bookings; English 24, Japanese 8, Arabic 6), made by two PDF producers the training data never uses:
| Converter | Schema-valid | Block F1 | Generic documents block F1 | Field accuracy | Text F1 |
|---|---|---|---|---|---|
| v2.0 (with the v2.1 front end and decoder) | 0.921 | 0.518 | 0.505 | 0.568 | 0.792 |
| v2.1 | 1.000 | 0.722 | 0.777 | 0.772 | 0.879 |
By kind on the 150 (block F1, v2.1): invoices 0.92, ZATCA invoices 0.96, receipts 0.84, contracts 0.87, cards 0.90, boarding passes 0.93, parking 0.93, work orders 0.99; coupons, keys, invitations and event tickets 1.00; generic documents 0.53 (DocLayNet pages 0.46). By language: Arabic 0.80, English 0.78, Japanese 0.96.
Limitations
- Generic documents are still weak on DocLayNet pages (block F1 0.46): financial reports and manuals where paragraph boundaries and heading levels are ambiguous. On our own letters, memos, reports, forms and two-column pages it reaches 0.78.
- Receipts in unfamiliar layouts (real set, 0.38) and Arabic PDFs from some producers (0.51): two Arabic test PDFs have glyph positions the text extractor cannot order.
- Text-layer PDFs only. Scanned PDFs, and PDFs whose fonts have no Unicode map, give no usable layout text and should be refused before they reach the model. There is no OCR stage.
- A page that prints a time without stating the zone cannot be turned into an exact instant.
- Inputs longer than 3,072 tokens are not covered by training.
- It does not verify amounts, tax numbers or signatures.
Known failure modes
Independent testing on new documents (2026-11-07) found these. They are not rare edge cases; plan for them.
- Times may get an invented time zone. A time printed without a zone (
09:42) can come back as a UTC instant with a guessed offset (10:42Z), and two times on one page can get two different offsets. The training labels store times as UTC instants, which teaches this. Keep times as printed. - Currency, dates and other fields may be invented. On one Arabic invoice that printed only
ر.س(Saudi riyal) the model wroteAED. It has also added a due date and a status the page does not show, and aTotalofUSD 0to a boarding pass that prints no fare. - Line amounts may contradict the printed totals. On a page with separate quantity and price columns, the model can multiply them and write line amounts that do not add up to the printed subtotal or total.
- Item names can change. It has altered an Arabic item name by a letter.
- Tax identifiers are often dropped. VAT and TRN numbers, the Japanese invoice registration number (T-number), bill and invoice numbers and the ZATCA QR code are missing from most receipt answers. The model never reads a QR code's content: the layout text only says a QR code is there.
- Text inside the document can steer the output. A sentence on the page addressed to "the AI" made the model
write a second, forged amount row (
USD 0.00) next to the real total. Treat document text as untrusted input. - Dates and recipient blocks on letters are often left out.
- Receipt dates and times are not written by design. Printless stamps a receipt with the time it is issued, so the training labels leave the printed date out. If you need it, read it from the page yourself.
- It does not say when the input is not a document; junk text comes back as paragraphs.
Before you use an answer
Check each answer in code, then show it to a person. At least:
- It parses and validates against the .printless v2.0 schema.
- Every currency code in the answer is printed on the page, as the code or its symbol.
- The line amounts add up to the printed subtotal or total.
- There is exactly one
Totalrow, and its figure is printed on the page. - Times are kept as printed: no zone or offset that the page does not show.
- Every date, status and due date in the answer is printed on the page.
- Tax numbers and document numbers on the page are present in the answer, or added by a person.
Reject or flag the answer when a check fails. Do not apply totals, tax or times from it automatically.
Training
- Data: 2,600 training documents. 29 were dropped (23 longer than 3,072 tokens, 6 without a text layer),
leaving 2,571 training pairs. The documents before that filter:
- Synthetic Printless prints rendered by two independent renderers (800).
- Synthetic generic documents written for this project: letters, memos, reports, forms, two-column newsletters, notices, minutes and FAQs in English, Arabic and Japanese (900).
- CORD v2 receipts (CC BY 4.0) (300).
- DocLayNet pages (CDLA-Permissive-1.0), including two-column pages in reading order (600).
- No share-alike data.
- No customer data: no PDF uploaded to Printless and no customer print is in the training, validation or test data.
- Run: the v2.0 recipe unchanged: LoRA r 32 / α 64 on all projections, 4 epochs, 644 steps, effective batch 16,
learning rate 1e-4, bf16, 3,072-token sequences. 57.5 minutes on one RTX 5070 (12 GB), peak 9.3 GB VRAM.
Validation loss 0.022 at the end (token accuracy 0.995). Details are in
training_run.json.
Use
import json
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
base = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-1.7B", dtype="bfloat16", device_map="auto")
model = PeftModel.from_pretrained(base, "smjai/printless-1.7b")
tok = AutoTokenizer.from_pretrained("smjai/printless-1.7b")
SYSTEM = "Convert the PDF layout to a .printless v2.0 document. Reply with JSON only."
layout = open("receipt.layout.txt", encoding="utf-8").read() # layout text, format below
messages = [{"role": "system", "content": SYSTEM}, {"role": "user", "content": layout}]
prompt = tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True, enable_thinking=False)
ids = tok(prompt, return_tensors="pt", add_special_tokens=False).input_ids.to(model.device)
out = model.generate(ids, max_new_tokens=4096, do_sample=False)
doc = json.loads(tok.decode(out[0, ids.shape[1]:], skip_special_tokens=True))
Prompt
Two messages, through the chat template in this repo:
- system: exactly
Convert the PDF layout to a .printless v2.0 document. Reply with JSON only. - user: the layout text of the whole PDF, nothing else.
Call apply_chat_template with enable_thinking=False. The model was trained without thinking, so the reply is
compact JSON and nothing else.
Decoding
- Greedy (
do_sample=False). No temperature, top-p or repetition penalty. - Stop at
<|im_end|>(the base model's generation config already does). We scored withmax_new_tokens=4096. - The reply is one JSON object. Parse it, then validate it against https://docs.printless.app/schemas/printless-v2.schema.json (JSON Schema 2020-12) before you use it.
Layout text
The user message describes what is on the page, never which .printless block it is. You can produce it with any PDF text library that gives you characters with positions, font names and sizes (we use pdfplumber).
@page Nstarts page N. Lines follow top to bottom.- Each line is one row of text. Text in the same row that is split by a wide gap becomes cells joined by
|, in reading order (an Arabic row is written right to left, so its first cell is the rightmost one). - Right-to-left text is written in logical (reading) order, not visual order.
- A line may start with tags in square brackets, separated by spaces, in this order:
h1,h2,h3: the line's median font size is at least 1.6, 1.3 or 1.12 times the page's body size (the median size of all characters on the page).b,i,m: more than 60% of the line's characters are in a bold, italic or monospace font.rtl: most of the line's text is right to left.box: the line sits on a filled rectangle (a coloured band or shaded row).- Position, only for a line with one cell (distances are from the leftmost and rightmost text on the page):
ccentred, with more than 8% of the page width free on each side;ra left-to-right line that starts more than 35% of the page width in;lthe mirror ofrfor a right-to-left line; otherwise>1or>2for an indent of one, or two or more, steps of 1.8 times the body size.
---is a horizontal rule (two in a row are written once).[img p1i1 96x96]is an embedded picture at that position: the id isp<page>i<n>, then its size in pixels. Animageblock in the answer names the picture by this id ("asset": "p1i1"); to make a complete document, add the picture to the document'sassetsmap yourself. The model never sees pixels.[gfx qr],[gfx barcode],[gfx vector]: a vector QR code, barcode or drawing at that position.- A page with two text columns is read column by column: the whole left column, then the right one, between any full-width lines.
- Ligatures (fi, fl) are written as their letters. Glyphs with no Unicode mapping are dropped.
- Training covered prompts up to 3,072 tokens.
Worked example
A small synthetic receipt (one A6 page) gives this layout text:
@page 1
[h1 b] Juniper Cafe
12 Harbour Street, Example City
[h2 b] Receipt
Customer: Ari Nakamura
Date: 2026-11-04
Flat white x2 | USD 9.00
Banana bread | USD 4.50
---
[h3 b] Total | USD 13.50
Payment | Card
Receipt no. | R-1042
and the model answers (spaced out here; the real reply is one compact line):
{"printless": "2.0", "type": "receipt", "blocks": [
{"type": "line_items", "key": "items", "items": [{"label": "Flat white x2", "amount": 9}, {"label": "Banana bread", "amount": 4.5}]},
{"type": "divider", "style": "dashed"},
{"type": "summary", "key": "summary", "rows": [{"label": "Total", "value": "USD 13.50", "bold": true, "large": true}]},
{"type": "label_value", "key": "paymentMethod", "value": "Card"},
{"type": "label_value", "key": "receiptNumber", "value": "R-1042"}
], "lang": "en"}
Check every answer: here the model left out the customer line and the date. Like the training labels, it also leaves out the shop name and address at the top of the page.
Attribution
- Base model: Qwen3-1.7B by the Qwen team (Apache-2.0).
- Receipts: CORD v2, Park et al., NAVER Clova (CC BY 4.0).
- Documents: DocLayNet, Pfitzmann et al., IBM DS4SD (CDLA-Permissive-1.0).
Contact
SMJ AI LLC · https://printless.app · privacy@printless.app
- Downloads last month
- 31
Model tree for smjai/printless-1.7b
Datasets used to train smjai/printless-1.7b
docling-project/DocLayNet
Evaluation results
- block F1 on Printless v2 held-out test (150 PDFs - synthetic, CORD, DocLayNet), v2.1 labelsself-reported0.810
- field accuracy on Printless v2 held-out test (150 PDFs - synthetic, CORD, DocLayNet), v2.1 labelsself-reported0.893
- text F1 on Printless v2 held-out test (150 PDFs - synthetic, CORD, DocLayNet), v2.1 labelsself-reported0.921
- schema-valid output on Printless v2 held-out test (150 PDFs - synthetic, CORD, DocLayNet), v2.1 labelsself-reported0.987
- exact match on Printless v2 held-out test (150 PDFs - synthetic, CORD, DocLayNet), v2.1 labelsself-reported0.527
- block F1 on Printless hand-checked real-world-style test (38 PDFs)self-reported0.722
- field accuracy on Printless hand-checked real-world-style test (38 PDFs)self-reported0.772
- text F1 on Printless hand-checked real-world-style test (38 PDFs)self-reported0.879