[jira] [Created] (FOP-3330) MultiByteFont retains a java.awt.Rectangle per glyph for bounding boxes

"Jason Harrop (Jira)" <[email protected]> Thu, 30 Jul 2026 21:33:00 +0000 (UTC)
Newsgroups gmane.text.xml.fop.devel
Message-ID <[email protected]>
Jason Harrop created FOP-3330:
---------------------------------

             Summary: MultiByteFont retains a java.awt.Rectangle per glyph =
for bounding boxes
                 Key: FOP-3330
                 URL: https://issues.apache.org/jira/browse/FOP-3330
             Project: FOP
          Issue Type: Improvement
          Components: font/unqualified
    Affects Versions: 2.11
            Reporter: Jason Harrop
         Attachments: fop-packed-bboxes.patch

`MultiByteFont` holds its glyph bounding boxes as `Rectangle[]`, one object=
 per
glyph, populated eagerly by `OFFontLoader` for every CID font that is loade=
d:

multiFont.setBBoxArray(otf.getBoundingBoxes());

A `Rectangle` costs 32 bytes (with compressed oops) plus 4 bytes for the ar=
ray
slot, to carry four ints that need 16. Fonts are commonly 5,000+ glyphs, so=
 this
is ~180 KB per loaded font, retained for as long as the font is; a 65,000-g=
lyph
CJK font costs ~2.3 MB.

Measured in a long-lived JVM that loads the metrics of every installed font
(1246 fonts, 6.9M glyphs, via a full-GC live-object histogram):
|=C2=A0|live set|
|=E2=80=94|=E2=80=94|
|6,944,575 java.awt.Rectangle|222 MB|
|1,246 Rectangle[]|28 MB|
|everything else in the loaded fonts|~40 MB|

Packing the same values into an `int[]` (x, y, width, height per glyph) tak=
es
that 250 MB to 111 MB, and 6.1M live objects to 594k.

Nothing reads the stored `Rectangle`s directly: `getBoundingBox(glyphIndex,=
 size)`
already allocates a fresh, scaled `Rectangle` for its caller, and it is the=
 only
reader of the field. So the change is invisible to callers
(`FOPGVTGlyphVector`, `AbstractIFPainter`, the Java2D font mappers) =E2=80=
=94 the values
are unchanged, only the storage.

Worth noting how little of this is usually read: for plain PDF output the a=
rray
is written at load and never touched.

*{*}Proposed patch{*}* (attached / PR):

1. `MultiByteFont.boundingBoxes` becomes `int[]`, packed 4 ints per glyph;
`getBoundingBox(int, int)` reads it directly.
2. `setBBoxArray(Rectangle[])` is retained =E2=80=94 it is public API =E2=
=80=94 and converts;
a `setBBoxArray(int[])` overload is added.
3. `OpenFont.getBoundingBoxesPacked()` returns the packed form, so
`OFFontLoader` no longer builds a `Rectangle[]` at load time either.
`getBoundingBoxes()` stays, still used by `copyGlyphMetricsSingleByte`.

Same arithmetic in both paths, so the values are bit-identical.

If a further reduction is wanted, the values are in 1/1000 em after
`convertTTFUnit2PDFUnit`, so a `short[]` would halve it again (55 MB in the
measurement above)...



--
This message was sent by Atlassian Jira
(v8.20.10#820010)