Summary
With output_format="markdown", a table containing a single sparse row (a row where only some of the columns are populated) is not emitted as a table at all. Instead the table is transposed into column-wise prose blocks (Order Date 03/14/2024 05/02/2024 06/08/2024, Item Code IC-1048 IC-1049 IC-1051, …), a fragment of it re-emerges as a 4-column table with no header, and the remaining values are left orphaned on a trailing line with no row association at all.
The same PDF renders correctly in the default text mode — every column spatially aligned, every row present, including the sparse row. So the geometry is available; it's the Markdown structuring step that loses it.
This is output_format="markdown" like #395, but a different trigger and a different failure, so I've filed it separately rather than as a comment there. #395 is free text interleaved at the head of the table, where the header becomes prose and the separator shifts down — but every data row survives as a table row. Here the trigger is a row with missing cells, and the result is that values are silently detached from their rows. A fix targeting #395's header/separator handling would not obviously address this.
Reproduction
Fully self-contained (pip install liteparse reportlab):
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
W, H = letter
COLS = [48, 108, 190, 300, 360, 424, 486, 545]
HEADERS = ["Order Date", "Item Code", "Description", "Status", "Unit Cost", "Qty", "Tax", "Ref"]
c = canvas.Canvas("sparse_row.pdf", pagesize=letter)
c.setFont("Helvetica-Bold", 12)
c.drawString(48, H - 56, "Order Detail Report by Account")
c.setFont("Helvetica", 8)
y = H - 96
for x, h in zip(COLS, HEADERS):
c.drawString(x, y, h)
y -= 22
def row(cells, cols=COLS):
global y
for x, cell in zip(cols, cells):
c.drawString(x, y, cell)
y -= 16
row(["03/14/2024", "IC-1048", "WIDGET ASSEMBLY", "Shipped", "$482,110.40", "$0.00", "$9,215.75", "N/A"])
row(["05/02/2024", "IC-1049", "BRACKET SET", "Shipped", "$0.00", "$0.00", "$0.00", "N/A"])
# A SPARSE row: only 3 of the 8 columns are populated.
row(["06/08/2024", "IC-1051", "Open"], [COLS[0], COLS[1], COLS[3]])
y -= 6
# A free-text note, indented under the Description column.
c.drawString(190, y, "Unit was delayed in transit and re-routed through the regional hub.")
y -= 22
row(["06/08/2024", "IC-1051", "GEARBOX HOUSING", "Open", "$918,340.55", "$0.00", "$12,004.10", "N/A"])
row(["07/19/2024", "IC-1050", "CONTROL MODULE", "Open", "$31,905.22", "$4,100.00", "$0.00", "N/A"])
c.showPage()
c.save()
from liteparse import LiteParse
data = open("sparse_row.pdf", "rb").read()
print("--- output_format='text' ---")
print(LiteParse(ocr_enabled=False, quiet=True).parse(data).pages[0].text)
print("--- output_format='markdown' ---")
print(LiteParse(ocr_enabled=False, quiet=True, output_format="markdown").parse(data).pages[0].markdown)
Actual output
output_format="text" — correct. All 8 columns aligned, all rows present, the sparse row and the note both in the right place:
Order Date Item Code Description Status Unit Cost Qty Tax Ref
03/14/2024 IC-1048 WIDGET ASSEMBLY Shipped $482,110.40 $0.00 $9,215.75 N/A
05/02/2024 IC-1049 BRACKET SET Shipped $0.00 $0.00 $0.00 N/A
06/08/2024 IC-1051 Open
Unit was delayed in transit and re-routed through the regional hub.
06/08/2024 IC-1051 GEARBOX HOUSING Open $918,340.55 $0.00 $12,004.10 N/A
07/19/2024 IC-1050 CONTROL MODULE Open $31,905.22 $4,100.00 $0.00 N/A
output_format="markdown" — the table is gone:
# Order Detail Report by Account
Order Date 03/14/2024 05/02/2024 06/08/2024
Item Code IC-1048 IC-1049 IC-1051
06/08/2024 IC-1051 07/19/2024 IC-1050
Description WIDGET ASSEMBLY BRACKET SET
Status Shipped Shipped Open
Unit Cost $482,110.40 $0.00
Qty $0.00 $0.00
Tax $9,215.75 $0.00
Ref N/A N/A
Unit was delayed in transit and re-routed through the regional hub.
| GEARBOX HOUSING | Open | $918,340.55 | $0.00 |
|---|---|---|---|
| CONTROL MODULE | Open | $31,905.22 | $4,100.00 |
$12,004.10 N/A $0.00 N/A
What's wrong with it
Three distinct problems, all from the one sparse row:
- The table is transposed into column-wise prose.
Unit Cost $482,110.40 $0.00 groups the column's values together. A reader has no way to know which order each value belongs to.
- The surviving table fragment has lost its identifying columns.
| GEARBOX HOUSING | Open | $918,340.55 | $0.00 | has no Order Date and no Item Code, and no header row — the separator sits above the first row, so a Markdown parser reads GEARBOX HOUSING as a column name.
- Values are orphaned entirely. The trailing
$12,004.10 N/A $0.00 N/A belongs to the last two rows (Tax and Ref), but nothing in the output associates them with a row.
The failure is silent — the output is well-formed Markdown, and every value is still present somewhere on the page, so a consumer has no signal that the row/column association has been destroyed. That's what makes it worse than an outright parse error.
Expected
The sparse row should be emitted as a table row with empty cells:
| Order Date | Item Code | Description | Status | Unit Cost | Qty | Tax | Ref |
|---|---|---|---|---|---|---|---|
| 03/14/2024 | IC-1048 | WIDGET ASSEMBLY | Shipped | $482,110.40 | $0.00 | $9,215.75 | N/A |
| 05/02/2024 | IC-1049 | BRACKET SET | Shipped | $0.00 | $0.00 | $0.00 | N/A |
| 06/08/2024 | IC-1051 | | Open | | | | |
| 06/08/2024 | IC-1051 | GEARBOX HOUSING | Open | $918,340.55 | $0.00 | $12,004.10 | N/A |
| 07/19/2024 | IC-1050 | CONTROL MODULE | Open | $31,905.22 | $4,100.00 | $0.00 | N/A |
Sparse rows are common in real reports — subtotal lines, continuation rows, and records where a field simply doesn't apply. The text output shows the column geometry is already recovered correctly, so this looks like it's in the table-structuring step rather than in layout analysis.
Environment
- liteparse 2.11.0
- Python 3.13.13
- macOS 26.6, arm64
Summary
With
output_format="markdown", a table containing a single sparse row (a row where only some of the columns are populated) is not emitted as a table at all. Instead the table is transposed into column-wise prose blocks (Order Date 03/14/2024 05/02/2024 06/08/2024,Item Code IC-1048 IC-1049 IC-1051, …), a fragment of it re-emerges as a 4-column table with no header, and the remaining values are left orphaned on a trailing line with no row association at all.The same PDF renders correctly in the default
textmode — every column spatially aligned, every row present, including the sparse row. So the geometry is available; it's the Markdown structuring step that loses it.This is
output_format="markdown"like #395, but a different trigger and a different failure, so I've filed it separately rather than as a comment there. #395 is free text interleaved at the head of the table, where the header becomes prose and the separator shifts down — but every data row survives as a table row. Here the trigger is a row with missing cells, and the result is that values are silently detached from their rows. A fix targeting #395's header/separator handling would not obviously address this.Reproduction
Fully self-contained (
pip install liteparse reportlab):Actual output
output_format="text"— correct. All 8 columns aligned, all rows present, the sparse row and the note both in the right place:output_format="markdown"— the table is gone:What's wrong with it
Three distinct problems, all from the one sparse row:
Unit Cost $482,110.40 $0.00groups the column's values together. A reader has no way to know which order each value belongs to.| GEARBOX HOUSING | Open | $918,340.55 | $0.00 |has noOrder Dateand noItem Code, and no header row — the separator sits above the first row, so a Markdown parser readsGEARBOX HOUSINGas a column name.$12,004.10 N/A $0.00 N/Abelongs to the last two rows (TaxandRef), but nothing in the output associates them with a row.The failure is silent — the output is well-formed Markdown, and every value is still present somewhere on the page, so a consumer has no signal that the row/column association has been destroyed. That's what makes it worse than an outright parse error.
Expected
The sparse row should be emitted as a table row with empty cells:
Sparse rows are common in real reports — subtotal lines, continuation rows, and records where a field simply doesn't apply. The
textoutput shows the column geometry is already recovered correctly, so this looks like it's in the table-structuring step rather than in layout analysis.Environment