Skip to content

GH-494 truetype font hinting tie in - #534

Open
pcorless wants to merge 8 commits into
mainfrom
GH-494.truetype.font.hinting
Open

GH-494 truetype font hinting tie in #534
pcorless wants to merge 8 commits into
mainfrom
GH-494.truetype.font.hinting

Conversation

@pcorless

Copy link
Copy Markdown
Owner
  • hook for a bunch of work gone into fontbox for supporting composite fonts hinting instructions.

pcorless and others added 8 commits June 15, 2026 20:38
Two conflicts, both from main reworking the same font paint paths this
branch restructured for hinting.

ZSimpleFont.getOutline: kept both sides — main's isSubstitutedNotdef guard
plus this branch's unhinted cached outline (clipping/selection geometry must
not be grid-fit).

ZFontType2: this branch removed the paint() override in favour of the shared
ZSimpleFont.paint(), so main's additions to it were carried across rather
than kept in place — the isSubstitutedNotdef guard is already in the
inherited paint(), and the null-safe trueTypeFont.getGlyph() check moved into
getGlphyShape(). Main also widened that catch to RuntimeException for fontbox
throwing on OTF/CFF fonts with no glyf table; the shared paint() already
catches RuntimeException, and GlyphCache swallows IOException internally, so
that path is preserved.

Not compile-verified: this branch needs the fontbox 4.0.0-SNAPSHOT fork that
provides getHintedPath(gid, ppem), which is not installed in the local
repository.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WDXkgMFf1n9eaXi5q29exm
…box.ttf.hinting system property from FontBox fork
GH-535 landed the unhinted half of this branch's glyph cache on main, so the
three cache files conflicted with the versions they were extracted from.
Resolved by taking main as the base and re-applying the hinting layer on top:

- GlyphCache keeps main's PDFBox attribution block and its FINE-level,
  RuntimeException-catching handler, with the (code, ppem) hinted map and the
  getPathForCharacterCode(code, ppem) overload added back.  The grid-fit
  failure path picks up the same treatment - FINE rather than WARNING, and
  RuntimeException caught alongside IOException - since a failed grid-fit just
  falls back to a correct unhinted outline.
- ZSimpleFont keeps resolveGlyphShape/getUnitsPerEm/getHintedGlphyShape/
  hintingPpem and main's comment wording.
- ZFontType2 keeps main's getGlphyShape() plus this branch's getUnitsPerEm()
  and getHintedGlphyShape() overrides.

Not verified by a build: fontbox 4.0.0-SNAPSHOT is not installed locally.
Compiling against stock 3.0.6 leaves exactly three unresolved symbols, all
fork-only (isHintingEnabled x2, getHintedPath x1), and nothing structural.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cw2YK2dUcKQuFdzJ26gwQV
Five glyph-ish names had accumulated in ZSimpleFont - getGlphyShape,
getHintedGlphyShape, resolveGlyphShape, hintingPpem, getGlyphCache - and three
of them said "hint".  getHintedGlphyShape in particular read as getGlphyShape
plus a word, though one is the public FontFile API and the other a protected
hook, and GlyphCache had a getPathForCharacterCode overload whose two arities
consulted different maps and silently fell through to one another.

"Hinting" now names only the feature that is switched on and off - FontBox's
isHintingEnabled(), the -Dorg.apache.fontbox.ttf.hinting flag, the bytecode a
font does or does not carry.  The code paths are named for what they do:

  getHintedGlphyShape(char, int)          -> getGridFitGlyphShape(char, int)
  hintingPpem(AffineTransform)            -> gridFitPpem(AffineTransform)
  GlyphCache.getPathForCharacterCode(char, int)
                                          -> getGridFitPathForCharacterCode(char, int)
  GlyphCache.hintedCache                  -> gridFitCache

and "unhinted outline" becomes "plain outline" throughout, since it is just the
outline the font ships, not a variant.  Javadoc and comments follow.  The new
names spell Glyph correctly; getGlphyShape keeps its typo as it is existing
FontFile API, as does the unrelated FontFile.isHinted().

Rename and comments only, no behaviour change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cw2YK2dUcKQuFdzJ26gwQV
Every core test failed under maven with 95 errors, all rooted in

  NoClassDefFoundError: org/apache/logging/log4j/LogManager
    at org.apache.fontbox.ttf.TrueTypeFont.<clinit>

The trailing "Could not initialize class org.apache.fontbox.cff.CFFParser /
PfbParser" errors are the same fault seen later: a class whose static
initialiser throws stays permanently unusable, so the first failure poisons
every font parser after it.

module-info declared `requires org.apache.commons.logging`, which was right for
fontbox 3.x.  The 4.x line switched to log4j-api under PDFBOX-5695 (commit
1f95a1f876 on pdfbox trunk, never backported to the 3.0 branch), so the module
could not read the log4j classes it now needs.  log4j-api is already on the
compile classpath transitively via fontbox; only the module graph was missing it.

Gradle never caught this because it compiles module-info but runs tests on the
plain classpath, where JPMS readability does not apply.  Maven's surefire puts a
module with a module-info on the module path, where it does.  `mvn install` is
the stricter check of the two on this repo.

`mvn install` with tests now passes the full reactor, core-awt 256 tests / 0
failures / 0 errors.

Branch-local, like the FONT_BOX_VERSION bump: on main with stock fontbox 3.0.6
log4j-api is not on the module path at all, so this requires clause would fail
to resolve.  Must not be merged back without the fork.

Note that `requires org.apache.commons.logging` is now dead here - nothing in
core or viewer imports it, that clause was its only reference.  Left in place
since the pom still declares commons-logging so it resolves harmlessly; removing
it and the pom dependency is a separate cleanup.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cw2YK2dUcKQuFdzJ26gwQV
`./gradlew :viewer:viewer-awt:run -Dorg.apache.fontbox.ttf.hinting=true` started
the viewer with hinting off and no warning.  The run task forks a JVM that does
not inherit the daemon's system properties, and forwardProperties() bridges only
an allowlist of prefixes - which did not include the fontbox one, so the flag was
silently dropped.

Added 'org.apache.fontbox.' to the allowlist.  The whole prefix rather than the
single hinting property, matching how the other five entries are written, so any
future fontbox flag is covered too.  Comment column realigned to the new longest
prefix.

./gradle.sh with the same flag already worked - it re-exports -D args as
JAVA_TOOL_OPTIONS, which every forked JVM honours regardless of the allowlist -
and remains the fallback for anything not listed here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cw2YK2dUcKQuFdzJ26gwQV
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