Skip to content

Copy pages at chosen numbers, and read and write page label sections - #94

Merged
bobsingor merged 2 commits into
embedpdf/mainfrom
embedpdf/feature/optimistic-pages
Nov 1, 2026
Merged

bobsingor merged 2 commits into
embedpdf/mainfrom
embedpdf/feature/optimistic-pages

Conversation

@bobsingor

@bobsingor bobsingor commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Two mechanics the viewer needs to show page edits before the engine answers: copied pages named before they exist, and page labels that follow their sections. Where the pages and the sections go stays the TypeScript engine's decision; the fork reads and writes.

Pages copied at chosen numbers. EPDFDoc_ImportPages takes page_objnums, one object number per copied page (0 takes the next free one), as EPDFPage_InsertBlankRaw already does for a blank page. CPDF_PageExporter::ExportPages creates each copy at its number with CreateNewPageAt (tagged chosen-object-numbers); FPDF_ImportPagesByIndex is unchanged. The numbers are checked before anything is copied: free in the layer's open transaction, none chosen twice. A refused import writes nothing, not even a field. Everything else the pages bring gets the next free numbers, above the ones handed out. This is a signature change: the engine's two calls pass the numbers or NULL when it pins this release.

No same-document copy is added. The engine duplicates pages by copying them out into a scratch document, renaming clashing fields there as it does for an insert, and importing them back at the numbers it reserved.

Page label sections. EPDFDoc_GetPageLabelSectionCount and EPDFDoc_GetPageLabelSectionAt read /PageLabels as sections the way FPDF_GetPageLabel labels the pages: an entry starts a section at its key; pages before the first entry, and an entry that isn't a dictionary, number by their place; an entry past the last page is left out. The first section always starts at page 0, and each has its style (EPDF_PAGE_LABEL_STYLE_*), first number and prefix. EPDFDoc_SetPageLabelSections writes sections as given (first at page 0, increasing, every one at a page) as one flat number tree, into /PageLabels' own object when it is one; no sections removes /PageLabels. Today page moves, deletes and inserts leave the number tree by position, so labels slide onto other pages; the engine will move the sections and write them in the same transaction as the page op.

A fix on the way. FPDF_GetPageLabel read without the document's view scope, so on a layer it read the base's /PageLabels and a layer's own labels didn't count (a debug build asserts). It now opens one, tagged view-scope.

Checks. macOS on this branch: unit 1312 pass; embedder 1707 pass, and the 30 failures are the known macOS pixel ones (the same 30 before and after rebasing onto main with the fonts merge). New tests: EPDFPageLabelsEmbedderTest (7: a real file, a two-leaf tree compared page by page with FPDF_GetPageLabel, writes, round trips, refusals, a layer transaction with abort, commit and artifact reopen) and EPDFPpoEmbedderTest.CopiesTakeTheChosenNumbers / RefusesNumbersItCannotTake. Layer mode is clean for the page import, page label, object number and FPDFDocEmbedderTest suites (the two failures are the listed DeletePageAndSave* expectations). check-fork-tags --strict and check-c-api pass. The Linux unit and embedder suites haven't run yet; this waits for them before merging.

An optimistic edit names a new page before the engine makes it: a blank
page already takes its number (EPDFPage_InsertBlankRaw), a copied one
didn't. EPDFDoc_ImportPages now takes page_objnums, one number per copied
page (0 takes the next free one), and the page exporter creates each copy
at its number with CPDF_Document::CreateNewPageAt. The copy's annotations
name it as their page; what else the pages bring gets the next free
numbers, above the ones handed out.

The numbers are checked before anything is copied: free in the layer's
open transaction, and none chosen twice. A refused import writes nothing,
not even a field.

ExportPages takes the numbers as an optional span, tagged
chosen-object-numbers; FPDF_ImportPagesByIndex is unchanged.
/PageLabels numbers pages in sections, but nothing in the API reads a
section or writes one: FPDF_GetPageLabel only gives a page's finished
label, and page moves, deletes and inserts leave the number tree by
position, so labels slide onto other pages.

EPDFDoc_GetPageLabelSectionCount and EPDFDoc_GetPageLabelSectionAt read
the sections as FPDF_GetPageLabel labels the pages: an entry starts a
section at its key; pages before the first entry, and an entry that isn't
a dictionary, number by their place; an entry past the last page is left
out. So the first section always starts at page 0, and each section has
its style, first number and prefix.

EPDFDoc_SetPageLabelSections writes sections as given (the first at page
0, each after the one before, every one at a page) as one flat number
tree, into /PageLabels' own object when it is one; no sections removes
/PageLabels. Where sections go when pages change is the caller's.

FPDF_GetPageLabel now reads through the document's view scope: on a
layer it read the base's /PageLabels, so a layer's own labels didn't
count (a debug build asserts).
@bobsingor
bobsingor merged commit 4e4a8ea into embedpdf/main Nov 1, 2026
1 check passed
@bobsingor
bobsingor deleted the embedpdf/feature/optimistic-pages branch November 1, 2026 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant