Merge remote-tracking branch 'origin/master' into p5a
This commit is contained in:
@@ -64,3 +64,13 @@ reaches orion until the owner's review, the merge, and straylight's pin bump.
|
|||||||
files (task 06 now allows exactly that field).
|
files (task 06 now allows exactly that field).
|
||||||
- 2026-09-26, the given test's gate note: task 06 ends with `make gate` red on exactly the three
|
- 2026-09-26, the given test's gate note: task 06 ends with `make gate` red on exactly the three
|
||||||
task-07 tests (the pattern crossbar v2.2 used); the gate runs green after 07.
|
task-07 tests (the pattern crossbar v2.2 used); the gate runs green after 07.
|
||||||
|
- 2026-09-26, tasks 06–08 after the split: 52, 19 and 14 min, each first-gate. `run-plan`: all
|
||||||
|
tasks done at 04:37.
|
||||||
|
- **Owner review** (details in `docs/implementer-log.md`): end-to-end on real PDFs (arXiv, NIST,
|
||||||
|
RP2040 datasheet, a PhD thesis) against dixie's 9B found one real defect the given tests could
|
||||||
|
not see — **no outline for any LaTeX or Acrobat PDF**: named destinations were skipped (my task
|
||||||
|
02 said to skip them) and reader's `Dict.Get` answers `reader.Null{}`, not nil, for a missing
|
||||||
|
key, which hid the `/A` action. Fixed with a name-tree lookup; `internal/pdftest` gained
|
||||||
|
`NamedDests` (owner change to a given file; the copy here is the new reference). Also fixed: a
|
||||||
|
race on the default pool, revalidation outside the per-host limit (my spec), the runner waiting
|
||||||
|
for the deadline on over-limit output, `Accept` lacking `application/pdf` (my spec).
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ type Doc struct {
|
|||||||
Pages []string
|
Pages []string
|
||||||
Outline []Outline
|
Outline []Outline
|
||||||
Scanned []int
|
Scanned []int
|
||||||
|
// NamedDests writes the outline the way LaTeX's hyperref and Acrobat do: each item is a
|
||||||
|
// /GoTo action to a named destination (resolved through the catalog's /Names /Dests tree)
|
||||||
|
// and its /Title is an indirect object. Without it items carry a direct /Dest array.
|
||||||
|
NamedDests bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build returns the PDF bytes for d. It panics on a Doc it cannot express (an outline entry
|
// Build returns the PDF bytes for d. It panics on a Doc it cannot express (an outline entry
|
||||||
@@ -51,9 +55,15 @@ func Build(d Doc) []byte {
|
|||||||
scanned[p] = true
|
scanned[p] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// With NamedDests the outline items are followed by one title object per item and then
|
||||||
|
// the name tree, so the numbers below stay fixed by position.
|
||||||
|
nameTree := outlineRoot + 1 + 2*len(d.Outline)
|
||||||
catalog := "<< /Type /Catalog /Pages 2 0 R"
|
catalog := "<< /Type /Catalog /Pages 2 0 R"
|
||||||
if len(d.Outline) > 0 {
|
if len(d.Outline) > 0 {
|
||||||
catalog += fmt.Sprintf(" /Outlines %d 0 R /PageMode /UseOutlines", outlineRoot)
|
catalog += fmt.Sprintf(" /Outlines %d 0 R /PageMode /UseOutlines", outlineRoot)
|
||||||
|
if d.NamedDests {
|
||||||
|
catalog += fmt.Sprintf(" /Names << /Dests %d 0 R >>", nameTree)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
obj(catalog + " >>")
|
obj(catalog + " >>")
|
||||||
var kids []string
|
var kids []string
|
||||||
@@ -93,6 +103,9 @@ func Build(d Doc) []byte {
|
|||||||
}
|
}
|
||||||
n := first + i
|
n := first + i
|
||||||
item := fmt.Sprintf("<< /Title (%s) /Parent %d 0 R /Dest [%d 0 R /XYZ null null null]", esc(o.Title), outlineRoot, pageObj(o.Page-1))
|
item := fmt.Sprintf("<< /Title (%s) /Parent %d 0 R /Dest [%d 0 R /XYZ null null null]", esc(o.Title), outlineRoot, pageObj(o.Page-1))
|
||||||
|
if d.NamedDests {
|
||||||
|
item = fmt.Sprintf("<< /Title %d 0 R /Parent %d 0 R /A << /S /GoTo /D (dest.%d) >>", last+1+i, outlineRoot, i)
|
||||||
|
}
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
item += fmt.Sprintf(" /Prev %d 0 R", n-1)
|
item += fmt.Sprintf(" /Prev %d 0 R", n-1)
|
||||||
}
|
}
|
||||||
@@ -101,6 +114,14 @@ func Build(d Doc) []byte {
|
|||||||
}
|
}
|
||||||
obj(item + " >>")
|
obj(item + " >>")
|
||||||
}
|
}
|
||||||
|
if d.NamedDests {
|
||||||
|
var names []string
|
||||||
|
for i, o := range d.Outline {
|
||||||
|
obj(fmt.Sprintf("(%s)", esc(o.Title)))
|
||||||
|
names = append(names, fmt.Sprintf("(dest.%d) [%d 0 R /XYZ null null null]", i, pageObj(o.Page-1)))
|
||||||
|
}
|
||||||
|
obj(fmt.Sprintf("<< /Names [%s] >>", strings.Join(names, " ")))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
xref := b.Len()
|
xref := b.Len()
|
||||||
|
|||||||
Reference in New Issue
Block a user