Grouped sparse rows split into separate Markdown tables and place a prose note under Unit Cost
Problem
LiteParse 2.14.4 fixes the exact single-sparse-row reproduction in #400, but a related grouped-row shape still loses structure in Markdown mode.
The fictional one-page fixture generated below has a sparse group row, an indented free-text note, and three detail rows. Its visible layout has distinct item rows and monetary columns. output_format="markdown" instead produces two disconnected tables and malformed associations: the free-text note lands under Unit Cost, adjacent financial values are combined, and later detail rows lose the original header/column identity.
This is distinct from #400's old failure: the current #400 fixture now produces one coherent eight-column table with an explicit sparse row. The defect here is post-#444 fragmentation and cell association loss on a grouped sparse shape.
Environment
liteparse==2.14.4- CPython 3.13 on macOS arm64
Reproduction
This readable ReportLab script generates the fictional grouped-row fixture locally, including its longer interleaved note. Install reportlab and liteparse==2.14.4, then run:
from io import BytesIO
from reportlab.pdfgen import canvas
from liteparse import LiteParse
pdf = BytesIO()
c = canvas.Canvas(pdf, pagesize=(612, 792), invariant=1)
c.setFont("Helvetica", 7)
c.drawString(108, 702, "Account")
c.drawString(520, 702, "Adjust")
headers = [
(48, "Order Date"), (108, "Suffix"), (150, "Line Section"),
(215, "Item Code"), (300, "Description"), (362, "Status"),
(424, "Unit Cost"), (470, "Freight Charge"), (520, "Tax"),
(556, "Total Billed"),
]
for x, text in headers:
c.drawString(x, 692, text)
# Sparse group row followed by an interleaved prose note.
c.drawString(48, 676, "03/14/2024")
c.drawString(215, 676, "IC-1048")
c.drawString(362, 676, "Shipped")
c.drawString(300, 664, "Shipment was delayed in transit and re-routed through the regional distribution hub.")
# Three distinct printed records; all values are fictional.
rows = [
(652, "WIDGET ASSEMBLY", "$482,110.40", "$0.00", "$9,215.75", "$491,326.15"),
(640, "BRACKET SET", "$0.00", "$0.00", "$0.00", "$0.00"),
(628, "CONTROL MODULE", "$31,905.22", "$4,100.00", "$0.00", "$36,005.22"),
]
for y, description, *amounts in rows:
c.drawString(215, y, "IC-1048")
c.drawString(300, y, description)
c.drawString(362, y, "Shipped")
for right_edge, amount in zip((464, 514, 550, 590), amounts):
c.drawRightString(right_edge, y, amount)
c.showPage()
c.save()
result = LiteParse(ocr_enabled=False, quiet=True, output_format="markdown").parse(pdf.getvalue())
print(result.pages[0].markdown)Actual excerpt
| Order Date | Suffix | Line Section | Item Code | Description | Status | Unit Cost | Freight Charge Tax | Total Billed |
| 03/14/2024 | | | IC-1048 | | Shipped | Shipment was delayed ... | | |
| | | | IC-1048 | WIDGET ASSEMBLYShipped | | $482,110.40 | | $0.00 $9,215.75 $491,326.15 |
| IC-1048 | BRACKET SET | Shipped | $0.00 | $0.00 | $0.00 | $0.00 |
| IC-1048 | CONTROL MODULEShipped | | $31,905.22 | $4,100.00 | $0.00 $36,005.22 |Expected
Emit one table with stable headers and one row per printed record. The sparse group row and prose note may be represented separately or as rows with empty cells, but they must not move prose into a money column or detach later financial values from their original columns.
Prior art and scope
PR #444 (c3377de6cbd54fa14d81c327e008dc44815a064c) closed #400 and explicitly notes that sparse-table handling is finnicky rather than complete. The fixed #400 fixture confirms that this is not a request to reopen its exact regression.
Some description/status glyphs touch in this intentionally cramped source, so the concatenated description/status text is not itself claimed as a parser defect. The report concerns the separate item rows, the displaced prose note and the financial-column associations.
This report is limited to Markdown serialization. Default text preserves the visible spatial content, so the evidence points to table projection rather than native text extraction. No customer input, hosted service, OCR, or model call is involved.
Source: run-llama/liteparse