Skip to content

export: add C ABI wrappers for paragraph measure/split + language-aware font parse#283

Merged
carlos7ags merged 1 commit intomainfrom
feat/cabi-paragraph-helpers
May 1, 2026
Merged

export: add C ABI wrappers for paragraph measure/split + language-aware font parse#283
carlos7ags merged 1 commit intomainfrom
feat/cabi-paragraph-helpers

Conversation

@carlos7ags
Copy link
Copy Markdown
Owner

Summary

Surfaces four v0.8.0 Go-side additions to the C ABI so non-Go callers (Python, Node, Rust, etc.) get the same surface area without each having to write their own CGO bridge.

New C export Wraps Source PR
folio_paragraph_measure_lines(p, max_width) layout.Paragraph.MeasureLines #238
folio_paragraph_measure_height(p, max_width) layout.Paragraph.MeasureHeight #238
folio_paragraph_split_after_line(p, n, max_width, *head, *tail) layout.Paragraph.SplitAfterLine #253
folio_font_parse_for_language(data, length, lang) font.ParseFontForLanguage #277

Out of scope

font.Fallback / layout.NewParagraphFallback (#225) is intentionally NOT wrapped here. Per its PR description, C ABI exposure is Phase 2 work tied to promoting *EmbeddedFont to an interface so Fallback becomes a drop-in replacement at every existing call site (heading, link, list, tab, table cell, page-level text, C ABI). Wiring the C ABI to the Phase-1 *EmbeddedFont-only constructor and then refactoring it again in Phase 2 would create churn for downstream consumers.

html.Options.Logger (*slog.Logger), Options.Client (*http.Client), and Options.StrictAssets are Go-specific by nature — slog and *http.Client don't have portable C equivalents. Adding a C-flavoured logging callback / HTTP transport surface is its own design pass, deferred.

API design notes

  • Split semantics for split_after_line: returns two handles via out-pointers. Either may be 0 for the no-op halves: n <= 0 returns (0, full clone); n >= total lines returns (full clone, 0). The receiver is unchanged. Caller frees non-zero handles via folio_paragraph_free. out_head and out_tail themselves must be non-NULL — the wrapper returns errInvalidHandle if either is missing rather than crashing.
  • measure_lines returns int32_t: -1 on bad handle. Callers should check folio_last_error for the message.
  • measure_height returns double: -1.0 on bad handle (negative height is impossible for a real paragraph).
  • font_parse_for_language accepts NULL lang: treated as empty string, which font.ParseFontForLanguage handles by falling back to face 0 (matching folio_font_parse_ttf semantics for non-TTC inputs).

Tests

test_cabi.c grows from 397 to 411 assertions (+14 new), covering:

  • measure_lines happy path (narrow vs wide width invariant), bad handle
  • measure_height happy path, bad handle
  • split_after_line n=2 success path with head/tail line-count assertions
  • split_after_line edge cases: n=0 (head=0, tail=full), n=large (head=full, tail=0)
  • split_after_line bad handle, NULL out_head, NULL out_tail
  • font_parse_for_language NULL data rejection (round-trip with real TTC is covered by Go-side tests in font/load_test.go)

Audit

scripts/audit-cabi.sh --build reports:

  • Go //export directives: 393
  • Header declarations: 393
  • Built dylib symbols: 393

All three in sync.

Test plan

  • go vet ./export/... — clean
  • make test-cabi — 411 passed, 0 failed
  • scripts/audit-cabi.sh --build — clean

…uage-aware font parse

Surfaces four Go-side additions from v0.8.0 to the C ABI so non-Go
callers can use them without resorting to a CGO bridge of their own.

Paragraph (3 wrappers):
- folio_paragraph_measure_lines(p, max_width) — wraps Paragraph.MeasureLines
- folio_paragraph_measure_height(p, max_width) — wraps Paragraph.MeasureHeight
- folio_paragraph_split_after_line(p, n, max_width, *head, *tail) — wraps
  Paragraph.SplitAfterLine. Returns two handles via out-pointers, either
  of which may be 0 for the no-op halves (n <= 0 or n >= total). Caller
  frees the non-zero handles.

Font (1 wrapper):
- folio_font_parse_for_language(data, length, lang) — wraps
  font.ParseFontForLanguage. Pan-CJK TTC face selection by BCP-47 tag
  ("zh-CN", "ja", etc.). Empty lang falls back to face 0, matching
  folio_font_parse_ttf semantics.

The font.Fallback / layout.NewParagraphFallback surface (#225) is
intentionally NOT wrapped here; per its PR description, C ABI exposure
is Phase 2 work tied to promoting *EmbeddedFont to an interface.

Tests: test_cabi.c grows from 397 to 411 assertions covering happy path,
bad handle, NULL out-pointers, n <= 0 / n >= total edge cases, and the
narrow-vs-wide width invariant for measure_lines.

Audit: scripts/audit-cabi.sh reports Go //export = header = built
symbols = 393.
@carlos7ags carlos7ags merged commit 2e60e21 into main May 1, 2026
2 checks passed
carlos7ags added a commit that referenced this pull request May 2, 2026
Compiles the v0.7.1..main delta (114 commits, ~40 PRs) into
release-ready CHANGELOG entries and updates MIGRATING with the
v0.7.x → v0.8.0 upgrade guide.

CHANGELOG additions to Unreleased:
- Visual changes section: GPOS cursive horizontal placement (#220),
  CJK paragraphs across page breaks (#246), Unicode NFC normalization
  at layout entry point (#217)
- Changed (breaking): layout.UnitValue no longer comparable with ==
  (#236), in addition to the existing BasePath removal and @font-face
  resolution change (#85)
- Added: font.Fallback (#225), Paragraph.MeasureLines/Height (#238),
  Paragraph.SplitAfterLine (#253), layout.Anchor + auto-named-
  destinations (#223), layout.UnitCalc + CalcUnit (#236),
  Info.Language (#214), PDF/A-3a + PDF/A-4 family (#214), C ABI
  additions (#214 + #283), CSS :empty / ::placeholder (#215), 8 Indic
  scripts (#216), GPOS Types 3/5/6 (#218 / #213), body-flow
  counter(page) (#221), GCPM bookmark properties (#224), the calc
  shorthand series across 13 properties (#236-#261)
- Changed: HTML CSS parser refactored into declarative property
  registry (#271)
- Fixed: GPOS cursive double-counted hmtx + Type 5 3+ component
  fallback (#220), inline elements + Arabic OriginalText preserved
  across page splits (#241), font: paren-aware slash detector +
  column-rule none/hidden zeros width (#270), transform() paren-aware
  arg extractor (#272), parseAngle/parseNumericVal/parseLineHeight
  understand calc/min/max/clamp (#274 / #275 / #284), margin auto-flag
  detector aligned (#263)
- Deprecated reminder: the four core direct-field accesses from v0.7.0
  carry forward to v0.8.0; removal target stays v1.0
- Tests: hyphenation contract guard (#250)
- Documentation: docs/CSS_SUPPORT.md introduced (#271/#273) and
  extended with Selectors / At-rules / Functions sections (#162/#282)

MIGRATING additions for v0.7.x → v0.8.0:
- C ABI surface grew from 388 to 393 (+5): folio_document_set_language,
  FOLIO_PDFA_3A/4/4F/4E (#214), folio_paragraph_measure_lines /
  measure_height / split_after_line (#283), folio_font_parse_for_language
  (#283)
- layout.UnitValue compile-error break for == comparisons
- Visual-change checklist for regression-diffing PDFs
- Asset-resolution side-effects from #229 (URLPolicy uniformity,
  absolute system font path with BaseFS=nil, FallbackFontPath
  programmatic-only carve-out)
carlos7ags added a commit that referenced this pull request May 2, 2026
CHANGELOG additions to Unreleased:
- Visual changes: <th> behavioural shift from #288 — explicit
  text-align: left on <th> is now preserved instead of being
  silently re-centered by the table-cell default heuristic.
- Added: text-align: start | end with direction-relative late
  binding (#288); text-decoration-line: overline (#289); CSS
  border styles groove/ridge/inset/outset rendered with per-side
  dark/light modulation (#290).

MIGRATING additions to the v0.7.x → v0.8.0 Visual changes section:
- <th> default-center heuristic shift (mirror of CHANGELOG entry,
  framed for upgraders auditing PDF diffs).
- border 3D styles now render the bevel — documents that used
  ridge/groove/inset/outset will see colored modulation instead
  of a flat solid line.

The text-align: start/end addition itself is in CHANGELOG Added but
intentionally omitted from MIGRATING Visual changes — the new
keywords were rejected pre-fix, so any document that relied on the
keyword is opting INTO the new behaviour rather than seeing an
existing render shift.

text-decoration overline is similarly omitted from MIGRATING — the
keyword was silently dropped pre-fix, so adding a visible overline
is an opt-in.

Three PRs (#283 C ABI, #284 calc fixes, #287 numeric font-weight)
are already entered. v0.8.0 is now release-note ready once #285
itself merges.
carlos7ags added a commit that referenced this pull request May 3, 2026
Compiles the v0.7.1..main delta (114 commits, ~40 PRs) into
release-ready CHANGELOG entries and updates MIGRATING with the
v0.7.x → v0.8.0 upgrade guide.

CHANGELOG additions to Unreleased:
- Visual changes section: GPOS cursive horizontal placement (#220),
  CJK paragraphs across page breaks (#246), Unicode NFC normalization
  at layout entry point (#217)
- Changed (breaking): layout.UnitValue no longer comparable with ==
  (#236), in addition to the existing BasePath removal and @font-face
  resolution change (#85)
- Added: font.Fallback (#225), Paragraph.MeasureLines/Height (#238),
  Paragraph.SplitAfterLine (#253), layout.Anchor + auto-named-
  destinations (#223), layout.UnitCalc + CalcUnit (#236),
  Info.Language (#214), PDF/A-3a + PDF/A-4 family (#214), C ABI
  additions (#214 + #283), CSS :empty / ::placeholder (#215), 8 Indic
  scripts (#216), GPOS Types 3/5/6 (#218 / #213), body-flow
  counter(page) (#221), GCPM bookmark properties (#224), the calc
  shorthand series across 13 properties (#236-#261)
- Changed: HTML CSS parser refactored into declarative property
  registry (#271)
- Fixed: GPOS cursive double-counted hmtx + Type 5 3+ component
  fallback (#220), inline elements + Arabic OriginalText preserved
  across page splits (#241), font: paren-aware slash detector +
  column-rule none/hidden zeros width (#270), transform() paren-aware
  arg extractor (#272), parseAngle/parseNumericVal/parseLineHeight
  understand calc/min/max/clamp (#274 / #275 / #284), margin auto-flag
  detector aligned (#263)
- Deprecated reminder: the four core direct-field accesses from v0.7.0
  carry forward to v0.8.0; removal target stays v1.0
- Tests: hyphenation contract guard (#250)
- Documentation: docs/CSS_SUPPORT.md introduced (#271/#273) and
  extended with Selectors / At-rules / Functions sections (#162/#282)

MIGRATING additions for v0.7.x → v0.8.0:
- C ABI surface grew from 388 to 393 (+5): folio_document_set_language,
  FOLIO_PDFA_3A/4/4F/4E (#214), folio_paragraph_measure_lines /
  measure_height / split_after_line (#283), folio_font_parse_for_language
  (#283)
- layout.UnitValue compile-error break for == comparisons
- Visual-change checklist for regression-diffing PDFs
- Asset-resolution side-effects from #229 (URLPolicy uniformity,
  absolute system font path with BaseFS=nil, FallbackFontPath
  programmatic-only carve-out)
carlos7ags added a commit that referenced this pull request May 3, 2026
CHANGELOG additions to Unreleased:
- Visual changes: <th> behavioural shift from #288 — explicit
  text-align: left on <th> is now preserved instead of being
  silently re-centered by the table-cell default heuristic.
- Added: text-align: start | end with direction-relative late
  binding (#288); text-decoration-line: overline (#289); CSS
  border styles groove/ridge/inset/outset rendered with per-side
  dark/light modulation (#290).

MIGRATING additions to the v0.7.x → v0.8.0 Visual changes section:
- <th> default-center heuristic shift (mirror of CHANGELOG entry,
  framed for upgraders auditing PDF diffs).
- border 3D styles now render the bevel — documents that used
  ridge/groove/inset/outset will see colored modulation instead
  of a flat solid line.

The text-align: start/end addition itself is in CHANGELOG Added but
intentionally omitted from MIGRATING Visual changes — the new
keywords were rejected pre-fix, so any document that relied on the
keyword is opting INTO the new behaviour rather than seeing an
existing render shift.

text-decoration overline is similarly omitted from MIGRATING — the
keyword was silently dropped pre-fix, so adding a visible overline
is an opt-in.

Three PRs (#283 C ABI, #284 calc fixes, #287 numeric font-weight)
are already entered. v0.8.0 is now release-note ready once #285
itself merges.
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