Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ go.work.sum
# OS
.DS_Store
Thumbs.db

# Python bytecode from the benchmark harness
__pycache__/
*.pyc
14 changes: 14 additions & 0 deletions bench/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,18 @@ go 1.25.0

require github.com/hallelx2/pdftable v0.0.0

require (
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/hhrutter/lzw v1.0.0 // indirect
github.com/hhrutter/pkcs7 v0.2.2 // indirect
github.com/hhrutter/tiff v1.0.3 // indirect
github.com/mattn/go-runewidth v0.0.23 // indirect
github.com/pdfcpu/pdfcpu v0.12.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
golang.org/x/crypto v0.50.0 // indirect
golang.org/x/image v0.39.0 // indirect
golang.org/x/text v0.36.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

replace github.com/hallelx2/pdftable => ../
24 changes: 24 additions & 0 deletions bench/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/hhrutter/lzw v1.0.0 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0=
github.com/hhrutter/lzw v1.0.0/go.mod h1:2HC6DJSn/n6iAZfgM3Pg+cP1KxeWc3ezG8bBqW5+WEo=
github.com/hhrutter/pkcs7 v0.2.2 h1:xMoifoVWah1LNym3C0pomEiLmyJyVIBXt/8oTPyPz+8=
github.com/hhrutter/pkcs7 v0.2.2/go.mod h1:aEzKz0+ZAlz7YaEMY47jDHL14hVWD6iXt0AgqgAvWgE=
github.com/hhrutter/tiff v1.0.3 h1:POV5xITOE1Lt5FvP24ylft0LyCmHmc8GkJ1SVlvUyk0=
github.com/hhrutter/tiff v1.0.3/go.mod h1:zZDLVY4cp9za2FLrryAaGszwWYAUM6DrRiBR0l//mxA=
github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/pdfcpu/pdfcpu v0.12.1 h1:HwoN72zJCj+pPbfMDChYBTZrT7SY0VwgUzqeaId3I20=
github.com/pdfcpu/pdfcpu v0.12.1/go.mod h1:7KPpVLMavcpliPrtN6o7Kuk3cFtYq8nii3SJnnsK7ps=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww=
golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
Binary file removed bench/icdar2013/__pycache__/score.cpython-313.pyc
Binary file not shown.
55 changes: 46 additions & 9 deletions bench/icdar2013/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"flag"
"fmt"
"os"
"strconv"

"github.com/hallelx2/pdftable"
)
Expand All @@ -15,9 +16,18 @@ type tableOut struct {
Rows [][]string `json:"rows"`
}

// edgeSet is one page's explicit row/column boundaries, in PDF points.
type edgeSet struct {
V []float64 `json:"v"`
H []float64 `json:"h"`
}

func main() {
strategy := flag.String("strategy", "lines", "lines | text | fallback")
strategy := flag.String("strategy", "lines",
"lines | text | mixed | auto | lines-then-mixed | fallback")
merge := flag.Bool("merge", false, "TableSettings.MergeSplitTokens")
oracle := flag.String("oracle", "",
`JSON of per-page explicit edges: {"1":{"v":[..],"h":[..]}}`)
flag.Parse()

doc, err := pdftable.OpenFile(flag.Arg(0))
Expand All @@ -38,27 +48,37 @@ func main() {
}
lines := mk(pdftable.StrategyLines, pdftable.StrategyLines)
text := mk(pdftable.StrategyText, pdftable.StrategyText)

// "mixed" is the booktabs case: horizontal rules give the rows, word
// alignment gives the columns. A table ruled only horizontally has no
// ruling intersections at all, so pure "lines" cannot see it.
// alignment gives the columns.
mixed := mk(pdftable.StrategyText, pdftable.StrategyLines)
auto := mk(pdftable.StrategyAuto, pdftable.StrategyAuto)

var attempts []pdftable.TableSettings
switch *strategy {
case "lines":
attempts = []pdftable.TableSettings{lines}
case "text":
attempts = []pdftable.TableSettings{text}
case "mixed":
attempts = []pdftable.TableSettings{mixed}
case "lines-then-mixed":
attempts = []pdftable.TableSettings{lines, mixed}
case "auto":
attempts = []pdftable.TableSettings{auto}
default: // fallback: ruled cells first, whitespace alignment if none
case "lines-then-mixed":
attempts = []pdftable.TableSettings{lines, mixed}
case "fallback":
attempts = []pdftable.TableSettings{lines, text}
default:
attempts = []pdftable.TableSettings{lines}
}

// Oracle mode: the caller supplies the row/column boundaries and
// pdftable only fills the cells. This is exactly the shape of the
// hybrid a layout model would drive — and fed GROUND-TRUTH edges it
// measures the ceiling that hybrid can reach: how good extraction gets
// if detection and gridding were solved perfectly.
var oracleEdges map[string]edgeSet
if *oracle != "" {
if b, err := os.ReadFile(*oracle); err == nil {
_ = json.Unmarshal(b, &oracleEdges)
}
}

out := []tableOut{}
Expand All @@ -67,6 +87,23 @@ func main() {
if err != nil {
continue
}

if oracleEdges != nil {
e, ok := oracleEdges[strconv.Itoa(i)]
if !ok || len(e.V) < 2 || len(e.H) < 2 {
continue
}
s := mk(pdftable.StrategyExplicit, pdftable.StrategyExplicit)
s.ExplicitVerticalLines = e.V
s.ExplicitHorizontalLines = e.H
if tables, err := p.ExtractTables(s); err == nil {
for _, t := range tables {
out = append(out, tableOut{Page: i, Rows: t.Rows})
}
}
continue
}

for _, s := range attempts {
tables, err := p.ExtractTables(s)
if err != nil || len(tables) == 0 {
Expand Down
Loading