Skip to content

Commit b201d78

Browse files
committed
fix(parser): drop publisher/license boilerplate rows
arXiv (and similar) print a rotated license stamp down the page margin: "Provided proper attribution is provided, Google hereby grants permission to reproduce the tables and figures ... solely for use in journalistic or scholarly works." Split across rows, each fragment passed looksLikeHeading and became a junk top-level section (and sometimes the document title). isBoilerplateLine drops rows matching a curated signature list (license/copyright/preprint phrases) plus the bare arXiv id stamp, before they reach structure or content extraction. Signatures are deliberately specific — verified no false positives on real prose incl. sentences that merely contain "permission" or "rights".
1 parent 793d416 commit b201d78

2 files changed

Lines changed: 77 additions & 0 deletions

File tree

pkg/parser/boilerplate_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package parser
2+
3+
import "testing"
4+
5+
func TestIsBoilerplateLine(t *testing.T) {
6+
boiler := []string{
7+
"Provided proper attribution is provided, Google hereby grants permission to",
8+
"reproduce the tables and figures in this paper solely for use in journalistic or",
9+
"All Rights Reserved.",
10+
"This work is licensed under a Creative Commons Attribution 4.0 License.",
11+
"arXiv:1706.03762v7 [cs.CL] 2 Aug 2023",
12+
"Preprint. Under review.",
13+
}
14+
for _, s := range boiler {
15+
if !isBoilerplateLine(s) {
16+
t.Errorf("expected boilerplate: %q", s)
17+
}
18+
}
19+
content := []string{
20+
"Attention Is All You Need",
21+
"3 Model Architecture",
22+
"The dominant sequence transduction models are based on attention.",
23+
"We grant the model permission to attend to all positions.", // 'permission' but not boilerplate
24+
"Results",
25+
"References",
26+
"Recommendation: initiate low-dose therapy (evidence grade A).",
27+
}
28+
for _, s := range content {
29+
if isBoilerplateLine(s) {
30+
t.Errorf("false positive on content: %q", s)
31+
}
32+
}
33+
}

pkg/parser/pdf.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,13 @@ func extractPDFRows(reader *pdflib.Reader) ([]pdfRow, error) {
279279
if text == "" {
280280
continue
281281
}
282+
// Drop publisher/preprint boilerplate (e.g. the rotated
283+
// arXiv license stamp in the left margin). Left in, it
284+
// pollutes the structure with junk top-level "headings"
285+
// and the document title.
286+
if isBoilerplateLine(text) {
287+
continue
288+
}
282289
out = append(out, pdfRow{
283290
page: pageNum,
284291
fontSize: b.maxFS,
@@ -452,6 +459,43 @@ func normalizeForMatch(s string) string {
452459
return strings.TrimSpace(b.String())
453460
}
454461

462+
// boilerplateSignatures are substrings (lower-cased) that mark a line
463+
// as publisher / preprint / license boilerplate rather than document
464+
// content. Kept deliberately specific so we never drop real prose —
465+
// each phrase is something that essentially only appears in a
466+
// copyright/license stamp.
467+
var boilerplateSignatures = []string{
468+
"hereby grants permission",
469+
"provided proper attribution is provided",
470+
"solely for use in journalistic",
471+
"all rights reserved",
472+
"is licensed under a creative commons",
473+
"licensed under cc by",
474+
"this work is licensed under",
475+
"permission to make digital or hard copies",
476+
"copyright held by the owner",
477+
"preprint. under review",
478+
"preprint submitted to",
479+
}
480+
481+
// isBoilerplateLine reports whether a row is publisher/license noise.
482+
// Matches the curated signature list, plus the bare arXiv id stamp
483+
// ("arXiv:2401.01234v2 [cs.CL] 5 Jan 2024") that arXiv prints down
484+
// the page margin.
485+
func isBoilerplateLine(s string) bool {
486+
low := strings.ToLower(strings.TrimSpace(s))
487+
for _, sig := range boilerplateSignatures {
488+
if strings.Contains(low, sig) {
489+
return true
490+
}
491+
}
492+
// arXiv margin stamp: starts with "arxiv:" followed by a digit.
493+
if strings.HasPrefix(low, "arxiv:") && len(low) > 6 && low[6] >= '0' && low[6] <= '9' {
494+
return true
495+
}
496+
return false
497+
}
498+
455499
func looksLikeHeading(s string) bool {
456500
// Headings are rarely > 14 words and never end with sentence punctuation
457501
// from the middle of a paragraph.

0 commit comments

Comments
 (0)