From 31d6e80534f70091293248dc31c23ac871a55934 Mon Sep 17 00:00:00 2001 From: Patrick Corless Date: Tue, 8 Sep 2026 20:51:57 -0600 Subject: [PATCH 1/8] PDFBOX-3293: parse the TrueType hinting tables (cvt, fpgm, prep, gasp) Adds the four tables a bytecode interpreter needs, as ordinary TTFTable implementations registered with TTFParser and exposed from TrueTypeFont: cvt ControlValueTable control values in font units fpgm FontProgramTable the font program, run once per font prep ControlValueProgramTable the control value program, run per size gasp GaspTable per-ppem grid-fitting and smoothing flags Parsing only - nothing executes these yet, and no existing behaviour changes. GaspTable resolves the flags for a ppem the way the specification describes: the first range whose upper limit is at or above the requested size, with the implied final 0xFFFF range. Expected values in HintingTablesTest were read from LiberationSans with ttx, so the test pins the parse against an independent tool rather than against itself. Co-Authored-By: Claude Opus 5 --- .../fontbox/ttf/ControlValueProgramTable.java | 65 ++++++++ .../apache/fontbox/ttf/ControlValueTable.java | 82 ++++++++++ .../apache/fontbox/ttf/FontProgramTable.java | 64 ++++++++ .../org/apache/fontbox/ttf/GaspTable.java | 147 ++++++++++++++++++ .../org/apache/fontbox/ttf/TTFParser.java | 12 ++ .../org/apache/fontbox/ttf/TrueTypeFont.java | 44 ++++++ .../apache/fontbox/ttf/HintingTablesTest.java | 125 +++++++++++++++ 7 files changed, 539 insertions(+) create mode 100644 fontbox/src/main/java/org/apache/fontbox/ttf/ControlValueProgramTable.java create mode 100644 fontbox/src/main/java/org/apache/fontbox/ttf/ControlValueTable.java create mode 100644 fontbox/src/main/java/org/apache/fontbox/ttf/FontProgramTable.java create mode 100644 fontbox/src/main/java/org/apache/fontbox/ttf/GaspTable.java create mode 100644 fontbox/src/test/java/org/apache/fontbox/ttf/HintingTablesTest.java diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/ControlValueProgramTable.java b/fontbox/src/main/java/org/apache/fontbox/ttf/ControlValueProgramTable.java new file mode 100644 index 00000000000..e5c3ae48e03 --- /dev/null +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/ControlValueProgramTable.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import java.io.IOException; + +/** + * The 'prep' (Control Value Program, also known as the pre-program) table. It holds TrueType hinting + * bytecode that is executed whenever the point size or transform changes, to prepare the Control Value + * Table and graphics state for the new ppem. The bytecode is stored here as raw bytes and run by the + * interpreter. + * + * @author Apache PDFBox + */ +public class ControlValueProgramTable extends TTFTable +{ + /** + * A tag that identifies this table type. + */ + public static final String TAG = "prep"; + + private byte[] program; + + ControlValueProgramTable() + { + } + + /** + * This will read the required data from the stream. + * + * @param ttf The font that is being read. + * @param data The stream to read the data from. + * @throws IOException If there is an error reading the data. + */ + @Override + void read(TrueTypeFont ttf, TTFDataStream data) throws IOException + { + program = data.read((int) getLength()); + initialized = true; + } + + /** + * Returns the raw control value program bytecode. + * + * @return the bytecode of the pre-program + */ + public byte[] getProgram() + { + return program; + } +} \ No newline at end of file diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/ControlValueTable.java b/fontbox/src/main/java/org/apache/fontbox/ttf/ControlValueTable.java new file mode 100644 index 00000000000..6d87318ac7a --- /dev/null +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/ControlValueTable.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import java.io.IOException; + +/** + * The 'cvt ' (Control Value) table. It holds an array of reference values - stem widths, heights and + * similar control measurements - used by the TrueType hinting bytecode. The values are stored here in + * raw font units (signed FWords); they are scaled to the active ppem by the interpreter, not at parse + * time. + * + * @author Apache PDFBox + */ +public class ControlValueTable extends TTFTable +{ + /** + * A tag that identifies this table type. + */ + public static final String TAG = "cvt "; + + private int[] values; + + ControlValueTable() + { + } + + /** + * This will read the required data from the stream. + * + * @param ttf The font that is being read. + * @param data The stream to read the data from. + * @throws IOException If there is an error reading the data. + */ + @Override + void read(TrueTypeFont ttf, TTFDataStream data) throws IOException + { + int count = (int) (getLength() / 2); + int[] cvt = new int[count]; + for (int i = 0; i < count; i++) + { + cvt[i] = data.readSignedShort(); + } + values = cvt; + initialized = true; + } + + /** + * Returns the raw control values in font units (FWords). The interpreter scales these to the + * active ppem. + * + * @return the control values in font units + */ + public int[] getValues() + { + return values; + } + + /** + * Returns the number of control values in this table. + * + * @return the entry count + */ + public int getValueCount() + { + return values != null ? values.length : 0; + } +} \ No newline at end of file diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/FontProgramTable.java b/fontbox/src/main/java/org/apache/fontbox/ttf/FontProgramTable.java new file mode 100644 index 00000000000..2e3e3d7d349 --- /dev/null +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/FontProgramTable.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import java.io.IOException; + +/** + * The 'fpgm' (Font Program) table. It holds TrueType hinting bytecode - typically a set of function + * definitions (FDEF) - that is executed once when the font is first used, before any glyph is hinted. + * The bytecode is stored here as raw bytes and run by the interpreter. + * + * @author Apache PDFBox + */ +public class FontProgramTable extends TTFTable +{ + /** + * A tag that identifies this table type. + */ + public static final String TAG = "fpgm"; + + private byte[] program; + + FontProgramTable() + { + } + + /** + * This will read the required data from the stream. + * + * @param ttf The font that is being read. + * @param data The stream to read the data from. + * @throws IOException If there is an error reading the data. + */ + @Override + void read(TrueTypeFont ttf, TTFDataStream data) throws IOException + { + program = data.read((int) getLength()); + initialized = true; + } + + /** + * Returns the raw font program bytecode. + * + * @return the bytecode of the font program + */ + public byte[] getProgram() + { + return program; + } +} \ No newline at end of file diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/GaspTable.java b/fontbox/src/main/java/org/apache/fontbox/ttf/GaspTable.java new file mode 100644 index 00000000000..baf354e97b8 --- /dev/null +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/GaspTable.java @@ -0,0 +1,147 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import java.io.IOException; + +/** + * The 'gasp' (Grid-fitting And Scan-conversion Procedure) table. It maps ppem ranges to flags that + * advise whether grid-fitting (hinting) and/or grayscale anti-aliasing should be applied at that size. + * The ranges are sorted by ascending maximum ppem; the last range always ends at 0xFFFF. + * + * @author Apache PDFBox + */ +public class GaspTable extends TTFTable +{ + /** + * A tag that identifies this table type. + */ + public static final String TAG = "gasp"; + + /** + * Use grid-fitting (i.e. execute the hinting bytecode) at this size. + */ + public static final int GASP_GRIDFIT = 0x0001; + + /** + * Use grayscale (anti-aliased) rendering at this size. + */ + public static final int GASP_DOGRAY = 0x0002; + + /** + * Use grid-fitting with ClearType symmetric smoothing (gasp version 1). + */ + public static final int GASP_SYMMETRIC_GRIDFIT = 0x0004; + + /** + * Use smoothing along multiple axes with ClearType (gasp version 1). + */ + public static final int GASP_SYMMETRIC_SMOOTHING = 0x0008; + + private int version; + private int[] rangeMaxPPEM; + private int[] rangeFlags; + + GaspTable() + { + } + + /** + * This will read the required data from the stream. + * + * @param ttf The font that is being read. + * @param data The stream to read the data from. + * @throws IOException If there is an error reading the data. + */ + @Override + void read(TrueTypeFont ttf, TTFDataStream data) throws IOException + { + version = data.readUnsignedShort(); + int numRanges = data.readUnsignedShort(); + rangeMaxPPEM = new int[numRanges]; + rangeFlags = new int[numRanges]; + for (int i = 0; i < numRanges; i++) + { + rangeMaxPPEM[i] = data.readUnsignedShort(); + rangeFlags[i] = data.readUnsignedShort(); + } + initialized = true; + } + + /** + * @return the table version (0 or 1) + */ + public int getVersion() + { + return version; + } + + /** + * Returns the upper ppem bound of each range, in ascending order. The last entry is 0xFFFF. + * + * @return the per-range maximum ppem values + */ + public int[] getRangeMaxPPEM() + { + return rangeMaxPPEM; + } + + /** + * Returns the flags for each range, parallel to {@link #getRangeMaxPPEM()}. + * + * @return the per-range flags + */ + public int[] getRangeFlags() + { + return rangeFlags; + } + + /** + * Returns the behavior flags that apply at the given ppem - those of the first range whose + * maximum ppem is greater than or equal to the requested ppem. + * + * @param ppem the pixels-per-em to look up + * @return the flags for that ppem, or 0 if the table has no ranges + */ + public int getFlags(int ppem) + { + if (rangeMaxPPEM == null) + { + return 0; + } + for (int i = 0; i < rangeMaxPPEM.length; i++) + { + if (ppem <= rangeMaxPPEM[i]) + { + return rangeFlags[i]; + } + } + // beyond the last range (should not happen as the last bound is 0xFFFF) + return rangeFlags.length > 0 ? rangeFlags[rangeFlags.length - 1] : 0; + } + + /** + * Convenience test for whether grid-fitting (hinting) is advised at the given ppem. + * + * @param ppem the pixels-per-em to look up + * @return true if {@link #GASP_GRIDFIT} is set for that ppem + */ + public boolean isGridFit(int ppem) + { + return (getFlags(ppem) & GASP_GRIDFIT) != 0; + } +} \ No newline at end of file diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/TTFParser.java b/fontbox/src/main/java/org/apache/fontbox/ttf/TTFParser.java index 69a5a7e2c10..5a8de6d5fff 100644 --- a/fontbox/src/main/java/org/apache/fontbox/ttf/TTFParser.java +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/TTFParser.java @@ -377,6 +377,18 @@ private TTFTable readTableDirectory(TTFDataStream raf) throws IOException case GlyphSubstitutionTable.TAG: table = new GlyphSubstitutionTable(); break; + case ControlValueTable.TAG: + table = new ControlValueTable(); + break; + case FontProgramTable.TAG: + table = new FontProgramTable(); + break; + case ControlValueProgramTable.TAG: + table = new ControlValueProgramTable(); + break; + case GaspTable.TAG: + table = new GaspTable(); + break; default: table = readTable(tag); break; diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java b/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java index 90ecd9ece29..02f431f14d8 100644 --- a/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java @@ -369,6 +369,50 @@ public GlyphSubstitutionTable getGsub() throws IOException return (GlyphSubstitutionTable) getTable(GlyphSubstitutionTable.TAG); } + /** + * Get the "cvt " (Control Value) table for this TTF. + * + * @return The "cvt " table or null if it doesn't exist. + * @throws IOException if there was an error reading the table. + */ + public ControlValueTable getControlValues() throws IOException + { + return (ControlValueTable) getTable(ControlValueTable.TAG); + } + + /** + * Get the "fpgm" (Font Program) table for this TTF. + * + * @return The "fpgm" table or null if it doesn't exist. + * @throws IOException if there was an error reading the table. + */ + public FontProgramTable getFontProgram() throws IOException + { + return (FontProgramTable) getTable(FontProgramTable.TAG); + } + + /** + * Get the "prep" (Control Value Program) table for this TTF. + * + * @return The "prep" table or null if it doesn't exist. + * @throws IOException if there was an error reading the table. + */ + public ControlValueProgramTable getControlValueProgram() throws IOException + { + return (ControlValueProgramTable) getTable(ControlValueProgramTable.TAG); + } + + /** + * Get the "gasp" (Grid-fitting And Scan-conversion Procedure) table for this TTF. + * + * @return The "gasp" table or null if it doesn't exist. + * @throws IOException if there was an error reading the table. + */ + public GaspTable getGasp() throws IOException + { + return (GaspTable) getTable(GaspTable.TAG); + } + /** * Get the data of the TrueType Font * program representing the stream used to build this diff --git a/fontbox/src/test/java/org/apache/fontbox/ttf/HintingTablesTest.java b/fontbox/src/test/java/org/apache/fontbox/ttf/HintingTablesTest.java new file mode 100644 index 00000000000..c3cad84ceea --- /dev/null +++ b/fontbox/src/test/java/org/apache/fontbox/ttf/HintingTablesTest.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.pdfbox.io.RandomAccessReadBuffer; +import org.junit.jupiter.api.Test; + +/** + * Tests parsing of the TrueType hinting tables ('cvt ', 'fpgm', 'prep', 'gasp'). + * + * LiberationSans-Regular was built as a hand-hinted, metric-compatible Arial replacement, so it + * carries a full bytecode hinting program and all four tables. The expected values below were taken + * from its on-disk table directory. + */ +class HintingTablesTest +{ + private static TrueTypeFont parse(String resource) throws IOException + { + try (InputStream is = HintingTablesTest.class.getResourceAsStream(resource)) + { + assertNotNull(is, "missing test resource " + resource); + return new TTFParser().parse(new RandomAccessReadBuffer(is)); + } + } + + @Test + void testControlValueTable() throws IOException + { + TrueTypeFont font = parse("/ttf/LiberationSans-Regular.ttf"); + ControlValueTable cvt = font.getControlValues(); + assertNotNull(cvt); + // 648-byte table / 2 bytes per FWord + assertEquals(324, cvt.getValueCount()); + assertEquals(324, cvt.getValues().length); + // CVT entries are signed FWords (raw font units, not yet scaled to ppem) + assertEquals(1484, cvt.getValues()[0]); + } + + @Test + void testFontProgramTable() throws IOException + { + TrueTypeFont font = parse("/ttf/LiberationSans-Regular.ttf"); + FontProgramTable fpgm = font.getFontProgram(); + assertNotNull(fpgm); + assertEquals(1972, fpgm.getProgram().length); + } + + @Test + void testControlValueProgramTable() throws IOException + { + TrueTypeFont font = parse("/ttf/LiberationSans-Regular.ttf"); + ControlValueProgramTable prep = font.getControlValueProgram(); + assertNotNull(prep); + assertEquals(835, prep.getProgram().length); + } + + @Test + void testGaspTable() throws IOException + { + TrueTypeFont font = parse("/ttf/LiberationSans-Regular.ttf"); + GaspTable gasp = font.getGasp(); + assertNotNull(gasp); + assertEquals(0, gasp.getVersion()); + + // three ranges: (<=8: DOGRAY), (<=17: GRIDFIT), (<=65535: GRIDFIT|DOGRAY) + assertArrayEquals(new int[] { 8, 17, 65535 }, gasp.getRangeMaxPPEM()); + assertArrayEquals(new int[] { GaspTable.GASP_DOGRAY, GaspTable.GASP_GRIDFIT, + GaspTable.GASP_GRIDFIT | GaspTable.GASP_DOGRAY }, gasp.getRangeFlags()); + + // ppem -> flags lookup, including range boundaries + assertEquals(GaspTable.GASP_DOGRAY, gasp.getFlags(8)); + assertEquals(GaspTable.GASP_GRIDFIT, gasp.getFlags(9)); + assertEquals(GaspTable.GASP_GRIDFIT, gasp.getFlags(17)); + assertEquals(GaspTable.GASP_GRIDFIT | GaspTable.GASP_DOGRAY, gasp.getFlags(18)); + assertEquals(GaspTable.GASP_GRIDFIT | GaspTable.GASP_DOGRAY, gasp.getFlags(2000)); + + // grid-fitting is off at 8 ppem and below, on above it + assertFalse(gasp.isGridFit(8)); + assertTrue(gasp.isGridFit(9)); + assertTrue(gasp.isGridFit(16)); + } + + @Test + void testAbsentTablesReturnNull() throws IOException + { + // None of the bundled fonts lacks all four tables, but the absent-table path is covered: + // JosefinSans-Italic has no 'cvt '/'fpgm' (but does have 'prep'/'gasp')... + TrueTypeFont josefin = parse("/ttf/JosefinSans-Italic.ttf"); + assertNull(josefin.getControlValues()); + assertNull(josefin.getFontProgram()); + assertNotNull(josefin.getControlValueProgram()); + assertNotNull(josefin.getGasp()); + + // ...and Lohit-Tamil has no 'gasp'. Absent accessors must return null, not throw. + TrueTypeFont lohit = parse("/ttf/Lohit-Tamil.ttf"); + assertNull(lohit.getGasp()); + assertNotNull(lohit.getControlValues()); + assertNotNull(lohit.getFontProgram()); + assertNotNull(lohit.getControlValueProgram()); + } +} \ No newline at end of file From f36ec93112735a0f0445e752d76c52e620183c3c Mon Sep 17 00:00:00 2001 From: Patrick Corless Date: Tue, 8 Sep 2026 20:52:37 -0600 Subject: [PATCH 2/8] PDFBOX-3293: TrueType bytecode interpreter The virtual machine that executes TrueType hinting programs, package-private to org.apache.fontbox.ttf: nothing outside the package can reach it, and it is not exported from the OSGi bundle. TrueTypeInterpreter the driver and the 256-entry dispatch table ExecutionContext per-run state: operand stack, zones, call depth GraphicsState vectors, reference and zone pointers, round state Zone a set of points with current/original/unscaled coordinates and per-axis touch flags BytecodeStream a bounds-checked cursor over a program Fixed F26Dot6 and F2Dot14 integer math UnitVector the projection, freedom and dual-projection vectors FunctionDef an FDEF entry point HintingException any failure; callers fall back to the raw outline ExecutionTracer optional per-instruction trace, off in normal use All 173 opcodes are implemented. The 32-variant MDRP and MIRP families and the other flag-encoded groups decode their flags from the low bits of the opcode rather than being enumerated, so roughly a hundred dispatch slots are served by a dozen handlers. Everything stays in integer fixed point, matching FreeType, so output can be compared against it exactly rather than approximately. Fixed reproduces FT_MulDiv including its behaviour when the divisor is zero, and DIV truncates rather than rounds as FT_MulDiv_No_Round does. Two bounds keep a crafted font from running forever, both sized as FreeType sizes them in TT_RunIns: backward jumps and cumulative LOOPCALL iterations. A backward jump and LOOPCALL are the only ways TrueType bytecode can loop, so bounding them bounds the program; a four-byte glyph program otherwise spins indefinitely. CALL nesting is capped at 64 as FreeType does. The storage area and twilight zone belong to the size rather than to one program run, as in FreeType's TT_Size: a font may compute values into them in prep and read them back from every glyph program. Both are cleared when the ppem changes, before prep runs, as tt_size_run_prep does. Unit tests cover the opcodes individually, the rounding state machine, the fixed-point math, the bytecode cursor, and graphics state defaults, deep copy and per-glyph reset. Byte-exact agreement with FreeType is established separately by the golden comparison. Co-Authored-By: Claude Opus 5 --- .../apache/fontbox/ttf/BytecodeStream.java | 140 ++ .../apache/fontbox/ttf/ExecutionContext.java | 504 +++++ .../apache/fontbox/ttf/ExecutionTracer.java | 269 +++ .../java/org/apache/fontbox/ttf/Fixed.java | 210 +++ .../org/apache/fontbox/ttf/FunctionDef.java | 52 + .../org/apache/fontbox/ttf/GraphicsState.java | 530 ++++++ .../apache/fontbox/ttf/HintingException.java | 46 + .../fontbox/ttf/TrueTypeInterpreter.java | 1649 +++++++++++++++++ .../org/apache/fontbox/ttf/UnitVector.java | 151 ++ .../java/org/apache/fontbox/ttf/Zone.java | 150 ++ .../fontbox/ttf/BytecodeStreamTest.java | 78 + .../org/apache/fontbox/ttf/FixedTest.java | 81 + .../apache/fontbox/ttf/GraphicsStateTest.java | 104 ++ .../org/apache/fontbox/ttf/PointOpsTest.java | 343 ++++ .../apache/fontbox/ttf/RoundStateTest.java | 85 + .../fontbox/ttf/TrueTypeInterpreterTest.java | 352 ++++ 16 files changed, 4744 insertions(+) create mode 100644 fontbox/src/main/java/org/apache/fontbox/ttf/BytecodeStream.java create mode 100644 fontbox/src/main/java/org/apache/fontbox/ttf/ExecutionContext.java create mode 100644 fontbox/src/main/java/org/apache/fontbox/ttf/ExecutionTracer.java create mode 100644 fontbox/src/main/java/org/apache/fontbox/ttf/Fixed.java create mode 100644 fontbox/src/main/java/org/apache/fontbox/ttf/FunctionDef.java create mode 100644 fontbox/src/main/java/org/apache/fontbox/ttf/GraphicsState.java create mode 100644 fontbox/src/main/java/org/apache/fontbox/ttf/HintingException.java create mode 100644 fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeInterpreter.java create mode 100644 fontbox/src/main/java/org/apache/fontbox/ttf/UnitVector.java create mode 100644 fontbox/src/main/java/org/apache/fontbox/ttf/Zone.java create mode 100644 fontbox/src/test/java/org/apache/fontbox/ttf/BytecodeStreamTest.java create mode 100644 fontbox/src/test/java/org/apache/fontbox/ttf/FixedTest.java create mode 100644 fontbox/src/test/java/org/apache/fontbox/ttf/GraphicsStateTest.java create mode 100644 fontbox/src/test/java/org/apache/fontbox/ttf/PointOpsTest.java create mode 100644 fontbox/src/test/java/org/apache/fontbox/ttf/RoundStateTest.java create mode 100644 fontbox/src/test/java/org/apache/fontbox/ttf/TrueTypeInterpreterTest.java diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/BytecodeStream.java b/fontbox/src/main/java/org/apache/fontbox/ttf/BytecodeStream.java new file mode 100644 index 00000000000..80c740f4bf1 --- /dev/null +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/BytecodeStream.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +/** + * A bounds-checked cursor over a TrueType bytecode program (fpgm, prep, a glyph instruction stream, or + * a function body). It owns the program counter so individual opcode handlers never index the array by + * hand - this removes a whole class of off-by-one and overrun bugs and is trivially testable on its + * own. Reads past the end throw {@link HintingException} so the per-glyph fallback can catch cleanly. + * + * @author Apache PDFBox + */ +class BytecodeStream +{ + private final byte[] code; + private int ip; + private int instructionStart; + + /** + * @param code the bytecode program; not copied + */ + public BytecodeStream(byte[] code) + { + this.code = code != null ? code : new byte[0]; + } + + /** + * @return the underlying bytecode array (not copied); used to record function entry points + */ + public byte[] getCode() + { + return code; + } + + /** + * @return true if there is at least one more byte to read + */ + public boolean hasNext() + { + return ip < code.length; + } + + /** + * @return the current program-counter position + */ + public int position() + { + return ip; + } + + /** + * Records the current position as the start of the instruction about to be read. Relative jumps + * ({@code JMPR}/{@code JROT}/{@code JROF}) are measured from here. + */ + public void markInstructionStart() + { + instructionStart = ip; + } + + /** + * @return the position recorded by the most recent {@link #markInstructionStart()} + */ + public int instructionStart() + { + return instructionStart; + } + + /** + * Moves the program counter to an absolute position. + * + * @param position the new position, within {@code [0, length]} + * @throws HintingException if the position is out of range + */ + public void seek(int position) + { + if (position < 0 || position > code.length) + { + throw new HintingException( + "bytecode seek out of range: " + position + " of " + code.length); + } + ip = position; + } + + /** + * Advances the program counter by a relative amount (may be negative). + * + * @param delta the number of bytes to skip + * @throws HintingException if the result is out of range + */ + public void skip(int delta) + { + seek(ip + delta); + } + + /** + * Reads the next byte as an unsigned 0-255 value (an opcode, or push operand). + * + * @return the next unsigned byte + * @throws HintingException if the stream is exhausted + */ + public int nextByte() + { + if (ip >= code.length) + { + throw new HintingException("bytecode read past end at " + ip); + } + return code[ip++] & 0xFF; + } + + /** + * Reads the next two bytes as a signed big-endian 16-bit word. + * + * @return the next signed word + * @throws HintingException if fewer than two bytes remain + */ + public int nextWord() + { + if (ip + 1 >= code.length) + { + throw new HintingException("bytecode word read past end at " + ip); + } + int hi = code[ip++] & 0xFF; + int lo = code[ip++] & 0xFF; + return (short) ((hi << 8) | lo); + } +} diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/ExecutionContext.java b/fontbox/src/main/java/org/apache/fontbox/ttf/ExecutionContext.java new file mode 100644 index 00000000000..63f6c8bd5f4 --- /dev/null +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/ExecutionContext.java @@ -0,0 +1,504 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +/** + * All mutable state for a single run of the interpreter, bundled into one object so opcode handlers + * share a uniform {@code execute(ExecutionContext)} signature and new state can be added without + * touching every handler. It holds the operand stack, storage area, scaled control values, the two + * point zones, the {@link GraphicsState}, the current {@link BytecodeStream}, and the ppem the program + * is running at. + *

+ * Not all of that state is per-run. The storage area and the twilight zone are owned by the + * {@link TrueTypeInterpreter} and handed to every context at one size, because a font may compute values + * into them in {@code prep} and read them back from each glyph program - FreeType keeps both on the + * {@code TT_Size} for the same reason. + * + * @author Apache PDFBox + */ +class ExecutionContext +{ + private final TrueTypeInterpreter interpreter; + private final GraphicsState graphicsState; + + private final int[] stack; + private int stackPointer; + + // owned by the interpreter and shared by every context at one size, so prep can seed them + private final int[] storage; + private final Zone twilightZone; + + private final int[] controlValues; + private Zone glyphZone; + + private int ppem; + private int pointSize; + private int unitsPerEm; + + private BytecodeStream stream; + private int callDepth; + private boolean returnFromFunction; + + // Execution budget. TrueType can only loop through a backward jump or a LOOPCALL, so bounding + // those two bounds the whole program - without them a four-byte glyph program can spin forever. + // Both counters are per-context, and one context is one top-level program run, so they need no + // reset. After FreeType's neg_jump_counter / loopcall_counter in TT_RunIns. + private long negativeJumpCounter; + private long loopCallCounter; + private int executionBudget = -1; + + // v40 "backward compatibility" (grayscale subpixel) state. When set, point moves in the x + // direction are suppressed so stems are not grid-fit and darkened under antialiasing, and y moves + // are frozen once IUP has run on both axes. Only enabled for the glyph program, never fpgm/prep. + private boolean backwardCompatibility; + private boolean iupxCalled; + private boolean iupyCalled; + private boolean composite; + + /** + * @param interpreter the owning interpreter (for function calls) + * @param graphicsState the graphics state this run starts from + * @param maxStackElements operand stack capacity + * @param storage the interpreter's storage area, shared across the runs at one size + * @param controlValues the scaled control values (F26Dot6), or null + * @param twilightZone the interpreter's twilight zone, shared across the runs at one size + */ + public ExecutionContext(TrueTypeInterpreter interpreter, GraphicsState graphicsState, + int maxStackElements, int[] storage, int[] controlValues, Zone twilightZone) + { + this.interpreter = interpreter; + this.graphicsState = graphicsState; + this.stack = new int[Math.max(maxStackElements, 1)]; + this.storage = storage; + this.controlValues = controlValues != null ? controlValues : new int[0]; + this.twilightZone = twilightZone; + } + + /** @return the owning interpreter */ + public TrueTypeInterpreter getInterpreter() + { + return interpreter; + } + + /** @return the graphics state */ + public GraphicsState getGraphicsState() + { + return graphicsState; + } + + // --- operand stack --------------------------------------------------- + + /** + * Pushes a value onto the operand stack. + * + * @param value the value to push + * @throws HintingException on stack overflow + */ + public void push(int value) + { + if (stackPointer >= stack.length) + { + throw new HintingException("interpreter stack overflow at " + stackPointer); + } + stack[stackPointer++] = value; + } + + /** + * Pops a value from the operand stack. + * + * @return the popped value + * @throws HintingException on stack underflow + */ + public int pop() + { + if (stackPointer <= 0) + { + throw new HintingException("interpreter stack underflow"); + } + return stack[--stackPointer]; + } + + /** + * Returns the value {@code n} positions below the top without removing it ({@code peek(0)} is the + * top of stack). + * + * @param n depth below the top + * @return the value at that depth + * @throws HintingException if the depth is out of range + */ + public int peek(int n) + { + int index = stackPointer - 1 - n; + if (index < 0 || index >= stackPointer) + { + throw new HintingException("interpreter stack peek out of range: " + n); + } + return stack[index]; + } + + /** @return the current stack depth */ + public int getStackDepth() + { + return stackPointer; + } + + /** Empties the operand stack. */ + public void clearStack() + { + stackPointer = 0; + } + + // --- execution budget ------------------------------------------------- + + /** + * The maximum number of backward jumps, and separately of {@code LOOPCALL} iterations, this run may + * make before it is abandoned. Sized from the glyph's point count and the control value count the + * way FreeType sizes its counters, so a legitimately loop-heavy program still completes while a + * crafted one cannot run forever. Computed on first use, because the glyph zone is attached after + * the context is built. + * + * @return the per-run budget + */ + public int getExecutionBudget() + { + if (executionBudget < 0) + { + int points = glyphZone != null ? glyphZone.getPointCount() : 0; + executionBudget = Math.max(50, 10 * points) + Math.max(50, controlValues.length / 10); + } + return executionBudget; + } + + /** + * Records one backward jump, failing the run once {@link #getExecutionBudget()} is exhausted. + * + * @throws HintingException if too many backward jumps have been made + */ + public void countNegativeJump() + { + if (++negativeJumpCounter > getExecutionBudget()) + { + throw new HintingException( + "too many backward jumps, limit is " + getExecutionBudget()); + } + } + + /** + * Adds {@code count} iterations to the {@code LOOPCALL} budget, failing before the loop is entered + * rather than partway through it. The budget is cumulative across the run, so a program cannot slip + * past it by issuing many small loops. + * + * @param count the number of iterations about to be run, always positive + * @throws HintingException if the budget is exhausted + */ + public void countLoopCalls(int count) + { + loopCallCounter += count; + if (loopCallCounter > getExecutionBudget()) + { + throw new HintingException("LOOPCALL runs too long, limit is " + getExecutionBudget() + + " iterations, asked for " + loopCallCounter); + } + } + + // --- storage and control values -------------------------------------- + + /** @return the storage area, shared with every other run at this size */ + public int[] getStorage() + { + return storage; + } + + /** @return the scaled control values in F26Dot6 */ + public int[] getControlValues() + { + return controlValues; + } + + // --- zones ----------------------------------------------------------- + + /** @return the twilight zone (zone 0), shared with every other run at this size */ + public Zone getTwilightZone() + { + return twilightZone; + } + + /** @return the glyph zone (zone 1), or null if no glyph is loaded */ + public Zone getGlyphZone() + { + return glyphZone; + } + + /** @param zone the glyph zone (zone 1) */ + public void setGlyphZone(Zone zone) + { + this.glyphZone = zone; + } + + /** + * Resolves a zone pointer (0 = twilight, 1 = glyph) to its {@link Zone}. + * + * @param zonePointer the zone pointer value + * @return the corresponding zone + * @throws HintingException if the pointer is invalid or the glyph zone is unset + */ + public Zone getZone(int zonePointer) + { + if (zonePointer == 0) + { + return twilightZone; + } + if (zonePointer == 1) + { + if (glyphZone == null) + { + throw new HintingException("glyph zone referenced but not loaded"); + } + return glyphZone; + } + throw new HintingException("invalid zone pointer: " + zonePointer); + } + + // --- sizing ---------------------------------------------------------- + + /** @return the active pixels-per-em */ + public int getPpem() + { + return ppem; + } + + /** @param value the active pixels-per-em */ + public void setPpem(int value) + { + this.ppem = value; + } + + /** @return the point size */ + public int getPointSize() + { + return pointSize; + } + + /** @param value the point size */ + public void setPointSize(int value) + { + this.pointSize = value; + } + + /** @return the font's unitsPerEm */ + public int getUnitsPerEm() + { + return unitsPerEm; + } + + /** @param value the font's unitsPerEm */ + public void setUnitsPerEm(int value) + { + this.unitsPerEm = value; + } + + // --- projection / freedom vector math -------------------------------- + + /** + * Dot product of two F2Dot14 vectors (or a coordinate against an F2Dot14 vector), returning the + * result shifted back down by 14 bits with rounding. This is the projection primitive: projecting + * an F26Dot6 coordinate onto an F2Dot14 unit vector yields an F26Dot6 distance. + * + * @param ax first vector x + * @param ay first vector y + * @param bx second vector x (F2Dot14) + * @param by second vector y (F2Dot14) + * @return the rounded dot product + */ + public static int dot14(int ax, int ay, int bx, int by) + { + long product = (long) ax * bx + (long) ay * by; + return (int) ((product + 0x2000) >> 14); + } + + /** + * Projects a coordinate onto the projection vector. + * + * @param x the x coordinate in F26Dot6 + * @param y the y coordinate in F26Dot6 + * @return the projected distance in F26Dot6 + */ + public int project(int x, int y) + { + UnitVector pv = graphicsState.getProjectionVector(); + return dot14(x, y, pv.getX(), pv.getY()); + } + + /** + * Projects a coordinate onto the dual projection vector (used to measure original, unhinted + * positions). + * + * @param x the x coordinate in F26Dot6 + * @param y the y coordinate in F26Dot6 + * @return the projected distance in F26Dot6 + */ + public int dualProject(int x, int y) + { + UnitVector dv = graphicsState.getDualProjectionVector(); + return dot14(x, y, dv.getX(), dv.getY()); + } + + /** + * Returns the current (hinted) projected distance from point {@code p0} in {@code zone0} to point + * {@code p1} in {@code zone1}, measured along the projection vector. + */ + public int projectedDistance(Zone zone1, int p1, Zone zone0, int p0) + { + return project(zone1.getCurrentX()[p1] - zone0.getCurrentX()[p0], + zone1.getCurrentY()[p1] - zone0.getCurrentY()[p0]); + } + + /** + * Returns the original (unhinted) projected distance from point {@code p0} in {@code zone0} to + * point {@code p1} in {@code zone1}, measured along the dual projection vector. + */ + public int dualProjectedDistance(Zone zone1, int p1, Zone zone0, int p0) + { + return dualProject(zone1.getOriginalX()[p1] - zone0.getOriginalX()[p0], + zone1.getOriginalY()[p1] - zone0.getOriginalY()[p0]); + } + + /** + * Moves a point by the given projected distance along the freedom vector, touching the axes the + * freedom vector acts on. The displacement is {@code distance * freedom / (freedom . projection)}, + * which reduces to {@code distance} when both vectors are the same axis. + * + * @param zone the zone holding the point + * @param point the point index + * @param distance the projected distance to move, in F26Dot6 + */ + public void movePoint(Zone zone, int point, int distance) + { + UnitVector fv = graphicsState.getFreedomVector(); + UnitVector pv = graphicsState.getProjectionVector(); + int fDotP = dot14(fv.getX(), fv.getY(), pv.getX(), pv.getY()); + if (fDotP == 0) + { + fDotP = Fixed.ONE_F2DOT14; + } + if (fv.getX() != 0) + { + // backward-compatibility (v40 grayscale): never grid-fit in the x direction, so horizontal + // stems keep their natural sub-pixel position and are not darkened by antialiasing + if (!backwardCompatibility) + { + zone.getCurrentX()[point] += Fixed.mulDiv(distance, fv.getX(), fDotP); + } + zone.getTouchedX()[point] = true; + } + if (fv.getY() != 0) + { + // y moves are allowed until IUP has run on both axes; afterwards the glyph is frozen + if (!(backwardCompatibility && iupxCalled && iupyCalled)) + { + zone.getCurrentY()[point] += Fixed.mulDiv(distance, fv.getY(), fDotP); + } + zone.getTouchedY()[point] = true; + } + } + + /** @return whether v40 backward-compatibility (grayscale subpixel) movement rules are active */ + public boolean isBackwardCompatibility() + { + return backwardCompatibility; + } + + /** @param value whether to apply v40 backward-compatibility movement rules (glyph program only) */ + public void setBackwardCompatibility(boolean value) + { + this.backwardCompatibility = value; + } + + /** Marks IUP[x] as having run; resets each program run. */ + public void setIupxCalled() + { + this.iupxCalled = true; + } + + /** Marks IUP[y] as having run; resets each program run. */ + public void setIupyCalled() + { + this.iupyCalled = true; + } + + /** @return whether IUP has run on both axes (the glyph is frozen for backward compatibility) */ + public boolean isIupDone() + { + return iupxCalled && iupyCalled; + } + + /** @return whether the running program belongs to a composite glyph */ + public boolean isComposite() + { + return composite; + } + + /** @param value whether the running program belongs to a composite glyph */ + public void setComposite(boolean value) + { + this.composite = value; + } + + // --- execution cursor and call state --------------------------------- + + /** @return the current bytecode stream */ + public BytecodeStream getStream() + { + return stream; + } + + /** @param stream the current bytecode stream */ + public void setStream(BytecodeStream stream) + { + this.stream = stream; + } + + /** @return the current call nesting depth */ + public int getCallDepth() + { + return callDepth; + } + + /** Increments the call nesting depth. */ + public void enterCall() + { + callDepth++; + } + + /** Decrements the call nesting depth. */ + public void leaveCall() + { + callDepth--; + } + + /** @return true if an {@code ENDF} asked the current function body to return */ + public boolean isReturnFromFunction() + { + return returnFromFunction; + } + + /** @param value whether the current function body should return */ + public void setReturnFromFunction(boolean value) + { + this.returnFromFunction = value; + } +} diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/ExecutionTracer.java b/fontbox/src/main/java/org/apache/fontbox/ttf/ExecutionTracer.java new file mode 100644 index 00000000000..fc1b0c80cc0 --- /dev/null +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/ExecutionTracer.java @@ -0,0 +1,269 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import java.io.PrintStream; + +/** + * A first-class, toggleable execution tracer for the interpreter. When attached to a + * {@link TrueTypeInterpreter} it emits one line per executed instruction, just before the instruction + * runs, in a format deliberately close to FreeType's {@code FT2_DEBUG=ttinterp} output: + * + *

+ *   <pc>  <MNEMONIC>  # <top of stack, deepest..top>
+ * 
+ * + * The program counter and the operand stack are what matter for diffing: feeding the same glyph + * through this tracer and through FreeType and aligning the two traces by instruction index pinpoints + * the first instruction where control flow or an operand value diverges. See + * {@code src/test/resources/ttf/hinting/trace_diff.py} for the diff tool. + * + * @author Apache PDFBox + */ +class ExecutionTracer +{ + private static final String[] MNEMONICS = buildMnemonics(); + + /** Number of top-of-stack operands to print, matching FreeType's trace window. */ + private static final int STACK_WINDOW = 8; + + private final PrintStream out; + private final int tracePoint; + + /** + * @param out where to write trace lines + */ + public ExecutionTracer(PrintStream out) + { + this(out, -1); + } + + /** + * @param out where to write trace lines + * @param tracePoint a glyph-zone point index whose current coordinate is appended to each line + * (for localizing silent point-position divergence), or -1 to omit + */ + public ExecutionTracer(PrintStream out, int tracePoint) + { + this.out = out; + this.tracePoint = tracePoint; + } + + /** + * @param opcode an opcode value 0-255 + * @return the mnemonic for that opcode + */ + public static String mnemonic(int opcode) + { + return MNEMONICS[opcode & 0xFF]; + } + + /** + * Emits a trace line for the instruction about to execute. + * + * @param pc the program-counter position of the instruction + * @param opcode the opcode about to run + * @param ctx the execution context (its stack is sampled) + */ + void trace(int pc, int opcode, ExecutionContext ctx) + { + StringBuilder sb = new StringBuilder(); + sb.append(String.format("%06d %-11s", pc, MNEMONICS[opcode & 0xFF])); + if (tracePoint >= 0) + { + Zone zone = ctx.getGlyphZone(); + if (zone != null && tracePoint < zone.getPointCount()) + { + sb.append(String.format(" P%d=(%d,%d)", tracePoint, + zone.getCurrentX()[tracePoint], zone.getCurrentY()[tracePoint])); + } + } + sb.append(" #"); + // top of stack first, matching FreeType's ttinterp window + int window = Math.min(STACK_WINDOW, ctx.getStackDepth()); + for (int k = 0; k < window; k++) + { + sb.append(' ').append(ctx.peek(k)); + } + out.println(sb); + } + + private static String[] buildMnemonics() + { + String[] m = new String[256]; + for (int i = 0; i < 256; i++) + { + m[i] = String.format("INS_%02X", i); + } + // axis-variant vector setters: [y] for the even (0) code, [x] for the odd (1) code + put(m, 0x00, "SVTCA[y]"); + put(m, 0x01, "SVTCA[x]"); + put(m, 0x02, "SPVTCA[y]"); + put(m, 0x03, "SPVTCA[x]"); + put(m, 0x04, "SFVTCA[y]"); + put(m, 0x05, "SFVTCA[x]"); + put(m, 0x06, "SPVTL[||]"); + put(m, 0x07, "SPVTL[+]"); + put(m, 0x08, "SFVTL[||]"); + put(m, 0x09, "SFVTL[+]"); + put(m, 0x0A, "SPVFS"); + put(m, 0x0B, "SFVFS"); + put(m, 0x0C, "GPV"); + put(m, 0x0D, "GFV"); + put(m, 0x0E, "SFVTPV"); + put(m, 0x0F, "ISECT"); + put(m, 0x10, "SRP0"); + put(m, 0x11, "SRP1"); + put(m, 0x12, "SRP2"); + put(m, 0x13, "SZP0"); + put(m, 0x14, "SZP1"); + put(m, 0x15, "SZP2"); + put(m, 0x16, "SZPS"); + put(m, 0x17, "SLOOP"); + put(m, 0x18, "RTG"); + put(m, 0x19, "RTHG"); + put(m, 0x1A, "SMD"); + put(m, 0x1B, "ELSE"); + put(m, 0x1C, "JMPR"); + put(m, 0x1D, "SCVTCI"); + put(m, 0x1E, "SSWCI"); + put(m, 0x1F, "SSW"); + put(m, 0x20, "DUP"); + put(m, 0x21, "POP"); + put(m, 0x22, "CLEAR"); + put(m, 0x23, "SWAP"); + put(m, 0x24, "DEPTH"); + put(m, 0x25, "CINDEX"); + put(m, 0x26, "MINDEX"); + put(m, 0x27, "ALIGNPTS"); + put(m, 0x29, "UTP"); + put(m, 0x2A, "LOOPCALL"); + put(m, 0x2B, "CALL"); + put(m, 0x2C, "FDEF"); + put(m, 0x2D, "ENDF"); + put(m, 0x2E, "MDAP[nr]"); + put(m, 0x2F, "MDAP[rnd]"); + put(m, 0x30, "IUP[y]"); + put(m, 0x31, "IUP[x]"); + put(m, 0x32, "SHP[rp2]"); + put(m, 0x33, "SHP[rp1]"); + put(m, 0x34, "SHC[rp2]"); + put(m, 0x35, "SHC[rp1]"); + put(m, 0x36, "SHZ[rp2]"); + put(m, 0x37, "SHZ[rp1]"); + put(m, 0x38, "SHPIX"); + put(m, 0x39, "IP"); + put(m, 0x3A, "MSIRP[nr]"); + put(m, 0x3B, "MSIRP[rp0]"); + put(m, 0x3C, "ALIGNRP"); + put(m, 0x3D, "RTDG"); + put(m, 0x3E, "MIAP[nr]"); + put(m, 0x3F, "MIAP[rnd]"); + put(m, 0x40, "NPUSHB"); + put(m, 0x41, "NPUSHW"); + put(m, 0x42, "WS"); + put(m, 0x43, "RS"); + put(m, 0x44, "WCVTP"); + put(m, 0x45, "RCVT"); + put(m, 0x46, "GC[cur]"); + put(m, 0x47, "GC[org]"); + put(m, 0x48, "SCFS"); + put(m, 0x49, "MD[grid]"); + put(m, 0x4A, "MD[org]"); + put(m, 0x4B, "MPPEM"); + put(m, 0x4C, "MPS"); + put(m, 0x4D, "FLIPON"); + put(m, 0x4E, "FLIPOFF"); + put(m, 0x4F, "DEBUG"); + put(m, 0x50, "LT"); + put(m, 0x51, "LTEQ"); + put(m, 0x52, "GT"); + put(m, 0x53, "GTEQ"); + put(m, 0x54, "EQ"); + put(m, 0x55, "NEQ"); + put(m, 0x56, "ODD"); + put(m, 0x57, "EVEN"); + put(m, 0x58, "IF"); + put(m, 0x59, "EIF"); + put(m, 0x5A, "AND"); + put(m, 0x5B, "OR"); + put(m, 0x5C, "NOT"); + put(m, 0x5D, "DELTAP1"); + put(m, 0x5E, "SDB"); + put(m, 0x5F, "SDS"); + put(m, 0x60, "ADD"); + put(m, 0x61, "SUB"); + put(m, 0x62, "DIV"); + put(m, 0x63, "MUL"); + put(m, 0x64, "ABS"); + put(m, 0x65, "NEG"); + put(m, 0x66, "FLOOR"); + put(m, 0x67, "CEILING"); + put(m, 0x70, "WCVTF"); + put(m, 0x71, "DELTAP2"); + put(m, 0x72, "DELTAP3"); + put(m, 0x73, "DELTAC1"); + put(m, 0x74, "DELTAC2"); + put(m, 0x75, "DELTAC3"); + put(m, 0x76, "SROUND"); + put(m, 0x77, "S45ROUND"); + put(m, 0x78, "JROT"); + put(m, 0x79, "JROF"); + put(m, 0x7A, "ROFF"); + put(m, 0x7C, "RUTG"); + put(m, 0x7D, "RDTG"); + put(m, 0x7E, "SANGW"); + put(m, 0x7F, "AA"); + put(m, 0x80, "FLIPPT"); + put(m, 0x81, "FLIPRGON"); + put(m, 0x82, "FLIPRGOFF"); + put(m, 0x85, "SCANCTRL"); + put(m, 0x86, "SDPVTL[||]"); + put(m, 0x87, "SDPVTL[+]"); + put(m, 0x88, "GETINFO"); + put(m, 0x89, "IDEF"); + put(m, 0x8A, "ROLL"); + put(m, 0x8B, "MAX"); + put(m, 0x8C, "MIN"); + put(m, 0x8D, "SCANTYPE"); + put(m, 0x8E, "INSTCTRL"); + for (int k = 0; k < 4; k++) + { + put(m, 0x68 + k, "ROUND[" + k + "]"); + put(m, 0x6C + k, "NROUND[" + k + "]"); + } + for (int k = 0; k < 8; k++) + { + put(m, 0xB0 + k, "PUSHB[" + (k + 1) + "]"); + put(m, 0xB8 + k, "PUSHW[" + (k + 1) + "]"); + } + for (int op = 0xC0; op <= 0xDF; op++) + { + put(m, op, "MDRP[" + Integer.toHexString(op & 0x1F) + "]"); + } + for (int op = 0xE0; op <= 0xFF; op++) + { + put(m, op, "MIRP[" + Integer.toHexString(op & 0x1F) + "]"); + } + return m; + } + + private static void put(String[] m, int opcode, String name) + { + m[opcode] = name; + } +} diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/Fixed.java b/fontbox/src/main/java/org/apache/fontbox/ttf/Fixed.java new file mode 100644 index 00000000000..4f0d7623bd3 --- /dev/null +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/Fixed.java @@ -0,0 +1,210 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +/** + * Integer fixed-point math for the TrueType bytecode interpreter. + *

+ * The interpreter keeps coordinates in 26.6 fixed point (F26Dot6: 26 integer bits, 6 fractional bits, + * so 1 pixel == 64) and the projection/freedom vectors in 2.14 fixed point (F2Dot14). Mirroring + * FreeType's all-integer arithmetic - rather than mixing {@code float}/{@code double} - is what makes + * byte-exact comparison against a FreeType reference dump possible. + * + * @author Apache PDFBox + */ +final class Fixed +{ + /** One pixel in F26Dot6. */ + public static final int ONE = 64; + + /** Half a pixel in F26Dot6. */ + public static final int HALF = 32; + + /** 1.0 in F2Dot14. */ + public static final int ONE_F2DOT14 = 0x4000; + + private Fixed() + { + } + + /** + * Converts an integer to F26Dot6. + * + * @param value an integer pixel value + * @return the value in F26Dot6 + */ + public static int fromInt(int value) + { + return value << 6; + } + + /** + * Converts an F26Dot6 value back to an integer, rounding to nearest. + * + * @param value an F26Dot6 value + * @return the nearest integer + */ + public static int toInt(int value) + { + return (value + HALF) >> 6; + } + + /** + * Rounds an F26Dot6 value down to the pixel grid (towards negative infinity). + * + * @param value an F26Dot6 value + * @return the floored value, still in F26Dot6 + */ + public static int floor(int value) + { + return value & ~63; + } + + /** + * Rounds an F26Dot6 value up to the pixel grid. + * + * @param value an F26Dot6 value + * @return the ceiling value, still in F26Dot6 + */ + public static int ceil(int value) + { + return (value + 63) & ~63; + } + + /** + * Rounds an F26Dot6 value to the nearest pixel grid line (round-to-grid). + * + * @param value an F26Dot6 value + * @return the rounded value, still in F26Dot6 + */ + public static int round(int value) + { + return floor(value + HALF); + } + + /** + * Computes {@code round(a * b / c)} in 64-bit with correct sign handling, matching FreeType's + * {@code FT_MulDiv}. Used to build the F26Dot6 and F2Dot14 operators below. + * + * @param a first operand + * @param b second operand + * @param c divisor + * @return the rounded result + */ + public static int mulDiv(int a, int b, int c) + { + long la = a; + long lb = b; + long lc = c; + int sign = 1; + if (la < 0) + { + la = -la; + sign = -sign; + } + if (lb < 0) + { + lb = -lb; + sign = -sign; + } + if (lc < 0) + { + lc = -lc; + sign = -sign; + } + long result = lc != 0 ? (la * lb + lc / 2) / lc : 0x7FFFFFFFL; + return (int) (sign * result); + } + + /** + * Multiplies two F26Dot6 values, returning an F26Dot6 result (the TrueType {@code MUL} operator). + * + * @param a first F26Dot6 operand + * @param b second F26Dot6 operand + * @return {@code a * b} in F26Dot6 + */ + public static int mul(int a, int b) + { + return mulDiv(a, b, ONE); + } + + /** + * Divides two F26Dot6 values, returning an F26Dot6 result (the TrueType {@code DIV} operator). + * Division by zero yields zero. Unlike {@link #mul(int, int)} this truncates toward zero + * rather than rounding, matching FreeType's {@code DIV} opcode (which uses {@code FT_MulDiv_No_Round}). + * + * @param a F26Dot6 dividend + * @param b F26Dot6 divisor + * @return {@code a / b} in F26Dot6, or 0 if {@code b == 0} + */ + public static int div(int a, int b) + { + if (b == 0) + { + return 0; + } + long la = a; + long lb = b; + int sign = 1; + if (la < 0) + { + la = -la; + sign = -sign; + } + if (lb < 0) + { + lb = -lb; + sign = -sign; + } + return (int) (sign * (la * ONE / lb)); + } + + /** + * Multiplies an F26Dot6 value by an F2Dot14 value, returning F26Dot6. This is the building block + * for projecting a distance onto the projection/freedom vector. + * + * @param a an F26Dot6 value + * @param b an F2Dot14 value + * @return {@code a * b} in F26Dot6 + */ + public static int mul14(int a, int b) + { + return mulDiv(a, b, ONE_F2DOT14); + } + + /** + * Scales a coordinate from font units to F26Dot6 device pixels at the given ppem: + * {@code round(funits * ppem * 64 / unitsPerEm)}. Note this is not a flat {@code * 64} - + * that would only be correct when {@code unitsPerEm == ppem}. Both control values and glyph point + * coordinates are scaled with this. + * + * @param funits a value in font units + * @param ppem the active pixels-per-em + * @param unitsPerEm the font's unitsPerEm (from the head table) + * @return the value in F26Dot6 device pixels + */ + public static int scale(int funits, int ppem, int unitsPerEm) + { + if (unitsPerEm == 0) + { + return 0; + } + long numerator = (long) funits * ppem * ONE; + long rounded = numerator >= 0 ? numerator + unitsPerEm / 2 : numerator - unitsPerEm / 2; + return (int) (rounded / unitsPerEm); + } +} diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/FunctionDef.java b/fontbox/src/main/java/org/apache/fontbox/ttf/FunctionDef.java new file mode 100644 index 00000000000..11d44023fb9 --- /dev/null +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/FunctionDef.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +/** + * A function defined by an {@code FDEF} instruction: the bytecode program it lives in (normally the + * {@code fpgm}) and the offset of its first body instruction, just past the {@code FDEF}. {@code CALL} + * and {@code LOOPCALL} run the body from this offset until the matching {@code ENDF}. + * + * @author Apache PDFBox + */ +class FunctionDef +{ + private final byte[] program; + private final int entryPoint; + + /** + * @param program the bytecode the function body lives in + * @param entryPoint the offset of the first instruction after {@code FDEF} + */ + public FunctionDef(byte[] program, int entryPoint) + { + this.program = program; + this.entryPoint = entryPoint; + } + + /** @return the bytecode the function body lives in */ + public byte[] getProgram() + { + return program; + } + + /** @return the offset of the first body instruction */ + public int getEntryPoint() + { + return entryPoint; + } +} diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/GraphicsState.java b/fontbox/src/main/java/org/apache/fontbox/ttf/GraphicsState.java new file mode 100644 index 00000000000..708afad19d4 --- /dev/null +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/GraphicsState.java @@ -0,0 +1,530 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +/** + * The TrueType interpreter graphics state: projection/freedom vectors, reference and zone pointers, + * round state, and the various cut-ins and distances. Two operations carry the correctness burden: + *

    + *
  • {@link #copy()} performs a deep copy of the {@link UnitVector} fields, so mutating a + * per-glyph clone never contaminates the saved post-{@code prep} template.
  • + *
  • {@link #resetForGlyph()} resets only the fields the TrueType spec re-initializes at the start + * of each glyph program (the vectors, reference points, zone pointers and loop counter), while + * leaving the values established by {@code prep} (round state, cut-ins, delta base/shift, etc.) + * intact.
  • + *
+ * + * @author Apache PDFBox + */ +class GraphicsState +{ + /** Round to grid - the default round state. */ + public static final int ROUND_TO_GRID = 0; + /** Round to half grid. */ + public static final int ROUND_TO_HALF_GRID = 1; + /** Round to double grid. */ + public static final int ROUND_TO_DOUBLE_GRID = 2; + /** Round down to grid. */ + public static final int ROUND_DOWN_TO_GRID = 3; + /** Round up to grid. */ + public static final int ROUND_UP_TO_GRID = 4; + /** Rounding off. */ + public static final int ROUND_OFF = 5; + /** Super round (set by SROUND). */ + public static final int ROUND_SUPER = 6; + /** Super round 45 degrees (set by S45ROUND). */ + public static final int ROUND_SUPER_45 = 7; + + private UnitVector projectionVector; + private UnitVector freedomVector; + private UnitVector dualProjectionVector; + + private int rp0; + private int rp1; + private int rp2; + + private int zp0; + private int zp1; + private int zp2; + + private int loop; + private int roundState; + // derived rounding parameters (all F26Dot6), configured from the round state + private int roundPeriod; + private int roundPhase; + private int roundThreshold; + private boolean roundOff; + + private int minimumDistance; + private int controlValueCutIn; + private int singleWidthCutIn; + private int singleWidthValue; + private int deltaBase; + private int deltaShift; + private boolean autoFlip; + private int scanControl; + private int scanType; + private int instructControl; + + /** + * Creates a graphics state with the spec-mandated default values. This is the state before the + * font's {@code fpgm}/{@code prep} programs run. + */ + public GraphicsState() + { + projectionVector = UnitVector.xAxis(); + freedomVector = UnitVector.xAxis(); + dualProjectionVector = UnitVector.xAxis(); + rp0 = 0; + rp1 = 0; + rp2 = 0; + zp0 = 1; + zp1 = 1; + zp2 = 1; + loop = 1; + setRoundState(ROUND_TO_GRID); + minimumDistance = Fixed.ONE; // 1 pixel + controlValueCutIn = 17 * Fixed.ONE / 16; // 17/16 pixel = 68 + singleWidthCutIn = 0; + singleWidthValue = 0; + deltaBase = 9; + deltaShift = 3; + autoFlip = true; + scanControl = 0; + scanType = 0; + instructControl = 0; + } + + private GraphicsState(GraphicsState src) + { + projectionVector = src.projectionVector.copy(); + freedomVector = src.freedomVector.copy(); + dualProjectionVector = src.dualProjectionVector.copy(); + rp0 = src.rp0; + rp1 = src.rp1; + rp2 = src.rp2; + zp0 = src.zp0; + zp1 = src.zp1; + zp2 = src.zp2; + loop = src.loop; + roundState = src.roundState; + roundPeriod = src.roundPeriod; + roundPhase = src.roundPhase; + roundThreshold = src.roundThreshold; + roundOff = src.roundOff; + minimumDistance = src.minimumDistance; + controlValueCutIn = src.controlValueCutIn; + singleWidthCutIn = src.singleWidthCutIn; + singleWidthValue = src.singleWidthValue; + deltaBase = src.deltaBase; + deltaShift = src.deltaShift; + autoFlip = src.autoFlip; + scanControl = src.scanControl; + scanType = src.scanType; + instructControl = src.instructControl; + } + + /** + * Returns an independent deep copy, with the {@link UnitVector} fields cloned rather than shared. + * + * @return a deep copy of this graphics state + */ + public GraphicsState copy() + { + return new GraphicsState(this); + } + + /** + * Resets the per-glyph graphics state fields to their defaults, as the TrueType spec requires at + * the start of each glyph's instruction stream. The projection, freedom and dual-projection + * vectors return to the x axis; the reference points reset to 0; the zone pointers reset to the + * glyph zone (1); the loop counter resets to 1. Fields configured by {@code prep} (round state, + * cut-ins, minimum distance, delta base/shift, single width, auto-flip, scan control) are left + * untouched. + */ + public void resetForGlyph() + { + projectionVector.set(Fixed.ONE_F2DOT14, 0); + freedomVector.set(Fixed.ONE_F2DOT14, 0); + dualProjectionVector.set(Fixed.ONE_F2DOT14, 0); + rp0 = 0; + rp1 = 0; + rp2 = 0; + zp0 = 1; + zp1 = 1; + zp2 = 1; + loop = 1; + } + + /** @return the projection vector */ + public UnitVector getProjectionVector() + { + return projectionVector; + } + + /** @return the freedom vector */ + public UnitVector getFreedomVector() + { + return freedomVector; + } + + /** @return the dual projection vector */ + public UnitVector getDualProjectionVector() + { + return dualProjectionVector; + } + + /** @return reference point 0 */ + public int getRp0() + { + return rp0; + } + + /** @param value reference point 0 */ + public void setRp0(int value) + { + rp0 = value; + } + + /** @return reference point 1 */ + public int getRp1() + { + return rp1; + } + + /** @param value reference point 1 */ + public void setRp1(int value) + { + rp1 = value; + } + + /** @return reference point 2 */ + public int getRp2() + { + return rp2; + } + + /** @param value reference point 2 */ + public void setRp2(int value) + { + rp2 = value; + } + + /** @return zone pointer 0 */ + public int getZp0() + { + return zp0; + } + + /** @param value zone pointer 0 */ + public void setZp0(int value) + { + zp0 = value; + } + + /** @return zone pointer 1 */ + public int getZp1() + { + return zp1; + } + + /** @param value zone pointer 1 */ + public void setZp1(int value) + { + zp1 = value; + } + + /** @return zone pointer 2 */ + public int getZp2() + { + return zp2; + } + + /** @param value zone pointer 2 */ + public void setZp2(int value) + { + zp2 = value; + } + + /** @return the loop counter */ + public int getLoop() + { + return loop; + } + + /** @param value the loop counter */ + public void setLoop(int value) + { + loop = value; + } + + /** @return the round state */ + public int getRoundState() + { + return roundState; + } + + /** + * Sets the round state and derives the period/phase/threshold the {@link #round(int)} engine uses. + * The simple states are expressed as special cases of the super-round parameters. {@code SROUND} + * and {@code S45ROUND} call {@link #setSuperRound(int, int)} instead. + * + * @param value one of the {@code ROUND_*} constants + */ + public void setRoundState(int value) + { + roundState = value; + roundOff = false; + switch (value) + { + case ROUND_TO_GRID: + roundPeriod = Fixed.ONE; + roundPhase = 0; + roundThreshold = Fixed.HALF; + break; + case ROUND_TO_HALF_GRID: + roundPeriod = Fixed.ONE; + roundPhase = Fixed.HALF; + roundThreshold = Fixed.HALF; + break; + case ROUND_TO_DOUBLE_GRID: + roundPeriod = Fixed.HALF; + roundPhase = 0; + roundThreshold = Fixed.HALF / 2; + break; + case ROUND_DOWN_TO_GRID: + roundPeriod = Fixed.ONE; + roundPhase = 0; + roundThreshold = 0; + break; + case ROUND_UP_TO_GRID: + roundPeriod = Fixed.ONE; + roundPhase = 0; + roundThreshold = Fixed.ONE - 1; + break; + case ROUND_OFF: + roundOff = true; + break; + default: + // ROUND_SUPER / ROUND_SUPER_45 are configured by setSuperRound + break; + } + } + + /** + * Configures super-round parameters for {@code SROUND}/{@code S45ROUND} from the selector byte, + * per the TrueType specification. + * + * @param gridPeriod the base grid period in F26Dot6 (one pixel for SROUND; the diagonal for + * S45ROUND) + * @param selector the operand byte controlling period, phase and threshold + */ + public void setSuperRound(int gridPeriod, int selector) + { + switch (selector & 0xC0) + { + case 0x00: + roundPeriod = gridPeriod / 2; + break; + case 0x80: + roundPeriod = gridPeriod * 2; + break; + default: + roundPeriod = gridPeriod; + break; + } + if (roundPeriod < 1) + { + roundPeriod = 1; + } + switch (selector & 0x30) + { + case 0x00: + roundPhase = 0; + break; + case 0x10: + roundPhase = roundPeriod / 4; + break; + case 0x20: + roundPhase = roundPeriod / 2; + break; + default: + roundPhase = roundPeriod * 3 / 4; + break; + } + int n = selector & 0x0F; + roundThreshold = n == 0 ? roundPeriod - 1 : (n - 4) * roundPeriod / 8; + roundState = ROUND_SUPER; + roundOff = false; + } + + /** + * Rounds a distance according to the current round state. Engine compensation (the black/white/ + * grey distance bias FreeType applies) is treated as zero, which is correct for an anti-aliased + * Java2D target. + * + * @param distance the distance in F26Dot6 + * @return the rounded distance in F26Dot6 + */ + public int round(int distance) + { + if (roundOff) + { + return distance; + } + int val; + if (distance >= 0) + { + val = Math.floorDiv(distance - roundPhase + roundThreshold, roundPeriod) * roundPeriod; + if (val < 0) + { + val = 0; + } + val += roundPhase; + } + else + { + val = -(Math.floorDiv(roundThreshold - roundPhase - distance, roundPeriod) * roundPeriod); + if (val > 0) + { + val = 0; + } + val -= roundPhase; + } + return val; + } + + /** @return the minimum distance in F26Dot6 */ + public int getMinimumDistance() + { + return minimumDistance; + } + + /** @param value the minimum distance in F26Dot6 */ + public void setMinimumDistance(int value) + { + minimumDistance = value; + } + + /** @return the control value cut-in in F26Dot6 */ + public int getControlValueCutIn() + { + return controlValueCutIn; + } + + /** @param value the control value cut-in in F26Dot6 */ + public void setControlValueCutIn(int value) + { + controlValueCutIn = value; + } + + /** @return the single width cut-in in F26Dot6 */ + public int getSingleWidthCutIn() + { + return singleWidthCutIn; + } + + /** @param value the single width cut-in in F26Dot6 */ + public void setSingleWidthCutIn(int value) + { + singleWidthCutIn = value; + } + + /** @return the single width value in F26Dot6 */ + public int getSingleWidthValue() + { + return singleWidthValue; + } + + /** @param value the single width value in F26Dot6 */ + public void setSingleWidthValue(int value) + { + singleWidthValue = value; + } + + /** @return the delta base */ + public int getDeltaBase() + { + return deltaBase; + } + + /** @param value the delta base */ + public void setDeltaBase(int value) + { + deltaBase = value; + } + + /** @return the delta shift */ + public int getDeltaShift() + { + return deltaShift; + } + + /** @param value the delta shift */ + public void setDeltaShift(int value) + { + deltaShift = value; + } + + /** @return whether auto-flip is enabled */ + public boolean isAutoFlip() + { + return autoFlip; + } + + /** @param value whether auto-flip is enabled */ + public void setAutoFlip(boolean value) + { + autoFlip = value; + } + + /** @return the scan control flags */ + public int getScanControl() + { + return scanControl; + } + + /** @param value the scan control flags */ + public void setScanControl(int value) + { + scanControl = value; + } + + /** @return the scan type */ + public int getScanType() + { + return scanType; + } + + /** @param value the scan type */ + public void setScanType(int value) + { + scanType = value; + } + + /** @return the instruction control flags */ + public int getInstructControl() + { + return instructControl; + } + + /** @param value the instruction control flags */ + public void setInstructControl(int value) + { + instructControl = value; + } +} diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/HintingException.java b/fontbox/src/main/java/org/apache/fontbox/ttf/HintingException.java new file mode 100644 index 00000000000..6ace71dc70e --- /dev/null +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/HintingException.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +/** + * Thrown when the TrueType bytecode interpreter encounters a malformed or unsupported program. It is + * unchecked so that opcode handlers stay terse; the caller catches it per glyph and falls back to raw, + * unhinted coordinates rather than letting one bad glyph disable hinting for the whole font. + * + * @author Apache PDFBox + */ +class HintingException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * @param message describes the failure + */ + public HintingException(String message) + { + super(message); + } + + /** + * @param message describes the failure + * @param cause the underlying cause + */ + public HintingException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeInterpreter.java b/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeInterpreter.java new file mode 100644 index 00000000000..2fd52cc351e --- /dev/null +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeInterpreter.java @@ -0,0 +1,1649 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * The TrueType bytecode interpreter: the VM driver and the 256-entry opcode dispatch table. + *

+ * It carries the execution engine - the {@link BytecodeStream} cursor driven dispatch loop, function + * definition and calling with a depth cap, branching, the push family, and the + * stack/arithmetic/logical/storage and point-moving opcodes - plus the size lifecycle: {@code fpgm} + * executed once, {@code prep} executed per ppem change with the result saved as the per-glyph + * template. An opcode with no handler and no {@code IDEF} binding throws {@link HintingException}, + * which the caller catches per glyph and falls back to the raw outline. + * + * @author Apache PDFBox + */ +class TrueTypeInterpreter +{ + /** Maximum {@code CALL}/{@code LOOPCALL} nesting depth, matching FreeType. */ + static final int MAX_CALL_DEPTH = 64; + + /** A single opcode handler. */ + @FunctionalInterface + private interface OpHandler + { + void execute(ExecutionContext ctx); + } + + // opcodes referenced by the engine itself (control flow / push) + private static final int NPUSHB = 0x40; + private static final int NPUSHW = 0x41; + private static final int PUSHB_BASE = 0xB0; + private static final int PUSHW_BASE = 0xB8; + private static final int ELSE = 0x1B; + private static final int IF = 0x58; + private static final int EIF = 0x59; + private static final int FDEF = 0x2C; + private static final int ENDF = 0x2D; + + private final OpHandler[] dispatch = new OpHandler[256]; + private final Map functions = new HashMap<>(); + private final Map instructionDefs = new HashMap<>(); + + private final int maxStackElements; + private final int unitsPerEm; + + // The storage area and twilight zone belong to the size, not to one program run: a font may seed + // them in prep and read them back from every glyph program. FreeType keeps both on the TT_Size and + // clears them in tt_size_run_prep, which is what setPpem does below. + private final int[] storage; + private final Zone twilightZone; + + private byte[] fontProgram; + private byte[] controlValueProgram; + private int[] rawControlValues = new int[0]; + private int[] scaledControlValues = new int[0]; + + private int ppem; + private int pointSize; + private GraphicsState savedState; + private ExecutionTracer tracer; + + /** + * @param maxStackElements operand stack capacity (from maxp) + * @param maxStorage storage area size (from maxp) + * @param maxTwilightPoints twilight zone size (from maxp) + * @param unitsPerEm the font's unitsPerEm (from head) + */ + public TrueTypeInterpreter(int maxStackElements, int maxStorage, int maxTwilightPoints, + int unitsPerEm) + { + this.maxStackElements = maxStackElements; + this.unitsPerEm = unitsPerEm; + this.storage = new int[Math.max(maxStorage, 0)]; + this.twilightZone = new Zone(Math.max(maxTwilightPoints, 0), 0); + buildDispatch(); + } + + // --- configuration --------------------------------------------------- + + /** @param program the raw {@code fpgm} bytecode, or null */ + public void setFontProgram(byte[] program) + { + this.fontProgram = program; + } + + /** @param program the raw {@code prep} bytecode, or null */ + public void setControlValueProgram(byte[] program) + { + this.controlValueProgram = program; + } + + /** @param values the raw control values in font units, or null */ + public void setControlValues(int[] values) + { + this.rawControlValues = values != null ? values : new int[0]; + } + + /** + * Attaches (or clears with {@code null}) an execution tracer that emits one FreeType-comparable + * line per executed instruction. Used by the trace-diff tooling to localize divergence; off in + * normal operation. + * + * @param tracer the tracer, or null to disable tracing + */ + public void setTracer(ExecutionTracer tracer) + { + this.tracer = tracer; + } + + // --- lifecycle ------------------------------------------------------- + + /** + * Runs the font program ({@code fpgm}) once, populating the function table. Safe to call when + * there is no font program. + */ + public void prepareFontProgram() + { + functions.clear(); + instructionDefs.clear(); + if (fontProgram == null || fontProgram.length == 0) + { + return; + } + ExecutionContext ctx = newContext(new GraphicsState()); + run(ctx, new BytecodeStream(fontProgram)); + } + + /** + * Establishes a new ppem: scales the control values, clears the storage area and twilight zone, runs + * the control value program ({@code prep}) from a default graphics state, and saves the resulting + * state as the per-glyph template. Whatever {@code prep} leaves in storage and the twilight zone + * stays there for every glyph hinted at this size, which is why they are cleared here and not per + * glyph - after FreeType's {@code tt_size_run_prep}. Anything the font program wrote to storage is + * discarded, again as FreeType does: {@code fpgm} is only meant to define functions. + * + * @param ppemValue the pixels-per-em to render at + * @param pointSizeValue the point size + */ + public void setPpem(int ppemValue, int pointSizeValue) + { + this.ppem = ppemValue; + this.pointSize = pointSizeValue; + scaleControlValues(); + Arrays.fill(storage, 0); + twilightZone.reset(); + + GraphicsState gs = new GraphicsState(); + if (controlValueProgram != null && controlValueProgram.length > 0) + { + ExecutionContext ctx = newContext(gs); + run(ctx, new BytecodeStream(controlValueProgram)); + } + savedState = gs; + } + + private void scaleControlValues() + { + scaledControlValues = new int[rawControlValues.length]; + for (int i = 0; i < rawControlValues.length; i++) + { + scaledControlValues[i] = Fixed.scale(rawControlValues[i], ppem, unitsPerEm); + } + } + + /** + * Builds a fresh execution context wired to this interpreter's sizes, scaled control values and the + * current ppem. The stack and per-run counters are new; the storage area and twilight zone are the + * interpreter's own, so values {@code prep} left there are visible to the glyph programs. + * + * @param gs the graphics state the context starts from + * @return a new execution context + */ + public ExecutionContext newContext(GraphicsState gs) + { + ExecutionContext ctx = new ExecutionContext(this, gs, maxStackElements, storage, + scaledControlValues, twilightZone); + ctx.setUnitsPerEm(unitsPerEm); + ctx.setPpem(ppem); + ctx.setPointSize(pointSize); + return ctx; + } + + /** + * Test/utility entry point: runs a standalone bytecode program from the saved (post-{@code prep}) + * state, or a default state if no size has been set, and returns the resulting context so callers + * can inspect the stack and state. + * + * @param program the bytecode to run + * @param ppemValue the ppem to run at + * @return the execution context after the program completes + */ + public ExecutionContext executeProgram(byte[] program, int ppemValue) + { + this.ppem = ppemValue; + GraphicsState gs = savedState != null ? savedState.copy() : new GraphicsState(); + ExecutionContext ctx = newContext(gs); + run(ctx, new BytecodeStream(program)); + return ctx; + } + + /** @return the saved post-{@code prep} graphics state, or null if no size has been set */ + public GraphicsState getSavedState() + { + return savedState; + } + + /** @return the function table populated by {@code fpgm} */ + public Map getFunctions() + { + return functions; + } + + // --- execution engine ------------------------------------------------ + + /** + * Runs a bytecode stream to completion (or until an {@code ENDF} returns from a function body), + * dispatching each opcode through the table. + * + * @param ctx the execution context + * @param s the stream to run + */ + public void run(ExecutionContext ctx, BytecodeStream s) + { + BytecodeStream previous = ctx.getStream(); + ctx.setStream(s); + try + { + while (s.hasNext() && !ctx.isReturnFromFunction()) + { + s.markInstructionStart(); + int opcode = s.nextByte(); + if (tracer != null) + { + tracer.trace(s.instructionStart(), opcode, ctx); + } + dispatch[opcode].execute(ctx); + } + } + finally + { + ctx.setStream(previous); + } + } + + /** + * Calls the function with the given number, running its body until the matching {@code ENDF}. + * + * @param ctx the execution context + * @param functionNumber the function to call + * @throws HintingException if the function is undefined or the call depth is exceeded + */ + public void callFunction(ExecutionContext ctx, int functionNumber) + { + FunctionDef def = functions.get(functionNumber); + if (def == null) + { + throw new HintingException("call to undefined function " + functionNumber); + } + callBody(ctx, def); + } + + /** Runs a function/instruction body from its entry point until the matching {@code ENDF}. */ + private void callBody(ExecutionContext ctx, FunctionDef def) + { + if (ctx.getCallDepth() >= MAX_CALL_DEPTH) + { + throw new HintingException("maximum call depth " + MAX_CALL_DEPTH + " exceeded"); + } + ctx.enterCall(); + try + { + BytecodeStream body = new BytecodeStream(def.getProgram()); + body.seek(def.getEntryPoint()); + run(ctx, body); + ctx.setReturnFromFunction(false); + } + finally + { + ctx.leaveCall(); + } + } + + private void defineFunction(ExecutionContext ctx) + { + int functionNumber = ctx.pop(); + BytecodeStream s = ctx.getStream(); + functions.put(functionNumber, new FunctionDef(s.getCode(), s.position())); + skipFunctionBody(s); + } + + /** IDEF: binds the opcode on top of the stack to the following instructions (until ENDF). */ + private void defineInstruction(ExecutionContext ctx) + { + int opcode = ctx.pop(); + BytecodeStream s = ctx.getStream(); + instructionDefs.put(opcode & 0xFF, new FunctionDef(s.getCode(), s.position())); + skipFunctionBody(s); + } + + private void skipFunctionBody(BytecodeStream s) + { + while (s.hasNext()) + { + int opcode = s.nextByte(); + if (opcode == ENDF) + { + return; + } + skipPushOperands(opcode, s); + } + throw new HintingException("FDEF without matching ENDF"); + } + + /** + * On a false {@code IF}, skips forward to the matching {@code ELSE} or {@code EIF}, accounting for + * nested {@code IF} blocks and for the inline operands of push instructions. Leaves the stream + * positioned just after the terminator. + */ + private void skipToElseOrEif(BytecodeStream s) + { + int depth = 0; + while (s.hasNext()) + { + int opcode = s.nextByte(); + if (opcode == IF) + { + depth++; + } + else if (opcode == EIF) + { + if (depth == 0) + { + return; + } + depth--; + } + else if (opcode == ELSE && depth == 0) + { + return; + } + else + { + skipPushOperands(opcode, s); + } + } + throw new HintingException("IF without matching EIF"); + } + + /** + * After a true {@code IF} branch reaches its {@code ELSE}, skips the else-branch to the matching + * {@code EIF}. + */ + private void skipToEif(BytecodeStream s) + { + int depth = 0; + while (s.hasNext()) + { + int opcode = s.nextByte(); + if (opcode == IF) + { + depth++; + } + else if (opcode == EIF) + { + if (depth == 0) + { + return; + } + depth--; + } + else + { + skipPushOperands(opcode, s); + } + } + throw new HintingException("ELSE without matching EIF"); + } + + /** Advances the stream past the inline operands of a push opcode; a no-op for other opcodes. */ + private void skipPushOperands(int opcode, BytecodeStream s) + { + if (opcode == NPUSHB) + { + s.skip(s.nextByte()); + } + else if (opcode == NPUSHW) + { + s.skip(2 * s.nextByte()); + } + else if (opcode >= PUSHB_BASE && opcode <= PUSHB_BASE + 7) + { + s.skip(opcode - PUSHB_BASE + 1); + } + else if (opcode >= PUSHW_BASE && opcode <= PUSHW_BASE + 7) + { + s.skip(2 * (opcode - PUSHW_BASE + 1)); + } + } + + // --- dispatch table -------------------------------------------------- + + private void buildDispatch() + { + for (int i = 0; i < dispatch.length; i++) + { + final int opcode = i; + dispatch[i] = ctx -> + { + // an opcode with no built-in handler may have been given one by IDEF + FunctionDef def = instructionDefs.get(opcode); + if (def != null) + { + callBody(ctx, def); + return; + } + throw new HintingException( + String.format("unsupported TrueType opcode 0x%02X", opcode)); + }; + } + + installPushOps(); + installStackOps(); + installArithmeticOps(); + installLogicalOps(); + installFlowOps(); + installStateOps(); + installStorageAndCvtOps(); + installMiscOps(); + installVectorOps(); + installRoundOps(); + installPointOps(); + installInterpolationOps(); + installMeasureOps(); + installDeltaOps(); + installFlipOps(); + } + + private void installPushOps() + { + dispatch[NPUSHB] = ctx -> + { + int n = ctx.getStream().nextByte(); + for (int i = 0; i < n; i++) + { + ctx.push(ctx.getStream().nextByte()); + } + }; + dispatch[NPUSHW] = ctx -> + { + int n = ctx.getStream().nextByte(); + for (int i = 0; i < n; i++) + { + ctx.push(ctx.getStream().nextWord()); + } + }; + for (int k = 0; k < 8; k++) + { + final int count = k + 1; + dispatch[PUSHB_BASE + k] = ctx -> + { + for (int i = 0; i < count; i++) + { + ctx.push(ctx.getStream().nextByte()); + } + }; + dispatch[PUSHW_BASE + k] = ctx -> + { + for (int i = 0; i < count; i++) + { + ctx.push(ctx.getStream().nextWord()); + } + }; + } + } + + private void installStackOps() + { + dispatch[0x20] = ctx -> ctx.push(ctx.peek(0)); // DUP + dispatch[0x21] = ExecutionContext::pop; // POP + dispatch[0x22] = ExecutionContext::clearStack; // CLEAR + dispatch[0x23] = ctx -> // SWAP + { + int a = ctx.pop(); + int b = ctx.pop(); + ctx.push(a); + ctx.push(b); + }; + dispatch[0x24] = ctx -> ctx.push(ctx.getStackDepth()); // DEPTH + dispatch[0x25] = ctx -> ctx.push(ctx.peek(ctx.pop() - 1)); // CINDEX + dispatch[0x26] = ctx -> // MINDEX + { + int k = ctx.pop(); + int[] tmp = new int[k]; + for (int i = 0; i < k; i++) + { + tmp[i] = ctx.pop(); + } + for (int i = k - 2; i >= 0; i--) + { + ctx.push(tmp[i]); + } + ctx.push(tmp[k - 1]); + }; + dispatch[0x8A] = ctx -> // ROLL + { + int c = ctx.pop(); + int b = ctx.pop(); + int a = ctx.pop(); + ctx.push(b); + ctx.push(c); + ctx.push(a); + }; + } + + private void installArithmeticOps() + { + dispatch[0x60] = ctx -> binary(ctx, (a, b) -> a + b); // ADD + dispatch[0x61] = ctx -> binary(ctx, (a, b) -> a - b); // SUB + dispatch[0x62] = ctx -> binary(ctx, Fixed::div); // DIV + dispatch[0x63] = ctx -> binary(ctx, Fixed::mul); // MUL + dispatch[0x64] = ctx -> ctx.push(Math.abs(ctx.pop())); // ABS + dispatch[0x65] = ctx -> ctx.push(-ctx.pop()); // NEG + dispatch[0x66] = ctx -> ctx.push(Fixed.floor(ctx.pop())); // FLOOR + dispatch[0x67] = ctx -> ctx.push(Fixed.ceil(ctx.pop())); // CEILING + dispatch[0x8B] = ctx -> binary(ctx, Math::max); // MAX + dispatch[0x8C] = ctx -> binary(ctx, Math::min); // MIN + } + + private void installLogicalOps() + { + dispatch[0x50] = ctx -> binary(ctx, (a, b) -> bool(a < b)); // LT + dispatch[0x51] = ctx -> binary(ctx, (a, b) -> bool(a <= b)); // LTEQ + dispatch[0x52] = ctx -> binary(ctx, (a, b) -> bool(a > b)); // GT + dispatch[0x53] = ctx -> binary(ctx, (a, b) -> bool(a >= b)); // GTEQ + dispatch[0x54] = ctx -> binary(ctx, (a, b) -> bool(a == b)); // EQ + dispatch[0x55] = ctx -> binary(ctx, (a, b) -> bool(a != b)); // NEQ + dispatch[0x56] = ctx -> ctx.push(bool(((Fixed.round(ctx.pop()) >> 6) & 1) != 0)); // ODD + dispatch[0x57] = ctx -> ctx.push(bool(((Fixed.round(ctx.pop()) >> 6) & 1) == 0)); // EVEN + dispatch[0x5A] = ctx -> binary(ctx, (a, b) -> bool(a != 0 && b != 0)); // AND + dispatch[0x5B] = ctx -> binary(ctx, (a, b) -> bool(a != 0 || b != 0)); // OR + dispatch[0x5C] = ctx -> ctx.push(bool(ctx.pop() == 0)); // NOT + } + + private void installFlowOps() + { + dispatch[IF] = ctx -> + { + if (ctx.pop() == 0) + { + skipToElseOrEif(ctx.getStream()); + } + }; + dispatch[ELSE] = ctx -> skipToEif(ctx.getStream()); + dispatch[EIF] = ctx -> { /* no-op terminator */ }; + dispatch[0x1C] = ctx -> jump(ctx, ctx.pop()); // JMPR + dispatch[0x78] = ctx -> // JROT + { + int e = ctx.pop(); + int offset = ctx.pop(); + if (e != 0) + { + jump(ctx, offset); + } + }; + dispatch[0x79] = ctx -> // JROF + { + int e = ctx.pop(); + int offset = ctx.pop(); + if (e == 0) + { + jump(ctx, offset); + } + }; + dispatch[FDEF] = this::defineFunction; + dispatch[ENDF] = ctx -> ctx.setReturnFromFunction(true); + dispatch[0x89] = this::defineInstruction; // IDEF + dispatch[0x2B] = ctx -> callFunction(ctx, ctx.pop()); // CALL + dispatch[0x2A] = ctx -> // LOOPCALL + { + int functionNumber = ctx.pop(); + int count = ctx.pop(); + // the spec calls the count unsigned; FreeType runs nothing at all when it is not positive + if (count <= 0) + { + return; + } + // charge the whole loop up front, so an absurd count fails before a single iteration runs + ctx.countLoopCalls(count); + for (int i = 0; i < count; i++) + { + callFunction(ctx, functionNumber); + } + }; + } + + /** + * Jumps to {@code offset} bytes from the start of the current instruction. A backward jump is the + * only way TrueType bytecode can loop other than {@code LOOPCALL}, so those are counted against the + * run's budget and the program is abandoned once it exceeds it. + * + * @param ctx the execution context + * @param offset the jump offset, relative to the current instruction + * @throws HintingException if the stream position is out of range, or too many backward jumps + */ + private static void jump(ExecutionContext ctx, int offset) + { + if (offset < 0) + { + ctx.countNegativeJump(); + } + BytecodeStream s = ctx.getStream(); + s.seek(s.instructionStart() + offset); + } + + private void installStateOps() + { + dispatch[0x17] = ctx -> ctx.getGraphicsState().setLoop(ctx.pop()); // SLOOP + dispatch[0x10] = ctx -> ctx.getGraphicsState().setRp0(ctx.pop()); // SRP0 + dispatch[0x11] = ctx -> ctx.getGraphicsState().setRp1(ctx.pop()); // SRP1 + dispatch[0x12] = ctx -> ctx.getGraphicsState().setRp2(ctx.pop()); // SRP2 + dispatch[0x1A] = ctx -> ctx.getGraphicsState().setMinimumDistance(ctx.pop()); // SMD + dispatch[0x5E] = ctx -> ctx.getGraphicsState().setDeltaBase(ctx.pop()); // SDB + dispatch[0x5F] = ctx -> ctx.getGraphicsState().setDeltaShift(ctx.pop()); // SDS + dispatch[0x1D] = ctx -> ctx.getGraphicsState().setControlValueCutIn(ctx.pop()); // SCVTCI + dispatch[0x1E] = ctx -> ctx.getGraphicsState().setSingleWidthCutIn(ctx.pop()); // SSWCI + dispatch[0x1F] = ctx -> ctx.getGraphicsState().setSingleWidthValue(ctx.pop()); // SSW + + dispatch[0x18] = roundState(GraphicsState.ROUND_TO_GRID); // RTG + dispatch[0x19] = roundState(GraphicsState.ROUND_TO_HALF_GRID); // RTHG + dispatch[0x3D] = roundState(GraphicsState.ROUND_TO_DOUBLE_GRID); // RTDG + dispatch[0x7C] = roundState(GraphicsState.ROUND_UP_TO_GRID); // RUTG + dispatch[0x7D] = roundState(GraphicsState.ROUND_DOWN_TO_GRID); // RDTG + dispatch[0x7A] = roundState(GraphicsState.ROUND_OFF); // ROFF + dispatch[0x76] = ctx -> ctx.getGraphicsState().setSuperRound(Fixed.ONE, ctx.pop()); // SROUND + // S45ROUND: grid period is the 45-degree diagonal, sqrt(2)/2 px ~= 45 in F26Dot6 + dispatch[0x77] = ctx -> ctx.getGraphicsState().setSuperRound(45, ctx.pop()); // S45ROUND + dispatch[0x13] = ctx -> ctx.getGraphicsState().setZp0(ctx.pop()); // SZP0 + dispatch[0x14] = ctx -> ctx.getGraphicsState().setZp1(ctx.pop()); // SZP1 + dispatch[0x15] = ctx -> ctx.getGraphicsState().setZp2(ctx.pop()); // SZP2 + dispatch[0x16] = ctx -> // SZPS + { + int zone = ctx.pop(); + GraphicsState gs = ctx.getGraphicsState(); + gs.setZp0(zone); + gs.setZp1(zone); + gs.setZp2(zone); + }; + } + + private void installStorageAndCvtOps() + { + dispatch[0x43] = ctx -> // RS + { + int index = ctx.pop(); + ctx.push(read(ctx.getStorage(), index, "storage")); + }; + dispatch[0x42] = ctx -> // WS + { + int value = ctx.pop(); + int index = ctx.pop(); + write(ctx.getStorage(), index, value, "storage"); + }; + dispatch[0x45] = ctx -> // RCVT + { + int index = ctx.pop(); + ctx.push(read(ctx.getControlValues(), index, "cvt")); + }; + dispatch[0x44] = ctx -> // WCVTP + { + int value = ctx.pop(); + int index = ctx.pop(); + write(ctx.getControlValues(), index, value, "cvt"); + }; + dispatch[0x70] = ctx -> // WCVTF + { + int value = ctx.pop(); + int index = ctx.pop(); + write(ctx.getControlValues(), index, + Fixed.scale(value, ctx.getPpem(), ctx.getUnitsPerEm()), "cvt"); + }; + } + + private void installMiscOps() + { + dispatch[0x4B] = ctx -> ctx.push(ctx.getPpem()); // MPPEM + dispatch[0x4C] = ctx -> ctx.push(ctx.getPointSize()); // MPS + dispatch[0x4F] = ExecutionContext::pop; // DEBUG (pops, no-op) + dispatch[0x7E] = ExecutionContext::pop; // SANGW (deprecated, pops) + dispatch[0x7F] = ctx -> { /* AA - deprecated no-op */ }; // AA + dispatch[0x88] = ctx -> // GETINFO + { + int selector = ctx.pop(); + int result = 0; + if ((selector & 0x0001) != 0) + { + // rasterizer version 40: FreeType's "minimal" subpixel interpreter, which we mirror + // for grayscale antialiased rendering (lighter stems than the classic v35) + result |= 40; + } + // we always render grayscale-subpixel ("lean"), non-LCD: report the subpixel bits a v40 + // grayscale rasterizer returns so fonts take their lighter ClearType-aware code paths. + // (the grayscale bit 12 is intentionally not set: FreeType clears exc->grayscale in lean mode) + if ((selector & 0x0040) != 0) + { + result |= 1 << 13; // subpixel hinting active + } + if ((selector & 0x0400) != 0) + { + result |= 1 << 17; // ClearType hinting active + } + if ((selector & 0x0800) != 0) + { + result |= 1 << 18; // subpixel positioned + } + if ((selector & 0x1000) != 0) + { + result |= 1 << 19; // grayscale ClearType + } + ctx.push(result); + }; + } + + // --- vector setters -------------------------------------------------- + + private void installVectorOps() + { + dispatch[0x00] = ctx -> setProjAndFreedomAxis(ctx, false); // SVTCA[0] y + dispatch[0x01] = ctx -> setProjAndFreedomAxis(ctx, true); // SVTCA[1] x + dispatch[0x02] = ctx -> setProjectionAxis(ctx, false); // SPVTCA[0] y + dispatch[0x03] = ctx -> setProjectionAxis(ctx, true); // SPVTCA[1] x + dispatch[0x04] = ctx -> setFreedomAxis(ctx, false); // SFVTCA[0] y + dispatch[0x05] = ctx -> setFreedomAxis(ctx, true); // SFVTCA[1] x + dispatch[0x06] = ctx -> setProjectionToLine(ctx, false); // SPVTL[0] parallel + dispatch[0x07] = ctx -> setProjectionToLine(ctx, true); // SPVTL[1] perpendicular + dispatch[0x08] = ctx -> setFreedomToLine(ctx, false); // SFVTL[0] parallel + dispatch[0x09] = ctx -> setFreedomToLine(ctx, true); // SFVTL[1] perpendicular + dispatch[0x86] = ctx -> setDualProjectionToLine(ctx, false); // SDPVTL[0] + dispatch[0x87] = ctx -> setDualProjectionToLine(ctx, true); // SDPVTL[1] + dispatch[0x0E] = ctx -> // SFVTPV + { + UnitVector pv = ctx.getGraphicsState().getProjectionVector(); + ctx.getGraphicsState().getFreedomVector().set(pv.getX(), pv.getY()); + }; + dispatch[0x0A] = ctx -> // SPVFS + { + int y = ctx.pop(); + int x = ctx.pop(); + ctx.getGraphicsState().getProjectionVector().set(x, y); + ctx.getGraphicsState().getDualProjectionVector().set(x, y); + }; + dispatch[0x0B] = ctx -> // SFVFS + { + int y = ctx.pop(); + int x = ctx.pop(); + ctx.getGraphicsState().getFreedomVector().set(x, y); + }; + dispatch[0x0C] = ctx -> // GPV + { + UnitVector pv = ctx.getGraphicsState().getProjectionVector(); + ctx.push(pv.getX()); + ctx.push(pv.getY()); + }; + dispatch[0x0D] = ctx -> // GFV + { + UnitVector fv = ctx.getGraphicsState().getFreedomVector(); + ctx.push(fv.getX()); + ctx.push(fv.getY()); + }; + } + + private static void setProjAndFreedomAxis(ExecutionContext ctx, boolean xAxis) + { + setProjectionAxis(ctx, xAxis); + setFreedomAxis(ctx, xAxis); + } + + private static void setProjectionAxis(ExecutionContext ctx, boolean xAxis) + { + int x = xAxis ? Fixed.ONE_F2DOT14 : 0; + int y = xAxis ? 0 : Fixed.ONE_F2DOT14; + ctx.getGraphicsState().getProjectionVector().set(x, y); + ctx.getGraphicsState().getDualProjectionVector().set(x, y); + } + + private static void setFreedomAxis(ExecutionContext ctx, boolean xAxis) + { + int x = xAxis ? Fixed.ONE_F2DOT14 : 0; + int y = xAxis ? 0 : Fixed.ONE_F2DOT14; + ctx.getGraphicsState().getFreedomVector().set(x, y); + } + + private void setProjectionToLine(ExecutionContext ctx, boolean perpendicular) + { + UnitVector[] v = lineVectors(ctx, perpendicular); + ctx.getGraphicsState().getProjectionVector().set(v[0].getX(), v[0].getY()); + ctx.getGraphicsState().getDualProjectionVector().set(v[1].getX(), v[1].getY()); + } + + private void setFreedomToLine(ExecutionContext ctx, boolean perpendicular) + { + UnitVector[] v = lineVectors(ctx, perpendicular); + ctx.getGraphicsState().getFreedomVector().set(v[0].getX(), v[0].getY()); + } + + private void setDualProjectionToLine(ExecutionContext ctx, boolean perpendicular) + { + UnitVector[] v = lineVectors(ctx, perpendicular); + ctx.getGraphicsState().getProjectionVector().set(v[0].getX(), v[0].getY()); + ctx.getGraphicsState().getDualProjectionVector().set(v[1].getX(), v[1].getY()); + } + + /** + * Pops two point numbers and returns {current-based, original-based} unit vectors along (or + * perpendicular to) the line between them. The first point is taken from zp2, the second from zp1. + */ + private UnitVector[] lineVectors(ExecutionContext ctx, boolean perpendicular) + { + GraphicsState gs = ctx.getGraphicsState(); + int p2 = ctx.pop(); + int p1 = ctx.pop(); + Zone z1 = ctx.getZone(gs.getZp2()); + Zone z2 = ctx.getZone(gs.getZp1()); + UnitVector current = UnitVector.normalize(z2.getCurrentX()[p2] - z1.getCurrentX()[p1], + z2.getCurrentY()[p2] - z1.getCurrentY()[p1]); + UnitVector original = UnitVector.normalize(z2.getOriginalX()[p2] - z1.getOriginalX()[p1], + z2.getOriginalY()[p2] - z1.getOriginalY()[p1]); + if (perpendicular) + { + current = current.perpendicular(); + original = original.perpendicular(); + } + return new UnitVector[] { current, original }; + } + + // --- rounding opcodes ------------------------------------------------ + + private void installRoundOps() + { + for (int k = 0; k < 4; k++) + { + dispatch[0x68 + k] = ctx -> ctx.push(ctx.getGraphicsState().round(ctx.pop())); // ROUND[ab] + dispatch[0x6C + k] = ctx -> ctx.push(ctx.pop()); // NROUND[ab] + } + } + + // --- point movement -------------------------------------------------- + + private void installPointOps() + { + dispatch[0x0F] = this::doIsect; // ISECT + dispatch[0x2E] = ctx -> doMDAP(ctx, false); // MDAP[0] no round + dispatch[0x2F] = ctx -> doMDAP(ctx, true); // MDAP[1] round + dispatch[0x3E] = ctx -> doMIAP(ctx, false); // MIAP[0] no round + dispatch[0x3F] = ctx -> doMIAP(ctx, true); // MIAP[1] round + cut-in + dispatch[0x3A] = ctx -> doMSIRP(ctx, false); // MSIRP[0] + dispatch[0x3B] = ctx -> doMSIRP(ctx, true); // MSIRP[1] set rp0 + dispatch[0x3C] = this::doAlignRp; // ALIGNRP + dispatch[0x27] = this::doAlignPts; // ALIGNPTS + dispatch[0x29] = this::doUtp; // UTP + dispatch[0x38] = this::doShpix; // SHPIX + dispatch[0x32] = ctx -> doShp(ctx, false); // SHP[0] rp2/zp1 + dispatch[0x33] = ctx -> doShp(ctx, true); // SHP[1] rp1/zp0 + dispatch[0x34] = ctx -> doShc(ctx, false); // SHC[0] + dispatch[0x35] = ctx -> doShc(ctx, true); // SHC[1] + dispatch[0x36] = ctx -> doShz(ctx, false); // SHZ[0] + dispatch[0x37] = ctx -> doShz(ctx, true); // SHZ[1] + for (int op = 0xC0; op <= 0xDF; op++) + { + final int code = op; + dispatch[op] = ctx -> doMDRP(ctx, code); // MDRP[abcde] + } + for (int op = 0xE0; op <= 0xFF; op++) + { + final int code = op; + dispatch[op] = ctx -> doMIRP(ctx, code); // MIRP[abcde] + } + } + + /** + * ISECT: moves a point to the intersection of line A (a0,a1 in zp1) and line B (b0,b1 in zp0). + * Mirrors FreeType's Ins_ISECT, including the parallel-lines fallback to the four-point average. + */ + private void doIsect(ExecutionContext ctx) + { + GraphicsState gs = ctx.getGraphicsState(); + int b1 = ctx.pop(); + int b0 = ctx.pop(); + int a1 = ctx.pop(); + int a0 = ctx.pop(); + int point = ctx.pop(); + Zone za = ctx.getZone(gs.getZp1()); + Zone zb = ctx.getZone(gs.getZp0()); + Zone zp = ctx.getZone(gs.getZp2()); + + int a0x = za.getCurrentX()[a0]; + int a0y = za.getCurrentY()[a0]; + int dax = za.getCurrentX()[a1] - a0x; + int day = za.getCurrentY()[a1] - a0y; + int b0x = zb.getCurrentX()[b0]; + int b0y = zb.getCurrentY()[b0]; + int dbx = zb.getCurrentX()[b1] - b0x; + int dby = zb.getCurrentY()[b1] - b0y; + int dx = b0x - a0x; + int dy = b0y - a0y; + + int discriminant = Fixed.mulDiv(dax, -dby, 0x40) + Fixed.mulDiv(day, dbx, 0x40); + int dotproduct = Fixed.mulDiv(dax, dbx, 0x40) + Fixed.mulDiv(day, dby, 0x40); + + // reject grazing intersections of nearly parallel lines, as FreeType does + if (Math.abs((long) discriminant * 0x40) > Math.abs((long) dotproduct)) + { + int val = Fixed.mulDiv(dx, -dby, 0x40) + Fixed.mulDiv(dy, dbx, 0x40); + zp.getCurrentX()[point] = a0x + Fixed.mulDiv(val, dax, discriminant); + zp.getCurrentY()[point] = a0y + Fixed.mulDiv(val, day, discriminant); + } + else + { + // parallel: average of the four line points + zp.getCurrentX()[point] = (a0x + za.getCurrentX()[a1] + b0x + zb.getCurrentX()[b1]) / 2 / 2; + zp.getCurrentY()[point] = (a0y + za.getCurrentY()[a1] + b0y + zb.getCurrentY()[b1]) / 2 / 2; + } + zp.getTouchedX()[point] = true; + zp.getTouchedY()[point] = true; + } + + private void doMDAP(ExecutionContext ctx, boolean round) + { + GraphicsState gs = ctx.getGraphicsState(); + int point = ctx.pop(); + Zone zone = ctx.getZone(gs.getZp0()); + int cur = ctx.project(zone.getCurrentX()[point], zone.getCurrentY()[point]); + int distance = round ? gs.round(cur) : cur; + ctx.movePoint(zone, point, distance - cur); + gs.setRp0(point); + gs.setRp1(point); + } + + private void doMIAP(ExecutionContext ctx, boolean round) + { + GraphicsState gs = ctx.getGraphicsState(); + int cvtIndex = ctx.pop(); + int point = ctx.pop(); + Zone zone = ctx.getZone(gs.getZp0()); + int[] cvt = ctx.getControlValues(); + int value = cvtIndex >= 0 && cvtIndex < cvt.length ? cvt[cvtIndex] : 0; + + if (gs.getZp0() == 0) + { + // twilight point: establish its position from the control value along the projection + UnitVector pv = gs.getProjectionVector(); + zone.getOriginalX()[point] = Fixed.mul14(value, pv.getX()); + zone.getOriginalY()[point] = Fixed.mul14(value, pv.getY()); + zone.getCurrentX()[point] = zone.getOriginalX()[point]; + zone.getCurrentY()[point] = zone.getOriginalY()[point]; + } + int cur = ctx.project(zone.getCurrentX()[point], zone.getCurrentY()[point]); + if (round) + { + if (Math.abs(value - cur) > gs.getControlValueCutIn()) + { + value = cur; + } + value = gs.round(value); + } + ctx.movePoint(zone, point, value - cur); + gs.setRp0(point); + gs.setRp1(point); + } + + private void doMSIRP(ExecutionContext ctx, boolean setRp0) + { + GraphicsState gs = ctx.getGraphicsState(); + int distance = ctx.pop(); + int point = ctx.pop(); + Zone zp1 = ctx.getZone(gs.getZp1()); + Zone zp0 = ctx.getZone(gs.getZp0()); + int rp0 = gs.getRp0(); + int curDist = ctx.projectedDistance(zp1, point, zp0, rp0); + ctx.movePoint(zp1, point, distance - curDist); + gs.setRp1(rp0); + gs.setRp2(point); + if (setRp0) + { + gs.setRp0(point); + } + } + + private void doAlignRp(ExecutionContext ctx) + { + GraphicsState gs = ctx.getGraphicsState(); + Zone zp1 = ctx.getZone(gs.getZp1()); + Zone zp0 = ctx.getZone(gs.getZp0()); + int rp0 = gs.getRp0(); + forEachLoopPoint(ctx, point -> + { + int dist = ctx.projectedDistance(zp1, point, zp0, rp0); + ctx.movePoint(zp1, point, -dist); + }); + } + + private void doAlignPts(ExecutionContext ctx) + { + GraphicsState gs = ctx.getGraphicsState(); + int p2 = ctx.pop(); + int p1 = ctx.pop(); + Zone zp1 = ctx.getZone(gs.getZp1()); + Zone zp0 = ctx.getZone(gs.getZp0()); + int distance = ctx.projectedDistance(zp0, p1, zp1, p2); + // move both points to the midpoint of their projected positions + ctx.movePoint(zp1, p2, distance / 2); + ctx.movePoint(zp0, p1, -(distance - distance / 2)); + } + + private void doUtp(ExecutionContext ctx) + { + GraphicsState gs = ctx.getGraphicsState(); + Zone zone = ctx.getZone(gs.getZp0()); + UnitVector fv = gs.getFreedomVector(); + int point = ctx.pop(); + if (fv.getX() != 0) + { + zone.getTouchedX()[point] = false; + } + if (fv.getY() != 0) + { + zone.getTouchedY()[point] = false; + } + } + + private void doShpix(ExecutionContext ctx) + { + GraphicsState gs = ctx.getGraphicsState(); + int amount = ctx.pop(); + Zone zp2 = ctx.getZone(gs.getZp2()); + forEachLoopPoint(ctx, point -> ctx.movePoint(zp2, point, amount)); + } + + private void doShp(ExecutionContext ctx, boolean useRp1) + { + GraphicsState gs = ctx.getGraphicsState(); + int ref = referencePoint(gs, useRp1); + Zone refZone = referenceZone(ctx, useRp1); + int shift = referenceShift(ctx, refZone, ref); + Zone zp2 = ctx.getZone(gs.getZp2()); + forEachLoopPoint(ctx, point -> + { + if (!(refZone == zp2 && point == ref)) + { + ctx.movePoint(zp2, point, shift); + } + }); + } + + private void doShc(ExecutionContext ctx, boolean useRp1) + { + GraphicsState gs = ctx.getGraphicsState(); + int ref = referencePoint(gs, useRp1); + Zone refZone = referenceZone(ctx, useRp1); + int shift = referenceShift(ctx, refZone, ref); + int contour = ctx.pop(); + Zone zp2 = ctx.getZone(gs.getZp2()); + int[] ends = zp2.getContourEnds(); + if (contour < 0 || contour >= ends.length) + { + return; + } + int start = contour == 0 ? 0 : ends[contour - 1] + 1; + for (int i = start; i <= ends[contour]; i++) + { + // FreeType's SHC does not move the reference point itself (it has already moved) + if (!(refZone == zp2 && i == ref)) + { + ctx.movePoint(zp2, i, shift); + } + } + } + + private void doShz(ExecutionContext ctx, boolean useRp1) + { + GraphicsState gs = ctx.getGraphicsState(); + int ref = referencePoint(gs, useRp1); + Zone refZone = referenceZone(ctx, useRp1); + int shift = referenceShift(ctx, refZone, ref); + int zoneNumber = ctx.pop(); + Zone zone = ctx.getZone(zoneNumber); + for (int i = 0; i < zone.getPointCount(); i++) + { + if (!(refZone == zone && i == ref)) + { + ctx.movePoint(zone, i, shift); + } + } + } + + private static int referencePoint(GraphicsState gs, boolean useRp1) + { + return useRp1 ? gs.getRp1() : gs.getRp2(); + } + + private static Zone referenceZone(ExecutionContext ctx, boolean useRp1) + { + GraphicsState gs = ctx.getGraphicsState(); + return ctx.getZone(useRp1 ? gs.getZp0() : gs.getZp1()); + } + + /** The projected distance the reference point (rp1 in zp0, or rp2 in zp1) has been moved. */ + private static int referenceShift(ExecutionContext ctx, Zone refZone, int ref) + { + return ctx.project(refZone.getCurrentX()[ref] - refZone.getOriginalX()[ref], + refZone.getCurrentY()[ref] - refZone.getOriginalY()[ref]); + } + + private void doMDRP(ExecutionContext ctx, int op) + { + GraphicsState gs = ctx.getGraphicsState(); + int flags = op & 0x1F; + boolean setRp0 = (flags & 0x10) != 0; + boolean useMin = (flags & 0x08) != 0; + boolean round = (flags & 0x04) != 0; + int point = ctx.pop(); + Zone zp1 = ctx.getZone(gs.getZp1()); + Zone zp0 = ctx.getZone(gs.getZp0()); + int rp0 = gs.getRp0(); + + int orgDist = ctx.dualProjectedDistance(zp1, point, zp0, rp0); + orgDist = applySingleWidth(gs, orgDist); + int distance = round ? gs.round(orgDist) : orgDist; + distance = applyMinimumDistance(gs, useMin, orgDist, distance); + + int curDist = ctx.projectedDistance(zp1, point, zp0, rp0); + ctx.movePoint(zp1, point, distance - curDist); + gs.setRp1(rp0); + gs.setRp2(point); + if (setRp0) + { + gs.setRp0(point); + } + } + + private void doMIRP(ExecutionContext ctx, int op) + { + GraphicsState gs = ctx.getGraphicsState(); + int flags = op & 0x1F; + boolean setRp0 = (flags & 0x10) != 0; + boolean useMin = (flags & 0x08) != 0; + boolean round = (flags & 0x04) != 0; + // the CVT entry number is on top of the stack, the point number below it + int cvtIndex = ctx.pop(); + int point = ctx.pop(); + int[] cvt = ctx.getControlValues(); + int cvtValue = cvtIndex >= 0 && cvtIndex < cvt.length ? cvt[cvtIndex] : 0; + cvtValue = applySingleWidth(gs, cvtValue); + + Zone zp1 = ctx.getZone(gs.getZp1()); + Zone zp0 = ctx.getZone(gs.getZp0()); + int rp0 = gs.getRp0(); + int orgDist = ctx.dualProjectedDistance(zp1, point, zp0, rp0); + + // auto-flip the control value to match the sign of the original distance + if (gs.isAutoFlip() && (orgDist ^ cvtValue) < 0) + { + cvtValue = -cvtValue; + } + int distance; + if (round) + { + // the control value cut-in only applies when both points are in the same zone + if (gs.getZp0() == gs.getZp1() + && Math.abs(cvtValue - orgDist) > gs.getControlValueCutIn()) + { + cvtValue = orgDist; + } + distance = gs.round(cvtValue); + } + else + { + distance = cvtValue; + } + distance = applyMinimumDistance(gs, useMin, orgDist, distance); + + int curDist = ctx.projectedDistance(zp1, point, zp0, rp0); + ctx.movePoint(zp1, point, distance - curDist); + gs.setRp1(rp0); + gs.setRp2(point); + if (setRp0) + { + gs.setRp0(point); + } + } + + private static int applySingleWidth(GraphicsState gs, int distance) + { + if (Math.abs(distance - gs.getSingleWidthValue()) < gs.getSingleWidthCutIn()) + { + return distance >= 0 ? gs.getSingleWidthValue() : -gs.getSingleWidthValue(); + } + return distance; + } + + private static int applyMinimumDistance(GraphicsState gs, boolean useMin, int orgDist, + int distance) + { + if (!useMin) + { + return distance; + } + int md = gs.getMinimumDistance(); + if (orgDist >= 0) + { + return distance < md ? md : distance; + } + return distance > -md ? -md : distance; + } + + // --- interpolation --------------------------------------------------- + + private void installInterpolationOps() + { + dispatch[0x30] = ctx -> doIup(ctx, false); // IUP[0] y + dispatch[0x31] = ctx -> doIup(ctx, true); // IUP[1] x + dispatch[0x39] = this::doIp; // IP + } + + private void doIup(ExecutionContext ctx, boolean xAxis) + { + // record that IUP ran on this axis; under backward-compatibility, once both axes are done the + // glyph is frozen against further y moves (see ExecutionContext.movePoint) + if (xAxis) + { + ctx.setIupxCalled(); + } + else + { + ctx.setIupyCalled(); + } + // IUP always operates on the glyph zone, directly on the x or y coordinate + Zone zone = ctx.getZone(1); + int[] cur = xAxis ? zone.getCurrentX() : zone.getCurrentY(); + int[] org = xAxis ? zone.getOriginalX() : zone.getOriginalY(); + boolean[] touched = xAxis ? zone.getTouchedX() : zone.getTouchedY(); + int[] ends = zone.getContourEnds(); + int start = 0; + for (int end : ends) + { + interpolateContour(cur, org, touched, start, end); + start = end + 1; + } + } + + private static void interpolateContour(int[] cur, int[] org, boolean[] touched, int start, + int end) + { + if (end < start) + { + return; + } + int firstTouched = -1; + int touchedCount = 0; + for (int i = start; i <= end; i++) + { + if (touched[i]) + { + if (firstTouched < 0) + { + firstTouched = i; + } + touchedCount++; + } + } + if (touchedCount == 0) + { + return; + } + if (touchedCount == 1) + { + int delta = cur[firstTouched] - org[firstTouched]; + if (delta != 0) + { + for (int i = start; i <= end; i++) + { + if (i != firstTouched) + { + cur[i] = org[i] + delta; + } + } + } + return; + } + // walk the contour cyclically, interpolating the untouched run between each touched pair + int t1 = firstTouched; + int seen = 0; + for (int step = 1; step <= end - start + 1 && seen < touchedCount; step++) + { + int i = start + (firstTouched - start + step) % (end - start + 1); + if (touched[i]) + { + int u = t1 + 1 > end ? start : t1 + 1; + while (u != i) + { + interpolatePoint(cur, org, t1, i, u); + u = u + 1 > end ? start : u + 1; + } + t1 = i; + seen++; + } + } + } + + private static void interpolatePoint(int[] cur, int[] org, int t1, int t2, int u) + { + int orgLo; + int orgHi; + int curLo; + int curHi; + if (org[t1] <= org[t2]) + { + orgLo = org[t1]; + curLo = cur[t1]; + orgHi = org[t2]; + curHi = cur[t2]; + } + else + { + orgLo = org[t2]; + curLo = cur[t2]; + orgHi = org[t1]; + curHi = cur[t1]; + } + if (org[u] <= orgLo) + { + cur[u] = org[u] + (curLo - orgLo); + } + else if (org[u] >= orgHi) + { + cur[u] = org[u] + (curHi - orgHi); + } + else if (orgHi == orgLo) + { + cur[u] = org[u] + (curLo - orgLo); + } + else + { + cur[u] = curLo + Fixed.mulDiv(org[u] - orgLo, curHi - curLo, orgHi - orgLo); + } + } + + private void doIp(ExecutionContext ctx) + { + GraphicsState gs = ctx.getGraphicsState(); + Zone z0 = ctx.getZone(gs.getZp0()); + Zone z1 = ctx.getZone(gs.getZp1()); + Zone z2 = ctx.getZone(gs.getZp2()); + int rp1 = gs.getRp1(); + int rp2 = gs.getRp2(); + // Measure the original positions in unscaled font units (FreeType's orus) so the interpolation + // ratio keeps full precision; the scaled F26Dot6 originals round each coordinate and can shift + // an interpolated point by a unit, which a later rounding opcode then amplifies to a whole pixel. + // Exception: twilight-zone points have no font-unit source, so their unscaled coordinates are + // (0,0); using them would collapse every original distance to zero. When any zone here is the + // twilight zone, FreeType measures the scaled originals instead, so we do the same. + boolean twilight = gs.getZp0() == 0 || gs.getZp1() == 0 || gs.getZp2() == 0; + int curRp1 = ctx.project(z0.getCurrentX()[rp1], z0.getCurrentY()[rp1]); + int orgRp1 = twilight ? ctx.dualProject(z0.getOriginalX()[rp1], z0.getOriginalY()[rp1]) + : ctx.dualProject(z0.getUnscaledX()[rp1], z0.getUnscaledY()[rp1]); + int curRp2 = ctx.project(z1.getCurrentX()[rp2], z1.getCurrentY()[rp2]); + int orgRp2 = twilight ? ctx.dualProject(z1.getOriginalX()[rp2], z1.getOriginalY()[rp2]) + : ctx.dualProject(z1.getUnscaledX()[rp2], z1.getUnscaledY()[rp2]); + int orgRange = orgRp2 - orgRp1; + int curRange = curRp2 - curRp1; + forEachLoopPoint(ctx, point -> + { + int orgP = twilight ? ctx.dualProject(z2.getOriginalX()[point], z2.getOriginalY()[point]) + : ctx.dualProject(z2.getUnscaledX()[point], z2.getUnscaledY()[point]); + int curP = ctx.project(z2.getCurrentX()[point], z2.getCurrentY()[point]); + int newP; + if (orgRange == 0) + { + newP = curRp1 + (orgP - orgRp1); + } + else + { + newP = curRp1 + Fixed.mulDiv(orgP - orgRp1, curRange, orgRange); + } + ctx.movePoint(z2, point, newP - curP); + }); + } + + // --- measurement ----------------------------------------------------- + + private void installMeasureOps() + { + dispatch[0x46] = ctx -> doGc(ctx, false); // GC[0] current + dispatch[0x47] = ctx -> doGc(ctx, true); // GC[1] original + dispatch[0x48] = this::doScfs; // SCFS + dispatch[0x49] = ctx -> doMd(ctx, false); // MD[0] grid-fitted + dispatch[0x4A] = ctx -> doMd(ctx, true); // MD[1] original + } + + private void doGc(ExecutionContext ctx, boolean original) + { + GraphicsState gs = ctx.getGraphicsState(); + Zone zone = ctx.getZone(gs.getZp2()); + int point = ctx.pop(); + if (original) + { + ctx.push(ctx.dualProject(zone.getOriginalX()[point], zone.getOriginalY()[point])); + } + else + { + ctx.push(ctx.project(zone.getCurrentX()[point], zone.getCurrentY()[point])); + } + } + + private void doScfs(ExecutionContext ctx) + { + GraphicsState gs = ctx.getGraphicsState(); + int value = ctx.pop(); + int point = ctx.pop(); + Zone zone = ctx.getZone(gs.getZp2()); + int cur = ctx.project(zone.getCurrentX()[point], zone.getCurrentY()[point]); + ctx.movePoint(zone, point, value - cur); + } + + private void doMd(ExecutionContext ctx, boolean original) + { + GraphicsState gs = ctx.getGraphicsState(); + int p2 = ctx.pop(); + int p1 = ctx.pop(); + Zone zp0 = ctx.getZone(gs.getZp0()); + Zone zp1 = ctx.getZone(gs.getZp1()); + // FreeType measures project(zp0[p1] - zp1[p2]); p1 is the deeper operand, p2 the top + if (original) + { + ctx.push(ctx.dualProjectedDistance(zp0, p1, zp1, p2)); + } + else + { + ctx.push(ctx.projectedDistance(zp0, p1, zp1, p2)); + } + } + + // --- delta exceptions ------------------------------------------------ + + private void installDeltaOps() + { + dispatch[0x5D] = ctx -> doDeltaP(ctx, 0); // DELTAP1 + dispatch[0x71] = ctx -> doDeltaP(ctx, 1); // DELTAP2 + dispatch[0x72] = ctx -> doDeltaP(ctx, 2); // DELTAP3 + dispatch[0x73] = ctx -> doDeltaC(ctx, 0); // DELTAC1 + dispatch[0x74] = ctx -> doDeltaC(ctx, 1); // DELTAC2 + dispatch[0x75] = ctx -> doDeltaC(ctx, 2); // DELTAC3 + } + + private void doDeltaP(ExecutionContext ctx, int band) + { + GraphicsState gs = ctx.getGraphicsState(); + Zone zone = ctx.getZone(gs.getZp0()); + int n = ctx.pop(); + for (int i = 0; i < n; i++) + { + int point = ctx.pop(); + int arg = ctx.pop(); + if (deltaTargetPpem(gs, arg, band) == ctx.getPpem() + && deltaPointAllowed(ctx, zone, point)) + { + ctx.movePoint(zone, point, decodeDelta(arg & 0x0F, gs.getDeltaShift())); + } + } + } + + /** + * Backward-compatibility (v40 grayscale) gate for DELTAP: once IUP has run the delta is dropped, + * and before IUP it is applied only to points already touched in y (or, for composites, when the + * freedom vector has a y component). Outside backward-compatibility mode the delta always applies. + * This keeps DELTAP from nudging untouched points off their interpolated grayscale positions. + */ + private static boolean deltaPointAllowed(ExecutionContext ctx, Zone zone, int point) + { + if (!ctx.isBackwardCompatibility()) + { + return true; + } + if (ctx.isIupDone()) + { + return false; + } + boolean touchedY = point >= 0 && point < zone.getTouchedY().length + && zone.getTouchedY()[point]; + return touchedY + || (ctx.isComposite() && ctx.getGraphicsState().getFreedomVector().getY() != 0); + } + + private void doDeltaC(ExecutionContext ctx, int band) + { + GraphicsState gs = ctx.getGraphicsState(); + int[] cvt = ctx.getControlValues(); + int n = ctx.pop(); + for (int i = 0; i < n; i++) + { + int cvtIndex = ctx.pop(); + int arg = ctx.pop(); + if (deltaTargetPpem(gs, arg, band) == ctx.getPpem() && cvtIndex >= 0 + && cvtIndex < cvt.length) + { + cvt[cvtIndex] += decodeDelta(arg & 0x0F, gs.getDeltaShift()); + } + } + } + + private static int deltaTargetPpem(GraphicsState gs, int arg, int band) + { + return ((arg >> 4) & 0x0F) + gs.getDeltaBase() + band * 16; + } + + private static int decodeDelta(int steps, int deltaShift) + { + int relative = steps < 8 ? steps - 8 : steps - 7; // 0..15 -> -8..-1, 1..8 + int unit = Fixed.ONE >> deltaShift; // 1 / 2^deltaShift of a pixel + return relative * unit; + } + + // --- flip and scan-conversion ---------------------------------------- + + private void installFlipOps() + { + dispatch[0x4D] = ctx -> ctx.getGraphicsState().setAutoFlip(true); // FLIPON + dispatch[0x4E] = ctx -> ctx.getGraphicsState().setAutoFlip(false); // FLIPOFF + dispatch[0x80] = ctx -> // FLIPPT + { + boolean[] onCurve = ctx.getZone(1).getOnCurve(); + forEachLoopPoint(ctx, point -> onCurve[point] = !onCurve[point]); + }; + dispatch[0x81] = ctx -> flipRange(ctx, true); // FLIPRGON + dispatch[0x82] = ctx -> flipRange(ctx, false); // FLIPRGOFF + dispatch[0x85] = ctx -> ctx.getGraphicsState().setScanControl(ctx.pop()); // SCANCTRL + dispatch[0x8D] = ctx -> ctx.getGraphicsState().setScanType(ctx.pop()); // SCANTYPE + dispatch[0x8E] = ctx -> // INSTCTRL + { + int selector = ctx.pop(); + int value = ctx.pop(); + if (selector == 3) + { + // native-ClearType fonts use INSTCTRL(L,3) to waive backward compatibility and program + // points to the grid directly; L==4 turns the v40 movement restrictions off + ctx.setBackwardCompatibility(value != 4); + } + else + { + ctx.getGraphicsState().setInstructControl(value & selector); + } + }; + } + + private static void flipRange(ExecutionContext ctx, boolean onCurve) + { + boolean[] flags = ctx.getZone(1).getOnCurve(); + int high = ctx.pop(); + int low = ctx.pop(); + for (int i = low; i <= high && i < flags.length; i++) + { + if (i >= 0) + { + flags[i] = onCurve; + } + } + } + + // --- loop helper ----------------------------------------------------- + + @FunctionalInterface + private interface PointConsumer + { + void accept(int point); + } + + /** Processes the graphics-state loop count of points, popping one per iteration, then resets the + * loop counter to 1. */ + private static void forEachLoopPoint(ExecutionContext ctx, PointConsumer consumer) + { + int loop = ctx.getGraphicsState().getLoop(); + for (int i = 0; i < loop; i++) + { + consumer.accept(ctx.pop()); + } + ctx.getGraphicsState().setLoop(1); + } + + // --- handler helpers ------------------------------------------------- + + @FunctionalInterface + private interface IntBinaryOp + { + int apply(int a, int b); + } + + private static void binary(ExecutionContext ctx, IntBinaryOp op) + { + int b = ctx.pop(); + int a = ctx.pop(); + ctx.push(op.apply(a, b)); + } + + private static int bool(boolean value) + { + return value ? 1 : 0; + } + + private static OpHandler roundState(int state) + { + return ctx -> ctx.getGraphicsState().setRoundState(state); + } + + private static int read(int[] array, int index, String name) + { + if (index < 0 || index >= array.length) + { + throw new HintingException(name + " index out of range: " + index); + } + return array[index]; + } + + private static void write(int[] array, int index, int value, String name) + { + if (index < 0 || index >= array.length) + { + throw new HintingException(name + " index out of range: " + index); + } + array[index] = value; + } +} diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/UnitVector.java b/fontbox/src/main/java/org/apache/fontbox/ttf/UnitVector.java new file mode 100644 index 00000000000..435187654b9 --- /dev/null +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/UnitVector.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +/** + * A 2D unit vector in F2Dot14 fixed point, used for the projection, freedom and dual-projection + * vectors of the TrueType graphics state. Kept as a mutable class (not {@code Point2D.Float}) so the + * interpreter can stay in integer math. Because they are mutable, {@link GraphicsState#copy()} + * deep-copies them, so a per-glyph clone cannot write through to the saved post-{@code prep} template. + *

+ * Named after FreeType's {@code FT_UnitVector}, and like it the unit length is a convention rather than + * an enforced invariant: {@code SPVFS} and {@code SFVFS} write whatever the font pushed on the stack, + * which a malformed font need not have normalized. + * + * @author Apache PDFBox + */ +class UnitVector +{ + private int x; + private int y; + + /** + * @param x the x component in F2Dot14 + * @param y the y component in F2Dot14 + */ + public UnitVector(int x, int y) + { + this.x = x; + this.y = y; + } + + /** + * @return the x axis unit vector (1, 0) + */ + public static UnitVector xAxis() + { + return new UnitVector(Fixed.ONE_F2DOT14, 0); + } + + /** + * @return the y axis unit vector (0, 1) + */ + public static UnitVector yAxis() + { + return new UnitVector(0, Fixed.ONE_F2DOT14); + } + + /** + * Builds a unit vector in F2Dot14 from a coordinate delta. A zero-length delta falls back to the + * x axis. (The square-root normalization is the one place the interpreter steps outside integer + * math; it only affects a direction vector, and is verified by the golden tests.) + * + * @param dx the x delta + * @param dy the y delta + * @return the normalized unit vector + */ + public static UnitVector normalize(int dx, int dy) + { + double length = Math.hypot(dx, dy); + if (length == 0) + { + return xAxis(); + } + int ux = (int) Math.round(dx / length * Fixed.ONE_F2DOT14); + int uy = (int) Math.round(dy / length * Fixed.ONE_F2DOT14); + return new UnitVector(ux, uy); + } + + /** + * @return this vector rotated 90 degrees counter-clockwise, i.e. {@code (-y, x)} + */ + public UnitVector perpendicular() + { + return new UnitVector(-y, x); + } + + /** + * @return the x component in F2Dot14 + */ + public int getX() + { + return x; + } + + /** + * @return the y component in F2Dot14 + */ + public int getY() + { + return y; + } + + /** + * @param x the x component in F2Dot14 + * @param y the y component in F2Dot14 + */ + public void set(int x, int y) + { + this.x = x; + this.y = y; + } + + /** + * @return an independent copy of this vector + */ + public UnitVector copy() + { + return new UnitVector(x, y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (!(obj instanceof UnitVector)) + { + return false; + } + UnitVector other = (UnitVector) obj; + return x == other.x && y == other.y; + } + + @Override + public int hashCode() + { + return 31 * x + y; + } + + @Override + public String toString() + { + return "UnitVector(" + x + ", " + y + ")"; + } +} diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/Zone.java b/fontbox/src/main/java/org/apache/fontbox/ttf/Zone.java new file mode 100644 index 00000000000..60d7aea9e21 --- /dev/null +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/Zone.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import java.util.Arrays; + +/** + * A set of points the interpreter can manipulate - either zone 0 (the twilight zone, holding phantom + * reference points) or zone 1 (the glyph's own outline points plus its appended phantom points). Each + * point has its scaled-but-unhinted "original" position and a "current" position the bytecode moves, + * both in F26Dot6, plus per-axis touch flags used by interpolation. + * + * @author Apache PDFBox + */ +class Zone +{ + private final int[] currentX; + private final int[] currentY; + private final int[] originalX; + private final int[] originalY; + private final int[] unscaledX; + private final int[] unscaledY; + private final boolean[] touchedX; + private final boolean[] touchedY; + private final boolean[] onCurve; + private final int[] contourEnds; + + /** + * Allocates a zone of the given size. + * + * @param pointCount number of points (including any phantom points) + * @param contourCount number of contours (0 for the twilight zone) + */ + public Zone(int pointCount, int contourCount) + { + currentX = new int[pointCount]; + currentY = new int[pointCount]; + originalX = new int[pointCount]; + originalY = new int[pointCount]; + unscaledX = new int[pointCount]; + unscaledY = new int[pointCount]; + touchedX = new boolean[pointCount]; + touchedY = new boolean[pointCount]; + onCurve = new boolean[pointCount]; + contourEnds = new int[contourCount]; + } + + /** + * Zeroes every coordinate and flag. The twilight zone outlives a single program run - it is owned by + * the interpreter so values {@code prep} puts there survive into each glyph program - so it is reset + * rather than reallocated when the size changes, as FreeType does in {@code tt_size_run_prep}. + */ + public void reset() + { + Arrays.fill(currentX, 0); + Arrays.fill(currentY, 0); + Arrays.fill(originalX, 0); + Arrays.fill(originalY, 0); + Arrays.fill(unscaledX, 0); + Arrays.fill(unscaledY, 0); + Arrays.fill(touchedX, false); + Arrays.fill(touchedY, false); + Arrays.fill(onCurve, false); + Arrays.fill(contourEnds, 0); + } + + /** @return the number of points in this zone */ + public int getPointCount() + { + return currentX.length; + } + + /** @return the current (hinted) x coordinates in F26Dot6 */ + public int[] getCurrentX() + { + return currentX; + } + + /** @return the current (hinted) y coordinates in F26Dot6 */ + public int[] getCurrentY() + { + return currentY; + } + + /** @return the original (scaled, unhinted) x coordinates in F26Dot6 */ + public int[] getOriginalX() + { + return originalX; + } + + /** @return the original (scaled, unhinted) y coordinates in F26Dot6 */ + public int[] getOriginalY() + { + return originalY; + } + + /** + * @return the original unscaled x coordinates in font units. Interpolation and relative + * measurements use these for the ratio, matching FreeType's {@code orus}, because the unrounded + * font-unit values preserve precision the scaled F26Dot6 originals would lose. + */ + public int[] getUnscaledX() + { + return unscaledX; + } + + /** @return the original unscaled y coordinates in font units */ + public int[] getUnscaledY() + { + return unscaledY; + } + + /** @return per-point touch flags for the x axis */ + public boolean[] getTouchedX() + { + return touchedX; + } + + /** @return per-point touch flags for the y axis */ + public boolean[] getTouchedY() + { + return touchedY; + } + + /** @return per-point on-curve flags */ + public boolean[] getOnCurve() + { + return onCurve; + } + + /** @return the index of the last point of each contour */ + public int[] getContourEnds() + { + return contourEnds; + } +} diff --git a/fontbox/src/test/java/org/apache/fontbox/ttf/BytecodeStreamTest.java b/fontbox/src/test/java/org/apache/fontbox/ttf/BytecodeStreamTest.java new file mode 100644 index 00000000000..968463a9977 --- /dev/null +++ b/fontbox/src/test/java/org/apache/fontbox/ttf/BytecodeStreamTest.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +/** + * Tests for the {@link BytecodeStream} cursor, including bounds checking. + */ +class BytecodeStreamTest +{ + @Test + void testSequentialReads() + { + BytecodeStream s = new BytecodeStream(new byte[] { (byte) 0xB0, 0x05, (byte) 0xFF, 0x01 }); + assertTrue(s.hasNext()); + assertEquals(0xB0, s.nextByte()); + assertEquals(5, s.nextByte()); + // 0xFF01 as a signed word is negative + assertEquals((short) 0xFF01, s.nextWord()); + assertFalse(s.hasNext()); + } + + @Test + void testByteReadPastEndThrows() + { + BytecodeStream s = new BytecodeStream(new byte[] { 0x01 }); + assertEquals(1, s.nextByte()); + assertThrows(HintingException.class, s::nextByte); + } + + @Test + void testWordReadPastEndThrows() + { + // only one byte, but a word needs two + BytecodeStream s = new BytecodeStream(new byte[] { 0x01 }); + assertThrows(HintingException.class, s::nextWord); + } + + @Test + void testSeekOutOfRangeThrows() + { + BytecodeStream s = new BytecodeStream(new byte[] { 0x01, 0x02 }); + s.seek(2); // end position is valid + assertFalse(s.hasNext()); + assertThrows(HintingException.class, () -> s.seek(3)); + assertThrows(HintingException.class, () -> s.seek(-1)); + } + + @Test + void testInstructionStartTracking() + { + BytecodeStream s = new BytecodeStream(new byte[] { 0x10, 0x11, 0x12 }); + s.nextByte(); + s.markInstructionStart(); + assertEquals(1, s.instructionStart()); + assertEquals(0x11, s.nextByte()); + } +} diff --git a/fontbox/src/test/java/org/apache/fontbox/ttf/FixedTest.java b/fontbox/src/test/java/org/apache/fontbox/ttf/FixedTest.java new file mode 100644 index 00000000000..f42b533b3bc --- /dev/null +++ b/fontbox/src/test/java/org/apache/fontbox/ttf/FixedTest.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the {@link Fixed} fixed-point math used by the interpreter. + */ +class FixedTest +{ + @Test + void testIntRoundTrip() + { + for (int n = -1000; n <= 1000; n++) + { + assertEquals(n, Fixed.toInt(Fixed.fromInt(n)), "round-trip " + n); + } + assertEquals(64, Fixed.fromInt(1)); + assertEquals(-128, Fixed.fromInt(-2)); + } + + @Test + void testFloorCeilRound() + { + assertEquals(64, Fixed.floor(100)); // 1.5625px -> 1px + assertEquals(128, Fixed.ceil(100)); // -> 2px + assertEquals(64, Fixed.round(70)); // just above 1px rounds to 1px + assertEquals(128, Fixed.round(96)); // 1.5px rounds up to 2px + assertEquals(0, Fixed.round(31)); // just below half a pixel rounds to 0 + assertEquals(64, Fixed.round(32)); // exactly half rounds up + } + + @Test + void testMulDiv() + { + // 2.0 * 3.0 == 6.0 + assertEquals(Fixed.fromInt(6), Fixed.mul(Fixed.fromInt(2), Fixed.fromInt(3))); + // 6.0 / 2.0 == 3.0 + assertEquals(Fixed.fromInt(3), Fixed.div(Fixed.fromInt(6), Fixed.fromInt(2))); + // division by zero is defined as zero + assertEquals(0, Fixed.div(Fixed.fromInt(5), 0)); + // signed rounding + assertEquals(-Fixed.fromInt(6), Fixed.mul(Fixed.fromInt(-2), Fixed.fromInt(3))); + } + + @Test + void testMul14() + { + // multiplying by the F2Dot14 unit (1.0) is the identity + assertEquals(Fixed.fromInt(5), Fixed.mul14(Fixed.fromInt(5), Fixed.ONE_F2DOT14)); + // multiplying by 0.5 in F2Dot14 halves the value + assertEquals(Fixed.fromInt(5) / 2, Fixed.mul14(Fixed.fromInt(5), Fixed.ONE_F2DOT14 / 2)); + } + + @Test + void testScale() + { + // 1000 font units at 16 ppem with unitsPerEm 2048 == 500 subpixel units == 7.8125px + assertEquals(500, Fixed.scale(1000, 16, 2048)); + // a flat *64 would be wrong: it must depend on unitsPerEm + assertEquals(Fixed.fromInt(16), Fixed.scale(2048, 16, 2048)); // one em == ppem pixels + assertEquals(0, Fixed.scale(1234, 16, 0)); // guard against zero unitsPerEm + } +} diff --git a/fontbox/src/test/java/org/apache/fontbox/ttf/GraphicsStateTest.java b/fontbox/src/test/java/org/apache/fontbox/ttf/GraphicsStateTest.java new file mode 100644 index 00000000000..0ab52c91347 --- /dev/null +++ b/fontbox/src/test/java/org/apache/fontbox/ttf/GraphicsStateTest.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link GraphicsState} defaults, deep copy and per-glyph reset: a shallow copy would let a + * glyph write through to the saved post-{@code prep} template, and resetting too much or too little + * would discard or retain state the TrueType spec is specific about. + */ +class GraphicsStateTest +{ + @Test + void testDefaults() + { + GraphicsState gs = new GraphicsState(); + assertEquals(UnitVector.xAxis(), gs.getProjectionVector()); + assertEquals(UnitVector.xAxis(), gs.getFreedomVector()); + assertEquals(GraphicsState.ROUND_TO_GRID, gs.getRoundState()); + assertEquals(1, gs.getLoop()); + assertEquals(Fixed.ONE, gs.getMinimumDistance()); + assertEquals(17 * Fixed.ONE / 16, gs.getControlValueCutIn()); + assertEquals(9, gs.getDeltaBase()); + assertEquals(3, gs.getDeltaShift()); + assertEquals(1, gs.getZp0()); + assertEquals(0, gs.getRp0()); + assertTrue(gs.isAutoFlip()); + } + + @Test + void testDeepCopyIsIndependent() + { + GraphicsState original = new GraphicsState(); + GraphicsState clone = original.copy(); + + // the vector objects must not be shared (bug #2 guard) + assertNotSame(original.getFreedomVector(), clone.getFreedomVector()); + + // mutating the clone's vector in place must not touch the original + clone.getFreedomVector().set(0, Fixed.ONE_F2DOT14); + clone.setRp0(5); + clone.setRoundState(GraphicsState.ROUND_OFF); + + assertEquals(UnitVector.xAxis(), original.getFreedomVector()); + assertEquals(0, original.getRp0()); + assertEquals(GraphicsState.ROUND_TO_GRID, original.getRoundState()); + } + + @Test + void testResetForGlyphResetsOnlySpecMandatedFields() + { + GraphicsState gs = new GraphicsState(); + + // simulate state left behind by prep / a previous glyph + gs.getFreedomVector().set(0, Fixed.ONE_F2DOT14); + gs.getProjectionVector().set(0, Fixed.ONE_F2DOT14); + gs.setRp0(3); + gs.setRp1(4); + gs.setRp2(5); + gs.setZp0(0); + gs.setLoop(7); + // prep-configured fields that must survive a per-glyph reset + gs.setRoundState(GraphicsState.ROUND_OFF); + gs.setControlValueCutIn(999); + gs.setMinimumDistance(123); + gs.setDeltaBase(42); + + gs.resetForGlyph(); + + // reset to defaults + assertEquals(UnitVector.xAxis(), gs.getFreedomVector()); + assertEquals(UnitVector.xAxis(), gs.getProjectionVector()); + assertEquals(0, gs.getRp0()); + assertEquals(0, gs.getRp1()); + assertEquals(0, gs.getRp2()); + assertEquals(1, gs.getZp0()); + assertEquals(1, gs.getLoop()); + + // preserved from prep + assertEquals(GraphicsState.ROUND_OFF, gs.getRoundState()); + assertEquals(999, gs.getControlValueCutIn()); + assertEquals(123, gs.getMinimumDistance()); + assertEquals(42, gs.getDeltaBase()); + } +} diff --git a/fontbox/src/test/java/org/apache/fontbox/ttf/PointOpsTest.java b/fontbox/src/test/java/org/apache/fontbox/ttf/PointOpsTest.java new file mode 100644 index 00000000000..9c06b891b6b --- /dev/null +++ b/fontbox/src/test/java/org/apache/fontbox/ttf/PointOpsTest.java @@ -0,0 +1,343 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the point-moving opcodes: each builds a glyph zone, runs a short program + * through the interpreter, and asserts the resulting coordinates. Coordinates are in F26Dot6 (64 per + * pixel). Byte-exact agreement with FreeType is proven by {@link GoldenHintingTest}; these check the + * per-opcode logic. + */ +class PointOpsTest +{ + private static TrueTypeInterpreter interpreter() + { + return new TrueTypeInterpreter(256, 16, 16, 2048); + } + + /** Builds a single-contour glyph zone with the given x coordinates (original == current). */ + private static Zone lineZone(int... xs) + { + Zone zone = new Zone(xs.length, 1); + for (int i = 0; i < xs.length; i++) + { + zone.getOriginalX()[i] = xs[i]; + zone.getCurrentX()[i] = xs[i]; + // these synthetic tests work directly in device units, so the unscaled originals (used by + // IP for its interpolation ratio) mirror the scaled ones + zone.getUnscaledX()[i] = xs[i]; + } + zone.getContourEnds()[0] = xs.length - 1; + return zone; + } + + private static ExecutionContext context(TrueTypeInterpreter interp, Zone glyph) + { + ExecutionContext ctx = interp.newContext(new GraphicsState()); + ctx.setPpem(16); + ctx.setGlyphZone(glyph); + return ctx; + } + + @Test + void testProjectionAndMove() + { + ExecutionContext ctx = + new ExecutionContext(null, new GraphicsState(), 16, new int[0], null, new Zone(0, 0)); + Zone zone = lineZone(100, 0); + // default projection/freedom is the x axis: project returns the x coordinate + assertEquals(100, ctx.project(zone.getCurrentX()[0], zone.getCurrentY()[0])); + ctx.movePoint(zone, 0, 28); // move +28 along x + assertEquals(128, zone.getCurrentX()[0]); + assertTrue(zone.getTouchedX()[0]); + assertFalse(zone.getTouchedY()[0]); + } + + @Test + void testMdapRoundsToGrid() + { + TrueTypeInterpreter interp = interpreter(); + Zone zone = lineZone(100); // 1.5625px + ExecutionContext ctx = context(interp, zone); + // PUSHB[0] 0 ; MDAP[1] (round) + interp.run(ctx, new BytecodeStream(new byte[] { (byte) 0xB0, 0, 0x2F })); + assertEquals(128, zone.getCurrentX()[0]); // rounded to 2px + assertTrue(zone.getTouchedX()[0]); + } + + @Test + void testMdrpRelativeToRp0() + { + TrueTypeInterpreter interp = interpreter(); + Zone zone = lineZone(0, 100); // rp0 = point 0 at x=0, point 1 at 1.5625px + ExecutionContext ctx = context(interp, zone); + // PUSHB[0] 1 ; MDRP[round] (0xC4, round bit = 0x04) - grid-rounded distance from rp0 + interp.run(ctx, new BytecodeStream(new byte[] { (byte) 0xB0, 1, (byte) 0xC4 })); + assertEquals(128, zone.getCurrentX()[1]); // distance 100 rounds to 128 + } + + @Test + void testMdrpRoundAndMinimumDistanceFlagsAreDistinct() + { + // guards the flag encoding: round is bit 0x04, minimum-distance is bit 0x08 (they were once + // swapped). A small original distance (30) below the minimum (64): + TrueTypeInterpreter interp = interpreter(); + // round only (0xC4): 30 -> round(30) = 0, no minimum clamp + Zone roundZone = lineZone(0, 30); + interp.run(context(interp, roundZone), new BytecodeStream(new byte[] { (byte) 0xB0, 1, (byte) 0xC4 })); + assertEquals(0, roundZone.getCurrentX()[1]); + + // minimum-distance only (0xC8): no rounding, but clamp the distance up to the minimum (64) + Zone minZone = lineZone(0, 30); + interp.run(context(interp, minZone), new BytecodeStream(new byte[] { (byte) 0xB0, 1, (byte) 0xC8 })); + assertEquals(64, minZone.getCurrentX()[1]); + } + + @Test + void testMirpUsesControlValue() + { + TrueTypeInterpreter interp = interpreter(); + // raw cvt 256 at 16ppem / 2048 upem scales to 128 (2px) + interp.setControlValues(new int[] { 256 }); + interp.setPpem(16, 16); + Zone zone = lineZone(0, 100); + ExecutionContext ctx = interp.newContext(new GraphicsState()); + ctx.setPpem(16); + ctx.setGlyphZone(zone); + // PUSHB[1] 1 0 (point=1 pushed first, cvtIndex=0 on top) ; MIRP[round] (0xE4, round = 0x04) + interp.run(ctx, new BytecodeStream(new byte[] { (byte) 0xB1, 1, 0, (byte) 0xE4 })); + assertEquals(128, zone.getCurrentX()[1]); + } + + @Test + void testMsirpSetsExactDistance() + { + TrueTypeInterpreter interp = interpreter(); + Zone zone = lineZone(0, 100); + ExecutionContext ctx = context(interp, zone); + // PUSHB[1] 1 64 (point=1, distance=1px) ; MSIRP[0] (0x3A) + interp.run(ctx, new BytecodeStream(new byte[] { (byte) 0xB1, 1, 64, 0x3A })); + assertEquals(64, zone.getCurrentX()[1]); // exactly 1px from rp0 at x=0 + } + + @Test + void testAlignRp() + { + TrueTypeInterpreter interp = interpreter(); + Zone zone = lineZone(0, 100); // rp0 at 0 + ExecutionContext ctx = context(interp, zone); + // PUSHB[0] 1 ; ALIGNRP (0x3C) - align point 1 onto rp0 + interp.run(ctx, new BytecodeStream(new byte[] { (byte) 0xB0, 1, 0x3C })); + assertEquals(0, zone.getCurrentX()[1]); + } + + @Test + void testIupInterpolatesUntouched() + { + TrueTypeInterpreter interp = interpreter(); + // three points on one contour; the middle one is untouched + Zone zone = lineZone(0, 50, 100); + zone.getTouchedX()[0] = true; + zone.getTouchedX()[2] = true; + zone.getCurrentX()[2] = 120; // the right anchor was hinted +20 + ExecutionContext ctx = context(interp, zone); + // IUP[1] (x axis) + interp.run(ctx, new BytecodeStream(new byte[] { 0x31 })); + // p1 interpolates proportionally: 0 + 50*(120-0)/100 = 60 + assertEquals(60, zone.getCurrentX()[1]); + // touched anchors are never moved by IUP + assertEquals(0, zone.getCurrentX()[0]); + assertEquals(120, zone.getCurrentX()[2]); + } + + @Test + void testIupShiftsWhenSingleTouchedPoint() + { + TrueTypeInterpreter interp = interpreter(); + Zone zone = lineZone(0, 50, 100); + zone.getTouchedX()[1] = true; + zone.getCurrentX()[1] = 70; // the only touched point moved +20 + ExecutionContext ctx = context(interp, zone); + interp.run(ctx, new BytecodeStream(new byte[] { 0x31 })); // IUP[1] + // with one touched point, every other point shifts by the same delta (+20) + assertEquals(20, zone.getCurrentX()[0]); + assertEquals(120, zone.getCurrentX()[2]); + } + + @Test + void testIpInterpolatesBetweenReferencePoints() + { + TrueTypeInterpreter interp = interpreter(); + Zone zone = lineZone(0, 50, 100); + // set rp1=0, rp2=2, move the anchors, then IP point 1 + zone.getCurrentX()[2] = 120; + GraphicsState gs = new GraphicsState(); + gs.setRp1(0); + gs.setRp2(2); + ExecutionContext ctx = interp.newContext(gs); + ctx.setPpem(16); + ctx.setGlyphZone(zone); + // PUSHB[0] 1 ; IP (0x39) + interp.run(ctx, new BytecodeStream(new byte[] { (byte) 0xB0, 1, 0x39 })); + assertEquals(60, zone.getCurrentX()[1]); + } + + @Test + void testIpInterpolatesTwilightPointsUsingScaledOriginals() + { + // Regression (PDFBOX-3293): twilight-zone points have no font-unit source, so their unscaled + // coordinates are (0,0). Measuring IP's interpolation ratio against those zeros collapses every + // interpolated point onto the reference point. A prep program that builds the x-height control + // value this way then yields 0, flattening whole glyphs onto the baseline at small ppem (e.g. + // lowercase 'm' in a gasp-less Arial subset at 7ppem). For twilight points IP must measure the + // scaled originals instead, exactly as FreeType's Ins_IP does. + TrueTypeInterpreter interp = interpreter(); + GraphicsState gs = new GraphicsState(); + gs.setZp0(0); // all three zone pointers reference the twilight zone + gs.setZp1(0); + gs.setZp2(0); + gs.setRp1(0); + gs.setRp2(2); + ExecutionContext ctx = interp.newContext(gs); + ctx.setPpem(16); + Zone twilight = ctx.getTwilightZone(); + // anchors at originals 0 and 100; p2's current is stretched to 120. The unscaled coordinates + // stay (0,0) for every point, exactly as MIAP leaves freshly placed twilight points. + int[] xs = { 0, 50, 100 }; + for (int p = 0; p < xs.length; p++) + { + twilight.getOriginalX()[p] = xs[p]; + twilight.getCurrentX()[p] = xs[p]; + } + twilight.getCurrentX()[2] = 120; + // PUSHB[0] 1 ; IP (0x39) + interp.run(ctx, new BytecodeStream(new byte[] { (byte) 0xB0, 1, 0x39 })); + // interpolate by the scaled originals: 0 + 50*(120-0)/100 = 60 (the unscaled zeros would give 0) + assertEquals(60, twilight.getCurrentX()[1]); + } + + @Test + void testSvtcaSetsProjectionVector() + { + TrueTypeInterpreter interp = interpreter(); + ExecutionContext ctx = interp.newContext(new GraphicsState()); + // SVTCA[0] (y axis) ; GPV + interp.run(ctx, new BytecodeStream(new byte[] { 0x00, 0x0C })); + assertEquals(Fixed.ONE_F2DOT14, ctx.peek(0)); // pv.y + assertEquals(0, ctx.peek(1)); // pv.x + } + + @Test + void testRoundOpcode() + { + // PUSHB[0] 100 ; ROUND[0] (0x68) -> 128 under default round-to-grid + ExecutionContext ctx = interpreter().executeProgram(new byte[] { (byte) 0xB0, 100, 0x68 }, 16); + assertEquals(128, ctx.peek(0)); + } + + @Test + void testMdMeasuresSignedDistance() + { + // MD measures project(zp0[p1] - zp1[p2]); p1 is the deeper operand, p2 the top (FreeType sign). + TrueTypeInterpreter interp = interpreter(); + Zone zone = lineZone(0, 100); // point 0 at x=0, point 1 at x=100 + ExecutionContext ctx = context(interp, zone); + // PUSHB[1] 0 1 (p1=0 deeper, p2=1 top) ; MD[grid] (0x49) + interp.run(ctx, new BytecodeStream(new byte[] { (byte) 0xB1, 0, 1, 0x49 })); + assertEquals(-100, ctx.peek(0)); // x0 - x1 = -100 + } + + @Test + void testGcReadsProjectedCoordinate() + { + TrueTypeInterpreter interp = interpreter(); + Zone zone = lineZone(192); // 3px + ExecutionContext ctx = context(interp, zone); + // PUSHB[0] 0 ; GC[0] (0x46) current coordinate + interp.run(ctx, new BytecodeStream(new byte[] { (byte) 0xB0, 0, 0x46 })); + assertEquals(192, ctx.peek(0)); + } + + @Test + void testIsectMovesPointToLineIntersection() + { + TrueTypeInterpreter interp = interpreter(); + // p0 = the point to move; line A = horizontal y=64 (p1,p2); line B = vertical x=64 (p3,p4) + Zone zone = new Zone(5, 1); + setPoint(zone, 0, 0, 0); + setPoint(zone, 1, 0, 64); + setPoint(zone, 2, 128, 64); + setPoint(zone, 3, 64, 0); + setPoint(zone, 4, 64, 128); + ExecutionContext ctx = context(interp, zone); + // push p0,a0,a1,b0,b1 = 0 1 2 3 4 ; ISECT (0x0F) pops b1,b0,a1,a0,point + interp.run(ctx, new BytecodeStream( + new byte[] { (byte) 0xB4, 0, 1, 2, 3, 4, 0x0F })); + assertEquals(64, zone.getCurrentX()[0]); + assertEquals(64, zone.getCurrentY()[0]); + assertTrue(zone.getTouchedX()[0]); + assertTrue(zone.getTouchedY()[0]); + } + + // --- oracle-free invariant checks ------------------------------------ + + @Test + void testIupNeverMovesATouchedPoint() + { + // invariant: IUP leaves every already-touched point exactly where it is + TrueTypeInterpreter interp = interpreter(); + Zone zone = lineZone(0, 50, 100); + zone.getTouchedX()[1] = true; + zone.getCurrentX()[1] = 77; // a touched point at a deliberately off-grid position + ExecutionContext ctx = context(interp, zone); + interp.run(ctx, new BytecodeStream(new byte[] { 0x31 })); // IUP[x] + assertEquals(77, zone.getCurrentX()[1]); + } + + @Test + void testTouchedPointsLandOnGridUnderRoundToGrid() + { + // invariant: under an integer round state, a rounded (touched) point lands on a grid line + TrueTypeInterpreter interp = interpreter(); + Zone zone = lineZone(100, 150, 77); // off-grid positions + ExecutionContext ctx = context(interp, zone); // default round state is round-to-grid + // MDAP[1] each point (PUSHB[0] n ; MDAP[1]) + interp.run(ctx, new BytecodeStream(new byte[] { + (byte) 0xB0, 0, 0x2F, (byte) 0xB0, 1, 0x2F, (byte) 0xB0, 2, 0x2F })); + for (int i = 0; i < 3; i++) + { + assertTrue(zone.getTouchedX()[i], "point " + i + " should be touched"); + assertEquals(0, zone.getCurrentX()[i] % Fixed.ONE, "point " + i + " off the grid"); + } + } + + private static void setPoint(Zone zone, int i, int x, int y) + { + zone.getCurrentX()[i] = x; + zone.getCurrentY()[i] = y; + zone.getOriginalX()[i] = x; + zone.getOriginalY()[i] = y; + zone.getUnscaledX()[i] = x; + zone.getUnscaledY()[i] = y; + } +} diff --git a/fontbox/src/test/java/org/apache/fontbox/ttf/RoundStateTest.java b/fontbox/src/test/java/org/apache/fontbox/ttf/RoundStateTest.java new file mode 100644 index 00000000000..00c7161e616 --- /dev/null +++ b/fontbox/src/test/java/org/apache/fontbox/ttf/RoundStateTest.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Table-driven tests of the {@link GraphicsState#round(int)} state machine across all round states. + */ +class RoundStateTest +{ + private static int round(int state, int distance) + { + GraphicsState gs = new GraphicsState(); + gs.setRoundState(state); + return gs.round(distance); + } + + @Test + void testRoundToGrid() + { + assertEquals(128, round(GraphicsState.ROUND_TO_GRID, 100)); // 1.56px -> 2px + assertEquals(64, round(GraphicsState.ROUND_TO_GRID, 95)); // 1.48px -> 1px + assertEquals(128, round(GraphicsState.ROUND_TO_GRID, 96)); // 1.5px rounds up + assertEquals(-128, round(GraphicsState.ROUND_TO_GRID, -100)); + } + + @Test + void testRoundDownAndUp() + { + assertEquals(64, round(GraphicsState.ROUND_DOWN_TO_GRID, 100)); + assertEquals(64, round(GraphicsState.ROUND_DOWN_TO_GRID, 127)); + assertEquals(128, round(GraphicsState.ROUND_UP_TO_GRID, 65)); + assertEquals(64, round(GraphicsState.ROUND_UP_TO_GRID, 64)); + } + + @Test + void testRoundOff() + { + assertEquals(100, round(GraphicsState.ROUND_OFF, 100)); + assertEquals(-37, round(GraphicsState.ROUND_OFF, -37)); + } + + @Test + void testRoundToDoubleGrid() + { + // double grid snaps to multiples of half a pixel (32) + assertEquals(96, round(GraphicsState.ROUND_TO_DOUBLE_GRID, 80)); // 1.25px -> 1.5px + assertEquals(64, round(GraphicsState.ROUND_TO_DOUBLE_GRID, 70)); // 1.09px -> 1.0px + } + + @Test + void testRoundToHalfGrid() + { + // half grid snaps to (n + 0.5) pixels, i.e. 32, 96, 160, ... + assertEquals(96, round(GraphicsState.ROUND_TO_HALF_GRID, 100)); // -> 1.5px + assertEquals(32, round(GraphicsState.ROUND_TO_HALF_GRID, 50)); // -> 0.5px + } + + @Test + void testSuperRoundReducesToGrid() + { + // SROUND with period=1px, phase=0, threshold=half is equivalent to round-to-grid + GraphicsState gs = new GraphicsState(); + gs.setSuperRound(Fixed.ONE, 0x48); // 01 period=1.0, 00 phase=0, 1000 threshold=4*p/8=half + assertEquals(128, gs.round(100)); + assertEquals(64, gs.round(95)); + } +} diff --git a/fontbox/src/test/java/org/apache/fontbox/ttf/TrueTypeInterpreterTest.java b/fontbox/src/test/java/org/apache/fontbox/ttf/TrueTypeInterpreterTest.java new file mode 100644 index 00000000000..d1cbe58407c --- /dev/null +++ b/fontbox/src/test/java/org/apache/fontbox/ttf/TrueTypeInterpreterTest.java @@ -0,0 +1,352 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; + +import java.time.Duration; + +import org.junit.jupiter.api.Test; + +/** + * Program-tier (Tier 2) tests: hand-assembled bytecode fed through the dispatch loop with no font. + * These exercise the engine - dispatch, the push family, branching, function definition and calling, + * and the {@link BytecodeStream} bounds checks - independently of any glyph. + */ +class TrueTypeInterpreterTest +{ + // opcodes used to assemble test programs + private static final byte PUSHB1 = (byte) 0xB0; // PUSHB[0] - push one byte + private static final byte PUSHB2 = (byte) 0xB1; // PUSHB[1] - push two bytes + private static final byte NPUSHW = (byte) 0x41; + private static final byte ADD = 0x60; + private static final byte SUB = 0x61; + private static final byte MUL = 0x63; + private static final byte DUP = 0x20; + private static final byte SWAP = 0x23; + private static final byte DEPTH = 0x24; + private static final byte ROLL = (byte) 0x8A; + private static final byte GT = 0x52; + private static final byte IF = 0x58; + private static final byte ELSE = 0x1B; + private static final byte EIF = 0x59; + private static final byte PUSHW1 = (byte) 0xB8; // PUSHW[0] - push one signed word + private static final byte JMPR = 0x1C; + private static final byte JROT = 0x78; + private static final byte FDEF = 0x2C; + private static final byte ENDF = 0x2D; + private static final byte CALL = 0x2B; + private static final byte LOOPCALL = 0x2A; + private static final byte MPPEM = 0x4B; + private static final byte WS = 0x42; + private static final byte RS = 0x43; + private static final byte SZPS = 0x16; + private static final byte SCFS = 0x48; + private static final byte GC = 0x46; + + private static TrueTypeInterpreter interpreter() + { + return new TrueTypeInterpreter(256, 16, 0, 2048); + } + + private static int runTop(byte[] program) + { + ExecutionContext ctx = interpreter().executeProgram(program, 16); + return ctx.peek(0); + } + + @Test + void testPushAndAdd() + { + // PUSHB[1] 2 3 ; ADD -> 5 + assertEquals(5, runTop(new byte[] { PUSHB2, 2, 3, ADD })); + } + + @Test + void testNpushwSigned() + { + // NPUSHW 1 0xFFFF ; -> -1 on the stack + assertEquals(-1, runTop(new byte[] { NPUSHW, 1, (byte) 0xFF, (byte) 0xFF })); + } + + @Test + void testArithmetic() + { + // 10 - 3 == 7 + assertEquals(7, runTop(new byte[] { PUSHB2, 10, 3, SUB })); + // 64(=1.0) * 192(=3.0) == 192(=3.0) ... use F26Dot6: PUSHB 64, then need words; use small ints + // 2.0 * 3.0 in F26Dot6: push 128 and 192 via NPUSHW + assertEquals(Fixed.fromInt(6), + runTop(new byte[] { NPUSHW, 2, 0, (byte) 128, 0, (byte) 192, MUL })); + } + + @Test + void testStackOps() + { + // DUP: push 7, dup, add -> 14 + assertEquals(14, runTop(new byte[] { PUSHB1, 7, DUP, ADD })); + // SWAP then SUB: push 3,10 swap -> 10,3 ; SUB pops b=3,a=10 -> 7 + assertEquals(7, runTop(new byte[] { PUSHB2, 3, 10, SWAP, SUB })); + // DEPTH after pushing three values -> 3 + assertEquals(3, runTop(new byte[] { PUSHB2, 1, 2, PUSHB1, 9, DEPTH })); + // ROLL: 1 2 3 -> 2 3 1, top is 1 + assertEquals(1, runTop(new byte[] { PUSHB2, 1, 2, PUSHB1, 3, ROLL })); + } + + @Test + void testIfElseTrueBranch() + { + // push 1 (true) ; IF push 10 ELSE push 20 EIF -> 10 + assertEquals(10, runTop(new byte[] { PUSHB1, 1, IF, PUSHB1, 10, ELSE, PUSHB1, 20, EIF })); + } + + @Test + void testIfElseFalseBranch() + { + // push 0 (false) ; IF push 10 ELSE push 20 EIF -> 20 + assertEquals(20, runTop(new byte[] { PUSHB1, 0, IF, PUSHB1, 10, ELSE, PUSHB1, 20, EIF })); + } + + @Test + void testNestedIf() + { + // outer true, inner (5>3) true -> 99 + // PUSHB 1 ; IF [ PUSHB 5 3 ; GT ; IF PUSHB 99 ELSE PUSHB 1 EIF ] ELSE PUSHB 7 EIF + byte[] program = new byte[] { + PUSHB1, 1, IF, + PUSHB2, 5, 3, GT, IF, + PUSHB1, 99, + ELSE, + PUSHB1, 1, + EIF, + ELSE, + PUSHB1, 7, + EIF }; + assertEquals(99, runTop(program)); + } + + @Test + void testJmpr() + { + // PUSHB 3 ; JMPR (jump +3 from the JMPR opcode) skips a push, lands on PUSHB 42 + // layout: [0]PUSHB1 [1]3 [2]JMPR [3]PUSHB1 [4]7(skipped) [5]PUSHB1 [6]42 + ExecutionContext ctx = interpreter().executeProgram( + new byte[] { PUSHB1, 3, JMPR, PUSHB1, 7, PUSHB1, 42 }, 16); + assertEquals(42, ctx.peek(0)); + assertEquals(1, ctx.getStackDepth()); // the skipped push never ran + } + + @Test + void testFunctionDefAndCall() + { + // define function 5 = "double the top" (DUP ADD); call it on 21 -> 42 + TrueTypeInterpreter interp = interpreter(); + interp.setFontProgram(new byte[] { PUSHB1, 5, FDEF, DUP, ADD, ENDF }); + interp.prepareFontProgram(); + assertEquals(1, interp.getFunctions().size()); + + ExecutionContext ctx = interp.executeProgram(new byte[] { PUSHB1, 21, PUSHB1, 5, CALL }, 16); + assertEquals(42, ctx.peek(0)); + } + + @Test + void testLoopCall() + { + // function 1 = "add 1"; LOOPCALL it 3 times starting from 0 -> 3 + TrueTypeInterpreter interp = interpreter(); + interp.setFontProgram(new byte[] { PUSHB1, 1, FDEF, PUSHB1, 1, ADD, ENDF }); + interp.prepareFontProgram(); + + // stack: value=0, count=3, fn=1 ; LOOPCALL pops fn then count + ExecutionContext ctx = interp.executeProgram( + new byte[] { PUSHB1, 0, PUSHB2, 3, 1, LOOPCALL }, 16); + assertEquals(3, ctx.peek(0)); + } + + @Test + void testCallDepthLimitTrips() + { + // function 0 calls itself unconditionally -> must trip the depth cap, not StackOverflowError + TrueTypeInterpreter interp = interpreter(); + interp.setFontProgram(new byte[] { PUSHB1, 0, FDEF, PUSHB1, 0, CALL, ENDF }); + interp.prepareFontProgram(); + + HintingException ex = assertThrows(HintingException.class, + () -> interp.executeProgram(new byte[] { PUSHB1, 0, CALL }, 16)); + assertEquals(true, ex.getMessage().contains("call depth")); + } + + @Test + void testIdefDefinesOpcode() + { + // IDEF binds reserved opcode 0x83 to "push 42"; invoking 0x83 then runs that body. + // PUSHB[0] 0x83 ; IDEF ; PUSHB[0] 42 ; ENDF ; <0x83> + ExecutionContext ctx = interpreter().executeProgram( + new byte[] { PUSHB1, (byte) 0x83, (byte) 0x89, PUSHB1, 42, ENDF, (byte) 0x83 }, 16); + assertEquals(42, ctx.peek(0)); + } + + @Test + void testUndefinedFunctionThrows() + { + assertThrows(HintingException.class, + () -> interpreter().executeProgram(new byte[] { PUSHB1, 9, CALL }, 16)); + } + + @Test + void testMppemReflectsPpem() + { + ExecutionContext ctx = interpreter().executeProgram(new byte[] { MPPEM }, 19); + assertEquals(19, ctx.peek(0)); + } + + @Test + void testUnsupportedOpcodeThrows() + { + // 0x28 is a reserved/unused opcode; unimplemented opcodes must throw, not no-op + assertThrows(HintingException.class, + () -> interpreter().executeProgram(new byte[] { 0x28 }, 16)); + } + + @Test + void testControlValueScalingThroughPrep() + { + // raw cvt [2048] at 16 ppem, unitsPerEm 2048 -> scaled to 16px (1024 in F26Dot6) + TrueTypeInterpreter interp = interpreter(); + interp.setControlValues(new int[] { 2048 }); + interp.setPpem(16, 16); + // RCVT 0 -> the scaled value + ExecutionContext ctx = interp.executeProgram(new byte[] { PUSHB1, 0, 0x45 }, 16); + assertEquals(Fixed.fromInt(16), ctx.peek(0)); + } + + /** + * A backward jump is one of only two ways TrueType bytecode can loop, and this four-byte program - + * {@code PUSHW -3 ; JMPR}, which jumps back onto its own push - used to spin forever. It must now + * hit the execution budget and throw, so {@code GlyphHinter} falls back to the raw outline. + */ + @Test + void testBackwardJumpIsBounded() + { + assertTimeoutPreemptively(Duration.ofSeconds(5), () -> + assertThrows(HintingException.class, + () -> interpreter().executeProgram( + new byte[] { PUSHW1, (byte) 0xFF, (byte) 0xFD, JMPR }, 16))); + } + + /** + * A backward-jump loop that stays inside the budget must still run to completion - the bound exists + * to stop runaway programs, not legitimately loop-heavy ones. + */ + @Test + void testBackwardJumpWithinBudgetCompletes() + { + // counter = 60; loop { counter -= 1; if (counter != 0) jump back } -> 59 backward jumps + // [0]PUSHW1 60 [3]PUSHB1 1 [5]SUB [6]DUP [7]PUSHW1 -8 [10]SWAP [11]JROT + byte[] program = + { + PUSHW1, 0, 60, + PUSHB1, 1, SUB, DUP, PUSHW1, (byte) 0xFF, (byte) 0xF8, SWAP, JROT + }; + ExecutionContext ctx = assertTimeoutPreemptively(Duration.ofSeconds(5), + () -> interpreter().executeProgram(program, 16)); + assertEquals(0, ctx.peek(0)); + assertEquals(1, ctx.getStackDepth()); + } + + /** + * {@code LOOPCALL} takes its iteration count off the stack, so a crafted font can ask for billions. + * The whole loop is charged against the budget up front, so an absurd count fails before a single + * iteration runs. + */ + @Test + void testLoopCallCountIsBounded() + { + TrueTypeInterpreter interp = interpreter(); + interp.setFontProgram(new byte[] { PUSHB1, 1, FDEF, PUSHB1, 1, ADD, ENDF }); + interp.prepareFontProgram(); + + // stack: value=0, count=32767, fn=1 + assertTimeoutPreemptively(Duration.ofSeconds(5), () -> + assertThrows(HintingException.class, + () -> interp.executeProgram( + new byte[] { PUSHB1, 0, PUSHW1, 0x7F, (byte) 0xFF, PUSHB1, 1, + LOOPCALL }, 16))); + } + + /** + * The spec calls the {@code LOOPCALL} count unsigned; FreeType runs nothing at all when it is not + * positive, so a negative count must be a no-op rather than an error or an underflowing loop. + */ + @Test + void testNonPositiveLoopCallCountRunsNothing() + { + TrueTypeInterpreter interp = interpreter(); + interp.setFontProgram(new byte[] { PUSHB1, 1, FDEF, PUSHB1, 1, ADD, ENDF }); + interp.prepareFontProgram(); + + // stack: value=7, count=-1, fn=1 -> the function never runs, 7 is left untouched + ExecutionContext ctx = interp.executeProgram( + new byte[] { PUSHB1, 7, PUSHW1, (byte) 0xFF, (byte) 0xFF, PUSHB1, 1, LOOPCALL }, 16); + assertEquals(7, ctx.peek(0)); + assertEquals(1, ctx.getStackDepth()); + } + + /** + * A font may compute values into the storage area in {@code prep} and read them back from every + * glyph program, so storage belongs to the size and not to one program run. Building a fresh + * storage array per run made {@code RS} read zeros - silently wrong outlines rather than a failure. + */ + @Test + void testStoragePersistsFromPrepIntoGlyphProgram() + { + TrueTypeInterpreter interp = interpreter(); + interp.setControlValueProgram(new byte[] { PUSHB2, 5, 42, WS }); // storage[5] = 42 + interp.setPpem(16, 16); + + ExecutionContext ctx = interp.executeProgram(new byte[] { PUSHB1, 5, RS }, 16); + assertEquals(42, ctx.peek(0)); + } + + /** The twilight zone belongs to the size for the same reason: {@code prep} seeds points there. */ + @Test + void testTwilightPointsPersistFromPrep() + { + TrueTypeInterpreter interp = new TrueTypeInterpreter(256, 16, 4, 2048); + // prep: aim every zone pointer at the twilight zone, then set point 1's x to 128 + interp.setControlValueProgram(new byte[] { PUSHB1, 0, SZPS, PUSHB2, 1, (byte) 128, SCFS }); + interp.setPpem(16, 16); + + ExecutionContext ctx = interp.executeProgram(new byte[] { PUSHB1, 0, SZPS, PUSHB1, 1, GC }, 16); + assertEquals(128, ctx.peek(0)); + } + + /** A new size starts clean, as FreeType clears both in {@code tt_size_run_prep}. */ + @Test + void testStorageIsClearedOnPpemChange() + { + TrueTypeInterpreter interp = interpreter(); + interp.setPpem(16, 16); + interp.executeProgram(new byte[] { PUSHB2, 5, 42, WS }, 16); + assertEquals(42, interp.executeProgram(new byte[] { PUSHB1, 5, RS }, 16).peek(0)); + + interp.setPpem(24, 24); + assertEquals(0, interp.executeProgram(new byte[] { PUSHB1, 5, RS }, 24).peek(0)); + } +} From 0349b098dda7d3597a98b97a7d9e1853d8e3b6ca Mon Sep 17 00:00:00 2001 From: Patrick Corless Date: Tue, 8 Sep 2026 22:01:52 -0600 Subject: [PATCH 3/8] PDFBOX-3293: grid-fit glyph outlines in FontBox GlyphHinter drives the interpreter for one font: it builds the VM from the font's maxp, head, cvt, fpgm and prep tables, runs the font program once, re-runs the control value program whenever the ppem changes, and for each glyph scales the outline into the pixel grid, appends the phantom points, executes the glyph's instructions and scales the result back to font units. TrueTypeFont.getHintedPath(gid, ppem) is the entry point. Hinting is off by default. TrueTypeFont.SYSPROP_HINTING ("org.apache.fontbox.ttf.hinting") or setHintingEnabled(boolean) turns it on; while it is off getHintedPath returns null and callers use the raw outline, so existing output is unchanged. It is best-effort throughout. A composite whose components cannot be resolved, a glyph with no instructions, a ppem the gasp table excludes, a malformed program - each falls back to null for that glyph alone, never for the rest of the font. The first failure in a font is logged with a stack trace and the rest at debug level, so a font that never hints does not flood the log. Composites are assembled the way FreeType assembles them: each component is hinted on its own, then transformed and offset into the composite's space, with the assembled positions becoming the originals the composite's own instructions measure against. Component offsets are not grid-rounded. Grayscale rendering follows FreeType's v40 interpreter: movement in x is suppressed and y is frozen once IUP has run on both axes, which is what stops stems being darkened under antialiasing. Every entry point is synchronized, so one font hints one glyph at a time. That matters because a system-substituted font is held in a process-wide cache and several rendering threads can share one instance. Known limitation: FreeType exempts a short list of "tricky" fonts - mostly CJK fonts that assemble glyphs from sub-pixel-sized components - from the grayscale movement restrictions and from the gasp grid-fit bit, because they are unreadable without full bytecode control. Identifying them requires a lookup table, which is not included here; those fonts render unhinted, exactly as they do today. Adding that support is a follow-up. Co-Authored-By: Claude Opus 5 --- .../org/apache/fontbox/ttf/GlyphHinter.java | 519 ++++++++++++++++++ .../org/apache/fontbox/ttf/GlyphRenderer.java | 23 +- .../org/apache/fontbox/ttf/TrueTypeFont.java | 82 +++ .../fontbox/ttf/HintingConcurrencyTest.java | 164 ++++++ .../fontbox/ttf/HintingIntegrationTest.java | 222 ++++++++ .../apache/fontbox/ttf/HintingSwitchTest.java | 148 +++++ 6 files changed, 1156 insertions(+), 2 deletions(-) create mode 100644 fontbox/src/main/java/org/apache/fontbox/ttf/GlyphHinter.java create mode 100644 fontbox/src/test/java/org/apache/fontbox/ttf/HintingConcurrencyTest.java create mode 100644 fontbox/src/test/java/org/apache/fontbox/ttf/HintingIntegrationTest.java create mode 100644 fontbox/src/test/java/org/apache/fontbox/ttf/HintingSwitchTest.java diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphHinter.java b/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphHinter.java new file mode 100644 index 00000000000..a15959b82aa --- /dev/null +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphHinter.java @@ -0,0 +1,519 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import java.awt.geom.GeneralPath; +import java.io.IOException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Applies TrueType bytecode hinting (grid-fitting) to a font's glyphs, producing grid-fitted paths. + *

+ * One hinter is created per {@link TrueTypeFont}. It lazily builds a {@link TrueTypeInterpreter} from + * the font's {@code maxp}/{@code head}/{@code cvt}/{@code fpgm}/{@code prep} tables, runs the font + * program once, and re-runs the control value program whenever the ppem changes. For each glyph it + * scales the outline into the pixel grid (appending the phantom points), runs the glyph's instructions + * and scales the grid-fitted result back into font units, so the rest of the rendering pipeline - which + * scales font units to device pixels at exactly this ppem - reproduces the grid-fitting. + *

+ * Hinting is best-effort: anything malformed, unsupported, or not applicable (a composite glyph, a + * glyph with no instructions, a ppem the {@code gasp} table excludes) falls back to {@code null}, and + * the caller renders the raw outline. One bad glyph never disables hinting for the rest of the font. + * Hinting as a whole is switched on and off by {@link TrueTypeFont#isHintingEnabled()}; while it is + * off every glyph falls back to {@code null}. + *

+ * The interpreter carries a great deal of mutable state - the storage area, the twilight zone, the + * post-{@code prep} template, the active ppem - so every entry point here is {@code synchronized} and + * one font hints one glyph at a time. That is correct but it does serialize: a font substituted from + * the system is held in a process-wide cache, so several rendering threads can share one instance and + * queue on this monitor. Embedded fonts are per-document and unaffected. If it ever measures as a + * bottleneck the answer is a per-thread or pooled interpreter, not a weaker lock; until then the simple + * thing is the right thing. {@code HintingConcurrencyTest} pins the current behavior. + * + * @author Apache PDFBox + */ +class GlyphHinter +{ + private static final Logger LOG = LogManager.getLogger(GlyphHinter.class); + + private final TrueTypeFont font; + + private boolean initialized; + private boolean available; + private boolean warned; + private TrueTypeInterpreter interpreter; + private GaspTable gasp; + private int unitsPerEm; + private int currentPpem = -1; + + GlyphHinter(TrueTypeFont font) + { + this.font = font; + } + + private synchronized void initialize() throws IOException + { + if (initialized) + { + return; + } + initialized = true; + available = false; + + MaximumProfileTable maxp = font.getMaximumProfile(); + FontProgramTable fpgm = font.getFontProgram(); + ControlValueProgramTable prep = font.getControlValueProgram(); + ControlValueTable cvt = font.getControlValues(); + + // hinting is only meaningful if the font carries a bytecode program + if (maxp == null || (fpgm == null && prep == null)) + { + return; + } + + unitsPerEm = font.getUnitsPerEm(); + gasp = font.getGasp(); + + interpreter = new TrueTypeInterpreter(maxp.getMaxStackElements(), maxp.getMaxStorage(), + maxp.getMaxTwilightPoints(), unitsPerEm); + interpreter.setFontProgram(fpgm != null ? fpgm.getProgram() : null); + interpreter.setControlValueProgram(prep != null ? prep.getProgram() : null); + interpreter.setControlValues(cvt != null ? cvt.getValues() : null); + interpreter.prepareFontProgram(); + available = true; + } + + /** + * Returns the grid-fitted path of the glyph at the given ppem, or {@code null} if hinting does not + * apply and the caller should render the raw outline. + * + * @param gid the glyph id + * @param ppem the pixels-per-em to grid-fit to + * @return the hinted path in font units, or null + */ + synchronized GeneralPath getPath(int gid, int ppem) + { + Hinted hinted = hint(gid, ppem); + if (hinted == null) + { + return null; + } + // scale the grid-fitted coordinates back into font units (drop the phantom points) + int[] hintedX = new int[hinted.pointCount]; + int[] hintedY = new int[hinted.pointCount]; + int[] curX = hinted.zone.getCurrentX(); + int[] curY = hinted.zone.getCurrentY(); + for (int i = 0; i < hinted.pointCount; i++) + { + hintedX[i] = toFontUnits(curX[i], ppem); + hintedY[i] = toFontUnits(curY[i], ppem); + } + return new GlyphRenderer(hinted.gd, hintedX, hintedY).getPath(); + } + + /** + * Returns the grid-fitted glyph points in F26Dot6 device coordinates (the raw interpreter output, + * before scaling back to font units, and excluding the phantom points), or {@code null} if hinting + * does not apply. This is the form compared against a FreeType reference dump by the golden tests. + * + * @param gid the glyph id + * @param ppem the pixels-per-em to grid-fit to + * @return a {@code {x[], y[]}} pair in F26Dot6, or null + */ + synchronized int[][] getHintedPointsF26Dot6(int gid, int ppem) + { + Hinted hinted = hint(gid, ppem); + if (hinted == null) + { + return null; + } + int[] x = new int[hinted.pointCount]; + int[] y = new int[hinted.pointCount]; + System.arraycopy(hinted.zone.getCurrentX(), 0, x, 0, hinted.pointCount); + System.arraycopy(hinted.zone.getCurrentY(), 0, y, 0, hinted.pointCount); + return new int[][] { x, y }; + } + + /** + * Runs the control value program untraced, then grid-fits the glyph with an execution tracer + * attached, so the per-instruction trace can be diffed against FreeType's {@code ttinterp} trace. + * For development/debugging only. + * + * @param gid the glyph id + * @param ppem the pixels-per-em + * @param out where to write the trace + * @param tracePoint a glyph point index to log per instruction, or -1 + * @throws IOException if the font could not be read + */ + synchronized void traceGlyph(int gid, int ppem, java.io.PrintStream out, int tracePoint) + throws IOException + { + initialize(); + if (!available) + { + return; + } + setActivePpem(ppem); + interpreter.setTracer(new ExecutionTracer(out, tracePoint)); + try + { + hint(gid, ppem, 0); + } + finally + { + interpreter.setTracer(null); + } + } + + /** Maximum composite nesting depth, to bound recursion on pathological fonts. */ + private static final int MAX_COMPONENT_DEPTH = 8; + + /** Runs all gating, then grid-fits the glyph, returning the executed zone or null on fallback. */ + private Hinted hint(int gid, int ppem) + { + if (!TrueTypeFont.isHintingEnabled() || ppem <= 0) + { + return null; + } + try + { + initialize(); + if (!available) + { + return null; + } + // gasp gate: if a gasp table is present and does not request grid-fitting here, skip + if (gasp != null && !gasp.isGridFit(ppem)) + { + return null; + } + setActivePpem(ppem); + return hint(gid, ppem, 0); + } + catch (IOException | RuntimeException e) + { + logFailure(gid, ppem, e); + return null; + } + } + + /** + * Reports a glyph that could not be hinted. Only the first failure in a font is a warning carrying + * the stack trace; the rest go to debug. Hinting is attempted per {@code (glyph, ppem)} pair, so a + * font whose bytecode never runs - a malformed program, or one using something unimplemented - would + * otherwise emit thousands of identical stack traces for a page of CJK text. + */ + private void logFailure(int gid, int ppem, Exception e) + { + if (warned) + { + LOG.debug("hinting failed for glyph {} at {}ppem, using raw outline", gid, ppem, e); + return; + } + warned = true; + LOG.warn("hinting failed for glyph {} at {}ppem in font {}, using raw outline; further " + + "failures in this font are logged at debug level", gid, ppem, fontName(), e); + } + + /** The font's PostScript name for the warning above, best-effort - we are already handling a fault. */ + private String fontName() + { + try + { + return font.getName(); + } + catch (IOException e) + { + return ""; + } + } + + /** + * Re-runs the control value program if the ppem changed. The guard is load-bearing, not just an + * optimization: {@code setPpem} clears the storage area and twilight zone before running + * {@code prep}, so re-running it per glyph would wipe the values {@code prep} seeded for the glyph + * programs to read. + */ + private void setActivePpem(int ppem) throws IOException + { + if (ppem != currentPpem) + { + interpreter.setPpem(ppem, ppem); + currentPpem = ppem; + } + } + + /** Grid-fits one glyph (simple or composite), recursing into components. */ + private Hinted hint(int gid, int ppem, int depth) throws IOException + { + if (depth > MAX_COMPONENT_DEPTH) + { + return null; + } + GlyphData glyph = font.getGlyph().getGlyph(gid); + if (glyph == null) + { + return null; + } + GlyphDescription gd = glyph.getDescription(); + if (!(gd instanceof GlyfDescript)) + { + return null; + } + if (gd.isComposite()) + { + gd.resolve(); + if (gd.getPointCount() == 0) + { + return null; + } + return hintComposite(glyph, (GlyfCompositeDescript) gd, gid, ppem, depth); + } + if (gd.getContourCount() == 0 || gd.getPointCount() == 0) + { + // empty glyph (e.g. space, newline): nothing to hint + return null; + } + int[] instructions = ((GlyfDescript) gd).getInstructions(); + if (instructions == null || instructions.length == 0) + { + return null; + } + int pointCount = gd.getPointCount(); + Zone zone = buildZone(glyph, gd, gid, ppem, pointCount, gd.getContourCount()); + runProgram(zone, instructions, ppem, false); + return new Hinted(gd, zone, pointCount); + } + + /** + * Grid-fits a composite glyph the way FreeType does: each component is hinted on its own, then + * transformed and offset into the composite's coordinate space, the phantom points appended, and + * finally the composite's own instructions (if any) run over the assembled outline. + */ + private Hinted hintComposite(GlyphData glyph, GlyfCompositeDescript composite, int gid, int ppem, + int depth) throws IOException + { + int pointCount = composite.getPointCount(); + int contourCount = composite.getContourCount(); + Zone zone = new Zone(pointCount + 4, contourCount); + + for (GlyfCompositeComp comp : composite.getComponents()) + { + assembleComponent(comp, ppem, depth, zone); + } + int[] ends = zone.getContourEnds(); + for (int c = 0; c < contourCount; c++) + { + ends[c] = composite.getEndPtOfContours(c); + } + appendPhantomPoints(glyph, gid, ppem, pointCount, zone); + + int[] instructions = composite.getInstructions(); + if (instructions != null && instructions.length > 0) + { + runProgram(zone, instructions, ppem, true); + } + return new Hinted(composite, zone, pointCount); + } + + /** + * Hints one component glyph and writes its transformed/offset points into the composite's zone + * arrays. The component's grid-fitted outline goes to the current arrays and its scaled-but-unhinted + * outline to the original arrays, so the composite's instructions can measure original distances. + */ + private void assembleComponent(GlyfCompositeComp comp, int ppem, int depth, Zone zone) + throws IOException + { + int componentGid = comp.getGlyphIndex(); + int first = comp.getFirstIndex(); + + GlyphData componentGlyph = font.getGlyph().getGlyph(componentGid); + GlyphDescription cgd = componentGlyph != null ? componentGlyph.getDescription() : null; + if (cgd == null) + { + return; + } + if (cgd.isComposite()) + { + cgd.resolve(); + } + int count = cgd.getPointCount(); + boolean[] onCurve = zone.getOnCurve(); + + // scaled-but-unhinted component points (used as a fallback) and the unscaled font-unit ones + int[] cOrgX = new int[count]; + int[] cOrgY = new int[count]; + int[] cUnsX = new int[count]; + int[] cUnsY = new int[count]; + for (int k = 0; k < count; k++) + { + cUnsX[k] = cgd.getXCoordinate(k); + cUnsY[k] = cgd.getYCoordinate(k); + cOrgX[k] = Fixed.scale(cUnsX[k], ppem, unitsPerEm); + cOrgY[k] = Fixed.scale(cUnsY[k], ppem, unitsPerEm); + onCurve[first + k] = (cgd.getFlags(k) & GlyfDescript.ON_CURVE) != 0; + } + + // grid-fitted component points (its own instructions executed); fall back to unhinted + int[] cCurX = cOrgX; + int[] cCurY = cOrgY; + Hinted hintedComponent = hint(componentGid, ppem, depth + 1); + if (hintedComponent != null && hintedComponent.pointCount == count) + { + cCurX = hintedComponent.zone.getCurrentX(); + cCurY = hintedComponent.zone.getCurrentY(); + } + + // device-space offset (FreeType does not grid-round the component offset here, even when + // ROUND_XY_TO_GRID is set, so neither do we); the unscaled offset stays in font units + int offsetX = Fixed.scale(comp.getXTranslate(), ppem, unitsPerEm); + int offsetY = Fixed.scale(comp.getYTranslate(), ppem, unitsPerEm); + int unsOffsetX = comp.getXTranslate(); + int unsOffsetY = comp.getYTranslate(); + + int[] curX = zone.getCurrentX(); + int[] curY = zone.getCurrentY(); + int[] orgX = zone.getOriginalX(); + int[] orgY = zone.getOriginalY(); + int[] unsX = zone.getUnscaledX(); + int[] unsY = zone.getUnscaledY(); + for (int k = 0; k < count; k++) + { + curX[first + k] = comp.scaleX(cCurX[k], cCurY[k]) + offsetX; + curY[first + k] = comp.scaleY(cCurX[k], cCurY[k]) + offsetY; + // FreeType bakes each hinted component into the composite and copies cur -> org before + // running the composite program, so the original equals the assembled hinted position + // (a SHC/MDRP in the composite then measures zero movement for an unmoved component point) + orgX[first + k] = curX[first + k]; + orgY[first + k] = curY[first + k]; + unsX[first + k] = comp.scaleX(cUnsX[k], cUnsY[k]) + unsOffsetX; + unsY[first + k] = comp.scaleY(cUnsX[k], cUnsY[k]) + unsOffsetY; + } + } + + /** Clones the saved post-prep state, resets it for the glyph, and runs the program over the zone. */ + private void runProgram(Zone zone, int[] instructions, int ppem, boolean composite) + { + GraphicsState gs = interpreter.getSavedState().copy(); + gs.resetForGlyph(); + ExecutionContext ctx = interpreter.newContext(gs); + ctx.setPpem(ppem); + ctx.setGlyphZone(zone); + // v40 grayscale "backward compatibility" applies to the glyph program only, never fpgm/prep, + // which build control values via twilight-zone x/y moves that must not be suppressed + ctx.setBackwardCompatibility(true); + ctx.setComposite(composite); + interpreter.run(ctx, new BytecodeStream(toByteArray(instructions))); + } + + /** The result of grid-fitting one glyph: its description, the executed zone, and its point count + * (without the appended phantom points). */ + private static final class Hinted + { + private final GlyphDescription gd; + private final Zone zone; + private final int pointCount; + + Hinted(GlyphDescription gd, Zone zone, int pointCount) + { + this.gd = gd; + this.zone = zone; + this.pointCount = pointCount; + } + } + + private Zone buildZone(GlyphData glyph, GlyphDescription gd, int gid, int ppem, int pointCount, + int contourCount) throws IOException + { + // four phantom points are appended after the glyph's own points + Zone zone = new Zone(pointCount + 4, contourCount); + int[] curX = zone.getCurrentX(); + int[] curY = zone.getCurrentY(); + int[] orgX = zone.getOriginalX(); + int[] orgY = zone.getOriginalY(); + int[] unsX = zone.getUnscaledX(); + int[] unsY = zone.getUnscaledY(); + boolean[] onCurve = zone.getOnCurve(); + for (int i = 0; i < pointCount; i++) + { + int fx = gd.getXCoordinate(i); + int fy = gd.getYCoordinate(i); + unsX[i] = fx; + unsY[i] = fy; + int x = Fixed.scale(fx, ppem, unitsPerEm); + int y = Fixed.scale(fy, ppem, unitsPerEm); + orgX[i] = x; + orgY[i] = y; + curX[i] = x; + curY[i] = y; + onCurve[i] = (gd.getFlags(i) & GlyfDescript.ON_CURVE) != 0; + } + int[] ends = zone.getContourEnds(); + for (int c = 0; c < contourCount; c++) + { + ends[c] = gd.getEndPtOfContours(c); + } + appendPhantomPoints(glyph, gid, ppem, pointCount, zone); + return zone; + } + + private void appendPhantomPoints(GlyphData glyph, int gid, int ppem, int pointCount, Zone zone) + throws IOException + { + HorizontalMetricsTable hmtx = font.getHorizontalMetrics(); + int advanceWidth = hmtx != null ? hmtx.getAdvanceWidth(gid) : unitsPerEm; + int leftSideBearing = hmtx != null ? hmtx.getLeftSideBearing(gid) : 0; + int originX = glyph.getXMinimum() - leftSideBearing; + int yMax = glyph.getYMaximum(); + + // pp1 = origin, pp2 = origin + advance (horizontal); pp3/pp4 are the vertical pair + int[] px = { originX, originX + advanceWidth, 0, 0 }; + int[] py = { 0, 0, yMax, yMax - unitsPerEm }; + for (int i = 0; i < 4; i++) + { + int index = pointCount + i; + zone.getUnscaledX()[index] = px[i]; + zone.getUnscaledY()[index] = py[i]; + zone.getOriginalX()[index] = Fixed.scale(px[i], ppem, unitsPerEm); + zone.getOriginalY()[index] = Fixed.scale(py[i], ppem, unitsPerEm); + // FreeType rounds the phantom points to the grid before running the glyph program + zone.getCurrentX()[index] = Fixed.round(zone.getOriginalX()[index]); + zone.getCurrentY()[index] = Fixed.round(zone.getOriginalY()[index]); + } + } + + /** Scales an F26Dot6 device coordinate back to font units. */ + private int toFontUnits(int f26dot6, int ppem) + { + long numerator = (long) f26dot6 * unitsPerEm; + long denominator = (long) ppem * Fixed.ONE; + long half = denominator / 2; + return (int) ((numerator >= 0 ? numerator + half : numerator - half) / denominator); + } + + private static byte[] toByteArray(int[] instructions) + { + byte[] bytes = new byte[instructions.length]; + for (int i = 0; i < instructions.length; i++) + { + bytes[i] = (byte) instructions[i]; + } + return bytes; + } +} diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphRenderer.java b/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphRenderer.java index f54456f0276..0191cb5141f 100644 --- a/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphRenderer.java +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphRenderer.java @@ -41,10 +41,28 @@ class GlyphRenderer private static final Logger LOG = LogManager.getLogger(GlyphRenderer.class); private final GlyphDescription glyphDescription; + private final int[] hintedX; + private final int[] hintedY; GlyphRenderer(GlyphDescription glyphDescription) + { + this(glyphDescription, null, null); + } + + /** + * Creates a renderer that builds the path from grid-fitted (hinted) coordinates instead of the + * glyph's raw coordinates. The arrays are in font units (the hinting having been applied and + * scaled back), and must be parallel to the glyph's points. + * + * @param glyphDescription the glyph description + * @param hintedX the hinted x coordinates in font units, or null for unhinted + * @param hintedY the hinted y coordinates in font units, or null for unhinted + */ + GlyphRenderer(GlyphDescription glyphDescription, int[] hintedX, int[] hintedY) { this.glyphDescription = glyphDescription; + this.hintedX = hintedX; + this.hintedY = hintedY; } /** @@ -77,8 +95,9 @@ private Point[] describe(GlyphDescription gd) endPtIndex++; endPtOfContourIndex = -1; } - points[i] = new Point(gd.getXCoordinate(i), gd.getYCoordinate(i), - (gd.getFlags(i) & GlyfDescript.ON_CURVE) != 0, endPt); + int x = hintedX != null ? hintedX[i] : gd.getXCoordinate(i); + int y = hintedY != null ? hintedY[i] : gd.getYCoordinate(i); + points[i] = new Point(x, y, (gd.getFlags(i) & GlyfDescript.ON_CURVE) != 0, endPt); } return points; } diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java b/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java index 02f431f14d8..b730122d808 100644 --- a/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java @@ -54,7 +54,64 @@ public class TrueTypeFont implements FontBoxFont, Closeable private final Object lockReadtable = new Object(); private final Object lockPSNames = new Object(); + private final Object lockHinter = new Object(); private final List enabledGsubFeatures = new ArrayList<>(); + private GlyphHinter hinter; + + /** + * System property controlling whether TrueType bytecode hinting (grid-fitting) is applied to glyph + * outlines, e.g. {@code -Dorg.apache.fontbox.ttf.hinting=true}. Hinting is off by default, which + * preserves the raw outlines FontBox has always returned. The property is read once, on first use; + * call {@link #setHintingEnabled(boolean)} to change the setting after that. + */ + public static final String SYSPROP_HINTING = "org.apache.fontbox.ttf.hinting"; + + /** Resolved hinting setting; null until first read of {@link #SYSPROP_HINTING}. */ + private static volatile Boolean hintingEnabled; + + /** + * Returns whether TrueType bytecode hinting is enabled, resolving {@link #SYSPROP_HINTING} on the + * first call. This is the single switch for the feature: {@link #getHintedPath(int, int)} returns + * {@code null} while it is off, and callers that grid-fit at render time should consult it before + * asking for a hinted path. + * + * @return true if hinting is enabled + */ + public static boolean isHintingEnabled() + { + Boolean enabled = hintingEnabled; + if (enabled == null) + { + enabled = Boolean.parseBoolean(System.getProperty(SYSPROP_HINTING)); + hintingEnabled = enabled; + } + return enabled; + } + + /** + * Enables or disables TrueType bytecode hinting, overriding {@link #SYSPROP_HINTING}. This is a + * global setting; it takes effect for fonts already parsed. + *

+ * Intended for start-up and for tests. Renderers consult {@link #isHintingEnabled()} per glyph, so + * flipping this while a page is being drawn produces a half-hinted page rather than an error. + * + * @param enabled true to grid-fit glyph outlines, false to return raw outlines + */ + public static void setHintingEnabled(boolean enabled) + { + hintingEnabled = enabled; + } + + /** + * Discards the resolved setting so the next {@link #isHintingEnabled()} reads + * {@link #SYSPROP_HINTING} again. Package-private, and here for the tests that cover the + * property-to-default resolution: the setting is resolved once per JVM, so without this a test + * cannot observe what a fresh JVM would have seen. + */ + static void resetHintingSetting() + { + hintingEnabled = null; + } /** * Constructor. Clients should use the TTFParser to create a new TrueTypeFont object. @@ -833,6 +890,31 @@ public float getWidth(String name) throws IOException return getAdvanceWidth(gid); } + /** + * Returns the grid-fitted (hinted) path of the given glyph at the given ppem, in font units, or + * {@code null} if hinting does not apply (hinting disabled - see {@link #isHintingEnabled()} - no + * bytecode program, a composite or empty glyph, or a ppem excluded by the gasp table). The caller + * should fall back to the raw outline + * ({@link GlyphData#getPath()}) when this returns {@code null}. + * + * @param gid the glyph id + * @param ppem the pixels-per-em to grid-fit to + * @return the hinted path in font units, or null + */ + public GeneralPath getHintedPath(int gid, int ppem) + { + GlyphHinter glyphHinter; + synchronized (lockHinter) + { + if (hinter == null) + { + hinter = new GlyphHinter(this); + } + glyphHinter = hinter; + } + return glyphHinter.getPath(gid, ppem); + } + @Override public boolean hasGlyph(String name) throws IOException { diff --git a/fontbox/src/test/java/org/apache/fontbox/ttf/HintingConcurrencyTest.java b/fontbox/src/test/java/org/apache/fontbox/ttf/HintingConcurrencyTest.java new file mode 100644 index 00000000000..d3dc163b203 --- /dev/null +++ b/fontbox/src/test/java/org/apache/fontbox/ttf/HintingConcurrencyTest.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.apache.pdfbox.io.RandomAccessReadBuffer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Hammers one shared {@link TrueTypeFont} from several threads. A system-substituted font is held in + * a process-wide cache ({@code FontMapperImpl}), so this is how the renderer really uses it, and the + * interpreter it drives is a pile of mutable state - the storage area, the twilight zone, the + * post-{@code prep} template, the cached ppem - guarded only by {@link GlyphHinter}'s monitor. + */ +class HintingConcurrencyTest +{ + private static final int THREADS = 8; + private static final int ITERATIONS = 150; + private static final int[] PPEMS = { 11, 13, 16, 24 }; + private static final String GLYPHS = "HILEToxn"; + + @BeforeEach + void enableHinting() + { + TrueTypeFont.setHintingEnabled(true); + } + + @AfterEach + void restoreHinting() + { + TrueTypeFont.setHintingEnabled(false); + } + + private static TrueTypeFont parse() throws IOException + { + try (InputStream is = HintingConcurrencyTest.class + .getResourceAsStream("/ttf/LiberationSans-Regular.ttf")) + { + assertNotNull(is, "missing test font"); + return new TTFParser().parse(new RandomAccessReadBuffer(is)); + } + } + + private static double[] flatten(GeneralPath path) + { + double[] coords = new double[6]; + List out = new ArrayList<>(); + for (PathIterator it = path.getPathIterator(null); !it.isDone(); it.next()) + { + out.add((double) it.currentSegment(coords)); + for (double c : coords) + { + out.add(c); + } + } + double[] array = new double[out.size()]; + for (int i = 0; i < array.length; i++) + { + array[i] = out.get(i); + } + return array; + } + + /** + * Every thread must get exactly what a single thread would have got. Interleaving the ppems is the + * point: a ppem change re-runs the control value program and clears the storage area and twilight + * zone, so an unsynchronized hinter would let one thread wipe the state another is mid-way through + * using, and the results would drift rather than throw. + */ + @Test + void testConcurrentHintingMatchesSingleThreadedResults() throws Exception + { + TrueTypeFont reference = parse(); + int[] gids = new int[GLYPHS.length()]; + Map expected = new HashMap<>(); + for (int g = 0; g < GLYPHS.length(); g++) + { + gids[g] = reference.getUnicodeCmapLookup().getGlyphId(GLYPHS.charAt(g)); + for (int ppem : PPEMS) + { + GeneralPath path = reference.getHintedPath(gids[g], ppem); + assertNotNull(path, "expected '" + GLYPHS.charAt(g) + "' to hint at " + ppem + "ppem"); + expected.put(key(g, ppem), flatten(path)); + } + } + + // a font nothing has hinted yet, so the run also races the lazy hinter creation + TrueTypeFont shared = parse(); + Queue problems = new ConcurrentLinkedQueue<>(); + CountDownLatch start = new CountDownLatch(1); + ExecutorService pool = Executors.newFixedThreadPool(THREADS); + List> futures = new ArrayList<>(); + for (int t = 0; t < THREADS; t++) + { + // each thread walks the glyph/ppem grid from a different offset, so the threads are + // asking for different sizes at the same moment rather than moving in lockstep + final int offset = t; + futures.add(pool.submit(() -> + { + start.await(); + for (int i = 0; i < ITERATIONS; i++) + { + int g = (i + offset) % GLYPHS.length(); + int ppem = PPEMS[(i + offset) % PPEMS.length]; + double[] actual = flatten(shared.getHintedPath(gids[g], ppem)); + if (!Arrays.equals(expected.get(key(g, ppem)), actual)) + { + problems.add("'" + GLYPHS.charAt(g) + "' at " + ppem + "ppem"); + } + } + return null; + })); + } + start.countDown(); + pool.shutdown(); + assertTrue(pool.awaitTermination(120, TimeUnit.SECONDS), "hinting threads did not finish"); + for (Future future : futures) + { + future.get(); // surfaces anything thrown inside a worker + } + assertTrue(problems.isEmpty(), + () -> problems.size() + " mismatched results, first: " + problems.peek()); + } + + private static int key(int glyphIndex, int ppem) + { + return glyphIndex * 1000 + ppem; + } +} diff --git a/fontbox/src/test/java/org/apache/fontbox/ttf/HintingIntegrationTest.java b/fontbox/src/test/java/org/apache/fontbox/ttf/HintingIntegrationTest.java new file mode 100644 index 00000000000..fd82ad73bb7 --- /dev/null +++ b/fontbox/src/test/java/org/apache/fontbox/ttf/HintingIntegrationTest.java @@ -0,0 +1,222 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; + +import org.apache.pdfbox.io.RandomAccessReadBuffer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * End-to-end tests: drive hinting through {@link TrueTypeFont#getHintedPath(int, int)} at the + * {@link GeneralPath} level. LiberationSans carries a full bytecode program; Lohit-Bengali has a cvt + * but no fpgm/prep, so it exercises the no-bytecode fallback. + */ +class HintingIntegrationTest +{ + // hinting is off by default, so every test here has to turn the feature on first + @BeforeEach + void enableHinting() + { + TrueTypeFont.setHintingEnabled(true); + } + + @AfterEach + void restoreHinting() + { + TrueTypeFont.setHintingEnabled(false); + } + + private static TrueTypeFont parse(String resource) throws IOException + { + try (InputStream is = HintingIntegrationTest.class.getResourceAsStream(resource)) + { + assertNotNull(is, "missing test resource " + resource); + return new TTFParser().parse(new RandomAccessReadBuffer(is)); + } + } + + private static int gid(TrueTypeFont font, int codePoint) throws IOException + { + return font.getUnicodeCmapLookup().getGlyphId(codePoint); + } + + /** Flattens a path to a coordinate list so two paths can be compared point-for-point. */ + private static double[] flatten(GeneralPath path) + { + double[] coords = new double[6]; + java.util.List out = new java.util.ArrayList<>(); + for (PathIterator it = path.getPathIterator(null); !it.isDone(); it.next()) + { + int type = it.currentSegment(coords); + out.add((double) type); + for (int i = 0; i < 6; i++) + { + out.add(coords[i]); + } + } + double[] array = new double[out.size()]; + for (int i = 0; i < array.length; i++) + { + array[i] = out.get(i); + } + return array; + } + + @Test + void testGaspGate() throws IOException + { + TrueTypeFont font = parse("/ttf/LiberationSans-Regular.ttf"); + int h = gid(font, 'H'); + // LiberationSans gasp: grid-fitting is off at <=8 ppem, on above it + assertNull(font.getHintedPath(h, 8), "no hinting expected at 8ppem (gasp)"); + assertNotNull(font.getHintedPath(h, 16), "hinting expected at 16ppem"); + } + + @Test + void testHintedDiffersFromRawOutline() throws IOException + { + TrueTypeFont font = parse("/ttf/LiberationSans-Regular.ttf"); + int h = gid(font, 'H'); + GeneralPath hinted = font.getHintedPath(h, 16); + assertNotNull(hinted); + GeneralPath raw = font.getGlyph().getGlyph(h).getPath(); + // grid-fitting must actually change the outline (proves the pipeline is wired in) + assertFalse(Arrays.equals(flatten(hinted), flatten(raw)), + "hinted path should differ from the raw outline"); + } + + @Test + void testDeterministic() throws IOException + { + TrueTypeFont font = parse("/ttf/LiberationSans-Regular.ttf"); + int h = gid(font, 'H'); + double[] first = flatten(font.getHintedPath(h, 16)); + double[] second = flatten(font.getHintedPath(h, 16)); + // re-hinting the same glyph at the same ppem is deterministic (no leaked state) + assertTrue(Arrays.equals(first, second)); + } + + /** + * Hinting one glyph must not change the next. The storage area and twilight zone are deliberately + * shared across the glyphs hinted at one size (that is how {@code prep} seeds them), so this pins + * down that nothing else leaks between glyphs - graphics state, zone contents, the cached + * ppem. A composite is in the run because it re-enters the hinter for each component. + * + *

This is a property of a well-behaved font rather than a universal law: a glyph program may + * legally write storage, and FreeType would carry that into the next glyph too. LiberationSans does + * not, so any difference here is a bug on our side. + */ + @Test + void testHintingIsIndependentOfGlyphOrder() throws IOException + { + TrueTypeFont font = parse("/ttf/LiberationSans-Regular.ttf"); + int h = gid(font, 'H'); + int eacute = gid(font, 0x00E9); + assertTrue(font.getGlyph().getGlyph(eacute).getNumberOfContours() < 0, + "expected e-acute to be a composite glyph"); + + double[] before = flatten(font.getHintedPath(h, 16)); + for (char c : "oxn8".toCharArray()) + { + font.getHintedPath(gid(font, c), 16); + } + font.getHintedPath(eacute, 16); + double[] after = flatten(font.getHintedPath(h, 16)); + + assertTrue(Arrays.equals(before, after), + "hinting other glyphs must not change the result for 'H'"); + } + + /** + * Returning to a ppem must reproduce the earlier result. A ppem change re-runs the control value + * program, which clears the storage area and twilight zone, so this covers the round trip out of a + * size and back into it. + */ + @Test + void testHintingIsIndependentOfPpemOrder() throws IOException + { + TrueTypeFont font = parse("/ttf/LiberationSans-Regular.ttf"); + int h = gid(font, 'H'); + + double[] before = flatten(font.getHintedPath(h, 16)); + font.getHintedPath(h, 11); + font.getHintedPath(h, 24); + double[] after = flatten(font.getHintedPath(h, 16)); + + assertTrue(Arrays.equals(before, after), + "hinting at other ppems must not change the result at 16ppem"); + } + + @Test + void testEscapeHatchDisablesHinting() throws IOException + { + TrueTypeFont font = parse("/ttf/LiberationSans-Regular.ttf"); + int h = gid(font, 'H'); + TrueTypeFont.setHintingEnabled(false); + assertNull(font.getHintedPath(h, 16), "escape hatch must disable hinting"); + TrueTypeFont.setHintingEnabled(true); + assertNotNull(font.getHintedPath(h, 16), "hinting restored once the switch is back on"); + } + + @Test + void testFontWithoutBytecodeFallsBack() throws IOException + { + // Lohit-Bengali has a cvt but no fpgm and no prep: there is nothing to execute + TrueTypeFont font = parse("/ttf/Lohit-Bengali.ttf"); + int gid = font.getUnicodeCmapLookup().getGlyphId(0x0985); // Bengali letter A + assertNull(font.getHintedPath(gid, 16)); + } + + @Test + void testEmptyGlyphFallsBack() throws IOException + { + TrueTypeFont font = parse("/ttf/LiberationSans-Regular.ttf"); + // the space glyph has no contours; hinting must fall back rather than fail + assertNull(font.getHintedPath(gid(font, ' '), 16)); + } + + @Test + void testCommonGlyphsHintWithoutFallingBack() throws IOException + { + // a real-font smoke test: every one of these simple glyphs carries instructions and must + // grid-fit without throwing (which would silently fall back to null). This is what caught the + // MIRP stack-order bug. + TrueTypeFont font = parse("/ttf/LiberationSans-Regular.ttf"); + String sample = "HILEToxn0123456789"; + for (int ppem : new int[] { 11, 13, 16, 24 }) + { + for (int i = 0; i < sample.length(); i++) + { + char c = sample.charAt(i); + assertNotNull(font.getHintedPath(gid(font, c), ppem), + "expected '" + c + "' to hint at " + ppem + "ppem"); + } + } + } +} diff --git a/fontbox/src/test/java/org/apache/fontbox/ttf/HintingSwitchTest.java b/fontbox/src/test/java/org/apache/fontbox/ttf/HintingSwitchTest.java new file mode 100644 index 00000000000..6cc23740a74 --- /dev/null +++ b/fontbox/src/test/java/org/apache/fontbox/ttf/HintingSwitchTest.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.pdfbox.io.RandomAccessReadBuffer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * The switch itself: that hinting is off unless it is asked for, that + * {@link TrueTypeFont#SYSPROP_HINTING} is what asks for it, and that the setter overrides the property. + *

+ * The setting is resolved once per JVM, so every test here clears the resolved value first - otherwise + * it would be observing whatever an earlier test class left behind rather than what a fresh JVM sees. + */ +class HintingSwitchTest +{ + private String previousProperty; + + @BeforeEach + void rememberProperty() + { + previousProperty = System.getProperty(TrueTypeFont.SYSPROP_HINTING); + } + + /** Puts the JVM back exactly as it was: the property restored and nothing resolved from it yet. */ + @AfterEach + void restoreProperty() + { + if (previousProperty == null) + { + System.clearProperty(TrueTypeFont.SYSPROP_HINTING); + } + else + { + System.setProperty(TrueTypeFont.SYSPROP_HINTING, previousProperty); + } + TrueTypeFont.resetHintingSetting(); + } + + private static void resolve(String propertyValue) + { + if (propertyValue == null) + { + System.clearProperty(TrueTypeFont.SYSPROP_HINTING); + } + else + { + System.setProperty(TrueTypeFont.SYSPROP_HINTING, propertyValue); + } + TrueTypeFont.resetHintingSetting(); + } + + private static TrueTypeFont parse() throws IOException + { + try (InputStream is = HintingSwitchTest.class + .getResourceAsStream("/ttf/LiberationSans-Regular.ttf")) + { + assertNotNull(is, "missing test font"); + return new TTFParser().parse(new RandomAccessReadBuffer(is)); + } + } + + private static int gid(TrueTypeFont font) throws IOException + { + return font.getUnicodeCmapLookup().getGlyphId('H'); + } + + /** + * The contract the whole feature rests on while it is opt-in: a JVM that was never told to hint + * renders exactly what it rendered before hinting existed. + */ + @Test + void testHintingIsOffWhenThePropertyIsNotSet() throws IOException + { + resolve(null); + assertFalse(TrueTypeFont.isHintingEnabled(), "hinting must be off by default"); + + TrueTypeFont font = parse(); + assertNull(font.getHintedPath(gid(font), 16), "an unconfigured JVM must not grid-fit"); + } + + @Test + void testPropertyTurnsHintingOn() throws IOException + { + resolve("true"); + assertTrue(TrueTypeFont.isHintingEnabled()); + + TrueTypeFont font = parse(); + assertNotNull(font.getHintedPath(gid(font), 16), "the property should enable grid-fitting"); + } + + @Test + void testPropertyIsCaseInsensitive() + { + resolve("TRUE"); + assertTrue(TrueTypeFont.isHintingEnabled(), "-D...=TRUE should work like =true"); + } + + @Test + void testAnythingOtherThanTrueLeavesHintingOff() + { + resolve("false"); + assertFalse(TrueTypeFont.isHintingEnabled()); + + resolve("yes"); + assertFalse(TrueTypeFont.isHintingEnabled(), "only \"true\" enables hinting"); + } + + /** The programmatic switch is documented to override the property; nothing pinned that. */ + @Test + void testSetterOverridesTheProperty() throws IOException + { + resolve("false"); + TrueTypeFont.setHintingEnabled(true); + assertTrue(TrueTypeFont.isHintingEnabled()); + + TrueTypeFont font = parse(); + assertNotNull(font.getHintedPath(gid(font), 16)); + + resolve("true"); + TrueTypeFont.setHintingEnabled(false); + assertFalse(TrueTypeFont.isHintingEnabled()); + } +} From 33e8732982df23ff20f8945f83ce276742f38568 Mon Sep 17 00:00:00 2001 From: Patrick Corless Date: Tue, 8 Sep 2026 20:54:34 -0600 Subject: [PATCH 4/8] PDFBOX-3293: use grid-fitted glyphs in the render path PDVectorFont gains getHintedNormalizedPath(code, ppem), defaulting to null so nothing changes for fonts that cannot hint. PDTrueTypeFont and PDCIDFontType2 implement it for embedded glyf outlines, normalising to the 1000-unit em square exactly as getNormalizedPath does; PDType0Font delegates to its descendant. PageDrawer derives the ppem from the glyph-space-to-device transform. The text rendering matrix alone maps glyph space to PDF user space, so the device transform is composed in first - otherwise a 7pt font would be grid-fit at 7 pixels per em rather than the 29 it is actually rendered at on a 300dpi raster. The ppem is the magnitude of the transform's vertical basis vector, which is rotation-invariant: a rotated glyph is grid-fit in its own upright space and the full transform applied afterwards, as FreeType does for vertical CJK text. GlyphCache keeps hinted paths under a (code, ppem) key, since a hinted outline is only valid at the size it was fitted for, and caches the fallback under the same key so a glyph that does not hint is not retried. The existing code-keyed cache and its path are untouched: while hinting is off PageDrawer takes that path and the feature costs nothing. Co-Authored-By: Claude Opus 5 --- .../apache/pdfbox/pdmodel/font/PDCIDFont.java | 20 +++ .../pdfbox/pdmodel/font/PDCIDFontType2.java | 26 +++ .../pdfbox/pdmodel/font/PDTrueTypeFont.java | 26 +++ .../pdfbox/pdmodel/font/PDType0Font.java | 11 +- .../pdfbox/pdmodel/font/PDVectorFont.java | 17 ++ .../apache/pdfbox/rendering/GlyphCache.java | 34 ++++ .../apache/pdfbox/rendering/PageDrawer.java | 126 ++++++++----- .../font/PDTrueTypeFontHintingTest.java | 137 ++++++++++++++ .../pdmodel/font/PDType0FontHintingTest.java | 168 ++++++++++++++++++ .../pdfbox/rendering/HintingPpemTest.java | 84 +++++++++ .../RenderHintingIntegrationTest.java | 119 +++++++++++++ .../pdfbox/ttf/LiberationSans-Regular.ttf | Bin 0 -> 410712 bytes 12 files changed, 725 insertions(+), 43 deletions(-) create mode 100644 pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFontHintingTest.java create mode 100644 pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDType0FontHintingTest.java create mode 100644 pdfbox/src/test/java/org/apache/pdfbox/rendering/HintingPpemTest.java create mode 100644 pdfbox/src/test/java/org/apache/pdfbox/rendering/RenderHintingIntegrationTest.java create mode 100644 pdfbox/src/test/resources/org/apache/pdfbox/ttf/LiberationSans-Regular.ttf diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFont.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFont.java index 96a4f774d0f..cd79c437bb5 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFont.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFont.java @@ -312,6 +312,26 @@ public PDFontDescriptor getFontDescriptor() protected abstract GeneralPath getNormalizedPath(int code, PDType0Font parent) throws IOException; + /** + * Returns the grid-fitted (TrueType-hinted) normalized glyph path for the given character code at + * the given ppem, or {@code null} if hinting does not apply. Like + * {@link #getNormalizedPath(int, PDType0Font)} the result is normalized to the 1000 unit square. + * Only a descendant with embedded TrueType outlines can hint, so this returns {@code null} unless + * overridden. + * + * @param code character code in a PDF. Not to be confused with unicode. + * @param ppem the pixels-per-em the glyph will be rendered at + * @param parent the parent Type0 font. + * + * @return the hinted normalized glyph path, or null to use the unhinted path + * @throws java.io.IOException if the font could not be read + */ + protected GeneralPath getHintedNormalizedPath(int code, int ppem, PDType0Font parent) + throws IOException + { + return null; + } + /** * Returns true if this font contains a glyph for the given character code in a PDF. * diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2.java index 795cda173d6..57875650f6a 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2.java @@ -448,6 +448,32 @@ protected GeneralPath getPath(int code, PDType0Font parent) throws IOException return new GeneralPath(); } + @Override + protected GeneralPath getHintedNormalizedPath(int code, int ppem, PDType0Font parent) + throws IOException + { + if (!isEmbedded() || (otf != null && otf.isPostScript())) + { + return null; + } + int gid = codeToGID(code, parent); + if (gid == 0) + { + return null; + } + GeneralPath path = ttf.getHintedPath(gid, ppem); + if (path == null) + { + return null; + } + if (ttf.getUnitsPerEm() != 1000) + { + float scale = 1000f / ttf.getUnitsPerEm(); + path.transform(AffineTransform.getScaleInstance(scale, scale)); + } + return path; + } + @Override protected GeneralPath getNormalizedPath(int code, PDType0Font parent) throws IOException { diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java index dba0d0d233b..f6dd0d8b13c 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java @@ -572,6 +572,32 @@ public GeneralPath getNormalizedPath(int code) throws IOException return path; } + @Override + public GeneralPath getHintedNormalizedPath(int code, int ppem) throws IOException + { + // only embedded glyf-based outlines carry hinting we can execute + if (!isEmbedded() || (otf != null && otf.isPostScript())) + { + return null; + } + int gid = codeToGID(code); + if (gid == 0) + { + return null; + } + GeneralPath path = ttf.getHintedPath(gid, ppem); + if (path == null) + { + return null; + } + if (ttf.getUnitsPerEm() != 1000) + { + float scale = 1000f / ttf.getUnitsPerEm(); + path.transform(AffineTransform.getScaleInstance(scale, scale)); + } + return path; + } + private GeneralPath getPathFromOutlines(int code) throws IOException { CFFFont cffFont = otf.getCFF().getFont(); diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDType0Font.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDType0Font.java index 82a54d36727..aa35e0b8ac5 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDType0Font.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDType0Font.java @@ -717,7 +717,16 @@ public GeneralPath getNormalizedPath(int code) throws IOException { return descendantFont.getNormalizedPath(code, this); } - + + @Override + public GeneralPath getHintedNormalizedPath(int code, int ppem) throws IOException + { + // PDCIDFont's implementation returns null and only PDCIDFontType2 overrides it, so the + // dispatch alone gives the hinted path for a TrueType-based descendant and null for a + // CFF-based one. + return descendantFont.getHintedNormalizedPath(code, ppem, this); + } + @Override public boolean hasGlyph(int code) throws IOException { diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDVectorFont.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDVectorFont.java index 4a6e66e3b1c..361696fa690 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDVectorFont.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDVectorFont.java @@ -46,6 +46,23 @@ public interface PDVectorFont */ GeneralPath getNormalizedPath(int code) throws IOException; + /** + * Returns the grid-fitted (TrueType-hinted) normalized glyph path for the given character code at + * the given ppem, or {@code null} if hinting does not apply (not an embedded TrueType outline font, + * a ppem the font's gasp table excludes, or hinting disabled). Like {@link #getNormalizedPath(int)} + * the result is normalized to the 1000 unit square. The default implementation returns + * {@code null}, i.e. no hinting. + * + * @param code character code in a PDF. Not to be confused with unicode. + * @param ppem the pixels-per-em the glyph will be rendered at + * @return the hinted normalized glyph path, or null to use the unhinted path + * @throws java.io.IOException if the font could not be read + */ + default GeneralPath getHintedNormalizedPath(int code, int ppem) throws IOException + { + return null; + } + /** * Returns true if this font contains a glyph for the given character code in a PDF. * diff --git a/pdfbox/src/main/java/org/apache/pdfbox/rendering/GlyphCache.java b/pdfbox/src/main/java/org/apache/pdfbox/rendering/GlyphCache.java index 171b0fe85e0..7ac0ba7d4b4 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/rendering/GlyphCache.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/rendering/GlyphCache.java @@ -38,11 +38,45 @@ final class GlyphCache private final PDVectorFont font; private final Map cache = new HashMap<>(); + private final Map hintedCache = new HashMap<>(); GlyphCache(PDVectorFont font) { this.font = font; } + + /** + * Returns the grid-fitted (hinted) glyph path for the given character code at the given ppem, + * falling back to the unhinted path when the font does not hint that glyph/ppem. Results are + * cached per {@code (code, ppem)}. + * + * @param code character code in a PDF + * @param ppem the pixels-per-em the glyph will be rendered at + * @return the hinted path if available, otherwise the unhinted path + */ + public GeneralPath getPathForCharacterCode(int code, int ppem) + { + long key = ((long) ppem << 32) | (code & 0xFFFFFFFFL); + GeneralPath cached = hintedCache.get(key); + if (cached != null) + { + return cached; + } + GeneralPath path = null; + try + { + path = font.getHintedNormalizedPath(code, ppem); + } + catch (IOException e) + { + String fontName = ((PDFontLike) font).getName(); + LOG.warn(() -> "Hinting failed for code " + code + " in font " + fontName, e); + } + // fall back to the unhinted path (itself cached by code); cache the decision per (code, ppem) + GeneralPath result = path != null ? path : getPathForCharacterCode(code); + hintedCache.put(key, result); + return result; + } public GeneralPath getPathForCharacterCode(int code) { diff --git a/pdfbox/src/main/java/org/apache/pdfbox/rendering/PageDrawer.java b/pdfbox/src/main/java/org/apache/pdfbox/rendering/PageDrawer.java index bc76d2486d8..a99b83b115a 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/rendering/PageDrawer.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/rendering/PageDrawer.java @@ -62,6 +62,7 @@ import java.util.Map; import java.util.Set; +import org.apache.fontbox.ttf.TrueTypeFont; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import org.apache.pdfbox.contentstream.PDFGraphicsStreamEngine; @@ -134,15 +135,15 @@ public class PageDrawer extends PDFGraphicsStreamEngine // parent document renderer - note: this is needed for not-yet-implemented resource caching private final PDFRenderer renderer; - + private final boolean subsamplingAllowed; - + // the graphics device to draw to, xform is the initial transform of the device (i.e. DPI) private Graphics2D graphics; private AffineTransform xform; private float xformScalingFactorX; private float xformScalingFactorY; - + // the page box to draw (usually the crop box but may be another) private PDRectangle pageSize; @@ -153,13 +154,13 @@ public class PageDrawer extends PDFGraphicsStreamEngine // clipping winding rule used for the clipping path private int clipWindingRule = -1; private GeneralPath linePath = new GeneralPath(); - + // last clipping path private List lastClips; // clip when drawPage() is called, can be null, must be intersected when clipping private Shape initialClip; - + // shapes of glyphs being drawn to be used for clipping private List textClippings; @@ -167,7 +168,7 @@ public class PageDrawer extends PDFGraphicsStreamEngine private final Map glyphCaches = new HashMap<>(); private final TilingPaintFactory tilingPaintFactory = new TilingPaintFactory(this); - + private final Deque transparencyGroupStack = new ArrayDeque<>(); // if greater zero the content is hidden and will not be rendered @@ -202,7 +203,7 @@ public PageDrawer(PageDrawerParameters parameters) /** * Return the AnnotationFilter. - * + * * @return the AnnotationFilter */ public AnnotationFilter getAnnotationFilter() @@ -212,19 +213,19 @@ public AnnotationFilter getAnnotationFilter() /** * Set the AnnotationFilter. - * + * *

Allows to only render annotation accepted by the filter. - * + * * @param annotationFilter the AnnotationFilter */ public void setAnnotationFilter(AnnotationFilter annotationFilter) { this.annotationFilter = annotationFilter; } - + /** * Returns the parent renderer. - * + * * @return the parent renderer */ public final PDFRenderer getRenderer() @@ -234,7 +235,7 @@ public final PDFRenderer getRenderer() /** * Returns the underlying Graphics2D. May be null if drawPage has not yet been called. - * + * * @return the underlying Graphics2D */ protected final Graphics2D getGraphics() @@ -244,7 +245,7 @@ protected final Graphics2D getGraphics() /** * Returns the current line path. This is reset to empty after each fill/stroke. - * + * * @return the current line path */ protected final GeneralPath getLinePath() @@ -262,7 +263,7 @@ private void setRenderingHints() /** * Draws the page to the requested context. - * + * * @param g The graphics context to draw onto. * @param pageSize The size of the page to draw. * @throws IOException If there is an IO error while drawing the page. @@ -320,13 +321,13 @@ void drawTilingPattern(Graphics2D g, PDTilingPattern pattern, PDColorSpace color lastClips = null; Shape savedInitialClip = initialClip; initialClip = null; - + boolean savedFlipTG = flipTG; flipTG = true; setRenderingHints(); processTilingPattern(pattern, color, colorSpace, patternMatrix); - + flipTG = savedFlipTG; graphics = savedGraphics; linePath = savedLinePath; @@ -337,15 +338,15 @@ void drawTilingPattern(Graphics2D g, PDTilingPattern pattern, PDColorSpace color private float clampColor(float color) { - return color < 0 ? 0 : (color > 1 ? 1 : color); + return color < 0 ? 0 : (color > 1 ? 1 : color); } /** * Returns an AWT paint for the given PDColor. - * + * * @param color The color to get a paint for. This can be an actual color or a pattern. * @return an AWT paint for the given PDColor - * + * * @throws IOException if the AWT paint could not be created */ protected Paint getPaint(PDColor color) throws IOException @@ -384,7 +385,7 @@ else if (!(colorSpace instanceof PDPattern)) else { // uncolored tiling pattern - return tilingPaintFactory.create(tilingPattern, + return tilingPaintFactory.create(tilingPattern, patternSpace.getUnderlyingColorSpace(), color, xform); } } @@ -460,7 +461,7 @@ public void endText() throws IOException { endTextClip(); } - + /** * Begin buffering the text clipping path, if any. */ @@ -477,7 +478,7 @@ private void endTextClip() { PDGraphicsState state = getGraphicsState(); RenderingMode renderingMode = state.getTextState().getRenderingMode(); - + // apply the buffered clip as one area if (renderingMode.isClip() && !textClippings.isEmpty()) { @@ -488,7 +489,7 @@ private void endTextClip() state.intersectClippingPath(path); textClippings = new ArrayList<>(); - // PDFBOX-3681: lastClip needs to be reset, because after intersection it is still the same + // PDFBOX-3681: lastClip needs to be reset, because after intersection it is still the same // object, thus setClip() would believe that it is cached. lastClips = null; } @@ -510,13 +511,54 @@ protected void showFontGlyph(Matrix textRenderingMatrix, PDFont font, int code, glyphCaches.put(font, cache); } - GeneralPath path = cache.getPathForCharacterCode(code); + // Grid-fitting is off by default; TrueTypeFont.SYSPROP_HINTING + // ("-Dorg.apache.fontbox.ttf.hinting=true") or TrueTypeFont.setHintingEnabled(true) turns it + // on. While it is off we never derive a ppem and take the plain, code-keyed cache path, so the + // feature costs nothing when disabled. + int ppem = 0; + if (TrueTypeFont.isHintingEnabled()) + { + // hintingPpem expects the glyph-space-to-device transform, but 'at' only maps glyph space + // to PDF user space (points) - the device scale lives in 'xform', which the Graphics2D + // applies separately. Compose it in so the ppem is the true device pixels-per-em; + // otherwise we grid-fit at the font's point size (e.g. 7) instead of its rendered size + // (e.g. 29 at 300dpi). + AffineTransform deviceAt = at; + if (xform != null) + { + deviceAt = new AffineTransform(xform); + deviceAt.concatenate(at); + } + ppem = hintingPpem(deviceAt); + } + GeneralPath path = ppem > 0 ? cache.getPathForCharacterCode(code, ppem) + : cache.getPathForCharacterCode(code); drawGlyph(path, font, code, displacement, at); } + /** + * Derives the pixels-per-em for grid-fitting from the glyph-space-to-device transform, or returns + * 0 when the glyph is too small / degenerate to hint. The ppem is the magnitude of the transform's + * vertical basis vector, i.e. the device height of one em, so it is correct under rotation: the + * glyph is grid-fit in its own (upright) coordinate space and the full transform — including any + * rotation — is then applied to the hinted outline by the caller, exactly as FreeType does for + * rotated text (90-degree vertical CJK columns being the common case). The path fed through + * {@code at} is normalized to 1000 units/em, so one em is 1000 units in {@code at}'s input space. + * + * @param at the transform mapping normalized (1000/em) glyph coordinates to device space + * @return the ppem to hint at, or 0 to render unhinted + */ + static int hintingPpem(AffineTransform at) + { + // length of the y basis vector = device pixels per normalized unit, rotation-invariant + double scaleY = Math.hypot(at.getShearX(), at.getScaleY()); + int ppem = (int) Math.round(1000.0 * scaleY); + return ppem > 0 ? ppem : 0; + } + /** * Renders a glyph. - * + * * @param path the GeneralPath for the glyph * @param font the font * @param code character code @@ -625,7 +667,7 @@ private Paint applySoftMaskToPaint(Paint parentPaint, PDSoftMask softMask) throw } } } - TransparencyGroup transparencyGroup = new TransparencyGroup(form, true, + TransparencyGroup transparencyGroup = new TransparencyGroup(form, true, softMask.getInitialTransformationMatrix(), backdropColor); BufferedImage image = transparencyGroup.getImage(); if (image == null) @@ -663,7 +705,7 @@ private BufferedImage adjustImage(BufferedImage gray) Rectangle originalBounds = new Rectangle(gray.getWidth(), gray.getHeight()); Rectangle2D transformedBounds = at.createTransformedShape(originalBounds).getBounds2D(); - at.preConcatenate(AffineTransform.getTranslateInstance(-transformedBounds.getMinX(), + at.preConcatenate(AffineTransform.getTranslateInstance(-transformedBounds.getMinX(), -transformedBounds.getMinY())); int width = (int) Math.ceil(transformedBounds.getWidth()); @@ -851,7 +893,7 @@ public void fillPath(int windingRule) throws IOException graphics.setPaint(getNonStrokingPaint()); graphics.fill(shape); } - + linePath.reset(); if (noAntiAlias) @@ -973,7 +1015,7 @@ public void clip(int windingRule) getGraphicsState().intersectClippingPath(adjustClip(linePath)); } - // PDFBOX-3836: lastClip needs to be reset, because after intersection it is still the same + // PDFBOX-3836: lastClip needs to be reset, because after intersection it is still the same // object, thus setClip() would believe that it is cached. lastClips = null; @@ -1016,7 +1058,7 @@ public void endPath() { linePath.reset(); } - + /** * PDFBOX-5715 / PR#73: This was added to fix a problem with missing fine lines when printing * on MacOS. Lines vanish because CPrinterJob sets graphics scale to 1 for Printable so after @@ -1028,7 +1070,7 @@ public void endPath() * here. * * @param linePath - * @return + * @return */ private GeneralPath adjustClip(GeneralPath linePath) { @@ -1125,10 +1167,10 @@ public void drawImage(PDImage pdImage) throws IOException { // The earlier code for stencils (see "else") doesn't work with patterns because the // CTM is not taken into consideration. - // this code is based on the fact that it is easily possible to draw the mask and + // this code is based on the fact that it is easily possible to draw the mask and // the paint at the correct place with the existing code, but not in one step. // Thus what we do is to draw both in separate images, then combine the two and draw - // the result. + // the result. // Note that the device scale is not used. In theory, some patterns can get better // at higher resolutions but the stencil would become more and more "blocky". // If anybody wants to do this, have a look at the code in showTransparencyGroup(). @@ -1511,7 +1553,7 @@ private void drawBufferedImage(PDImage pdImage, BufferedImage image, AffineTrans // will trigger the workaround. Because of the slowness we only do it if the user // expects quality rendering and interpolation. Matrix imageTransformMatrix = new Matrix(imageTransform); - Matrix graphicsTransformMatrix = new Matrix(originalTransform); + Matrix graphicsTransformMatrix = new Matrix(originalTransform); float scaleX = Math.abs(imageTransformMatrix.getScalingFactorX() * graphicsTransformMatrix.getScalingFactorX()); float scaleY = Math.abs(imageTransformMatrix.getScalingFactorY() * graphicsTransformMatrix.getScalingFactorY()); @@ -1580,7 +1622,7 @@ private BufferedImage applyTransferFunction(BufferedImage image, COSBase transfe bim = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB); } - // prepare transfer functions (either one per color or one for all) + // prepare transfer functions (either one per color or one for all) // and maps (actually arrays[256] to be faster) to avoid calculating values several times Integer[] rMap; Integer[] gMap; @@ -1840,7 +1882,7 @@ public void showTransparencyGroup(PDTransparencyGroup form) throws IOException /** * For advanced users, to extract the transparency group into a separate graphics device. - * + * * @param form the transparency group to be extracted * @param graphics the target graphics device * @throws IOException if the transparency group could not be extracted @@ -1944,7 +1986,7 @@ private final class TransparencyGroup * masks. * @throws IOException */ - private TransparencyGroup(PDTransparencyGroup form, boolean isSoftMask, Matrix ctm, + private TransparencyGroup(PDTransparencyGroup form, boolean isSoftMask, Matrix ctm, PDColor backdropColor) throws IOException { Graphics2D savedGraphics = graphics; @@ -2049,8 +2091,8 @@ private TransparencyGroup(PDTransparencyGroup form, boolean isSoftMask, Matrix c } if (isSoftMask && backdropColor != null) { - // "If the subtype is Luminosity, the transparency group XObject G shall be - // composited with a fully opaque backdrop whose colour is everywhere defined + // "If the subtype is Luminosity, the transparency group XObject G shall be + // composited with a fully opaque backdrop whose colour is everywhere defined // by the soft-mask dictionary's BC entry." g.setBackground(new Color(backdropColor.toRGB())); g.clearRect(0, 0, width, height); @@ -2103,7 +2145,7 @@ private TransparencyGroup(PDTransparencyGroup form, boolean isSoftMask, Matrix c ((GroupGraphics) graphics).removeBackdrop(backdropImage, backdropX, backdropY); } } - finally + finally { flipTG = savedFlipTG; lastClips = savedLastClips; @@ -2118,7 +2160,7 @@ private TransparencyGroup(PDTransparencyGroup form, boolean isSoftMask, Matrix c } // http://stackoverflow.com/a/21181943/535646 - private BufferedImage create2ByteGrayAlphaImage(int width, int height) + private BufferedImage create2ByteGrayAlphaImage(int width, int height) { // gray + alpha int[] bandOffsets = {1, 0}; @@ -2339,7 +2381,7 @@ private boolean isHiddenOCMD(PDOptionalContentMembershipDictionary ocmd) List visibles = new ArrayList<>(oCGs.size()); oCGs.forEach(prop -> visibles.add(!isHiddenOCG(prop))); COSName visibilityPolicy = ocmd.getVisibilityPolicy(); - + // visible if any of the entries in OCGs are OFF if (COSName.ANY_OFF.equals(visibilityPolicy)) { diff --git a/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFontHintingTest.java b/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFontHintingTest.java new file mode 100644 index 00000000000..1480a9d5a4f --- /dev/null +++ b/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFontHintingTest.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.font; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; +import java.awt.geom.Rectangle2D; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; + +import org.apache.fontbox.ttf.TrueTypeFont; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.font.encoding.WinAnsiEncoding; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Isolated; + +/** + * Verifies the render-path hinting wiring at the font level: an embedded TrueType font returns a + * grid-fitted normalized path that differs from the unhinted one, is in the same 1000/em space, and + * respects the gasp gate. + */ +@Isolated // TrueTypeFont hinting is a global switch; other classes must not render while it is on +class PDTrueTypeFontHintingTest +{ + // hinting is off by default, so these tests have to turn the feature on first + @BeforeEach + void enableHinting() + { + TrueTypeFont.setHintingEnabled(true); + } + + @AfterEach + void restoreHinting() + { + TrueTypeFont.setHintingEnabled(false); + } + + private static final File FONT = + new File("src/test/resources/org/apache/pdfbox/ttf/LiberationSans-Regular.ttf"); + + @Test + void testHintedNormalizedPathDiffersFromUnhinted() throws IOException + { + try (PDDocument doc = new PDDocument()) + { + PDTrueTypeFont font = PDTrueTypeFont.load(doc, FONT, WinAnsiEncoding.INSTANCE); + int code = 'H'; + + GeneralPath hinted = font.getHintedNormalizedPath(code, 16); + assertNotNull(hinted, "expected a hinted path for 'H' at 16ppem"); + GeneralPath unhinted = font.getNormalizedPath(code); + + // hinting must change the outline + assertFalse(Arrays.equals(flatten(hinted), flatten(unhinted)), + "hinted path should differ from the unhinted path"); + + // and it must still be in the 1000-unit em square (an 'H' cap height is several hundred) + Rectangle2D bounds = hinted.getBounds2D(); + assertTrue(bounds.getMaxY() > 300 && bounds.getMaxY() < 1000, + "hinted path should be normalized to 1000/em, was maxY=" + bounds.getMaxY()); + } + } + + @Test + void testGaspGateReturnsNullAtSmallPpem() throws IOException + { + try (PDDocument doc = new PDDocument()) + { + PDTrueTypeFont font = PDTrueTypeFont.load(doc, FONT, WinAnsiEncoding.INSTANCE); + // LiberationSans gasp disables grid-fitting at <= 8 ppem + assertNull(font.getHintedNormalizedPath('H', 8)); + assertNotNull(font.getHintedNormalizedPath('H', 16)); + } + } + + /** Only an embedded outline carries bytecode we can execute; a substituted font must not hint. */ + @Test + void testNonEmbeddedFontDoesNotHint() throws IOException + { + COSDictionary dict = new COSDictionary(); + dict.setItem(COSName.TYPE, COSName.FONT); + dict.setItem(COSName.SUBTYPE, COSName.TRUE_TYPE); + dict.setName(COSName.BASE_FONT, "Helvetica"); + + PDTrueTypeFont font = new PDTrueTypeFont(dict, null); + assertFalse(font.isEmbedded(), "font should not be embedded"); + // the substituted font still draws, so a null hinted path is the embedded check talking + // rather than a font that cannot produce an outline at all + assertFalse(font.getNormalizedPath('H').getPathIterator(null).isDone(), + "expected the substitute font to produce an outline"); + assertNull(font.getHintedNormalizedPath('H', 16)); + } + + private static double[] flatten(GeneralPath path) + { + double[] coords = new double[6]; + java.util.List out = new java.util.ArrayList<>(); + for (PathIterator it = path.getPathIterator(null); !it.isDone(); it.next()) + { + out.add((double) it.currentSegment(coords)); + for (double c : coords) + { + out.add(c); + } + } + double[] array = new double[out.size()]; + for (int i = 0; i < array.length; i++) + { + array[i] = out.get(i); + } + return array; + } +} diff --git a/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDType0FontHintingTest.java b/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDType0FontHintingTest.java new file mode 100644 index 00000000000..194f8b3fa5f --- /dev/null +++ b/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDType0FontHintingTest.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.font; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; +import java.awt.geom.Rectangle2D; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.fontbox.ttf.TTFParser; +import org.apache.fontbox.ttf.TrueTypeFont; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.io.RandomAccessReadBufferedFile; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Isolated; + +/** + * The CID half of the render-path hinting wiring. {@link PDCIDFontType2#getHintedNormalizedPath} is a + * near-copy of the {@link PDTrueTypeFont} one but had no test of its own, and {@link PDType0Font} + * forwards to it only when the descendant really is a CIDFontType2. + */ +@Isolated // TrueTypeFont hinting is a global switch; other classes must not render while it is on +class PDType0FontHintingTest +{ + private static final File FONT = + new File("src/test/resources/org/apache/pdfbox/ttf/LiberationSans-Regular.ttf"); + + @BeforeEach + void enableHinting() + { + TrueTypeFont.setHintingEnabled(true); + } + + @AfterEach + void restoreHinting() + { + TrueTypeFont.setHintingEnabled(false); + } + + /** + * Embeds the font whole (no subsetting) so the encoding is Identity and a character code is its + * own glyph id, which keeps the test about hinting rather than about CID mapping. + */ + private static PDType0Font load(PDDocument doc, int[] gidOut) throws IOException + { + TrueTypeFont ttf = new TTFParser().parse(new RandomAccessReadBufferedFile(FONT)); + gidOut[0] = ttf.getUnicodeCmapLookup().getGlyphId('H'); + assertTrue(gidOut[0] > 0, "no glyph for 'H'"); + return PDType0Font.load(doc, ttf, false); + } + + @Test + void testHintedNormalizedPathDiffersFromUnhinted() throws IOException + { + try (PDDocument doc = new PDDocument()) + { + int[] gid = new int[1]; + PDType0Font font = load(doc, gid); + + GeneralPath hinted = font.getHintedNormalizedPath(gid[0], 16); + assertNotNull(hinted, "expected a hinted path for 'H' at 16ppem"); + GeneralPath unhinted = font.getNormalizedPath(gid[0]); + + assertFalse(Arrays.equals(flatten(hinted), flatten(unhinted)), + "hinted path should differ from the unhinted path"); + + // and it must still be in the 1000-unit em square (an 'H' cap height is several hundred) + Rectangle2D bounds = hinted.getBounds2D(); + assertTrue(bounds.getMaxY() > 300 && bounds.getMaxY() < 1000, + "hinted path should be normalized to 1000/em, was maxY=" + bounds.getMaxY()); + } + } + + @Test + void testGaspGateReturnsNullAtSmallPpem() throws IOException + { + try (PDDocument doc = new PDDocument()) + { + int[] gid = new int[1]; + PDType0Font font = load(doc, gid); + // LiberationSans gasp disables grid-fitting at <= 8 ppem + assertNull(font.getHintedNormalizedPath(gid[0], 8)); + assertNotNull(font.getHintedNormalizedPath(gid[0], 16)); + } + } + + /** Only an embedded outline carries bytecode we can execute; a substituted font must not hint. */ + @Test + void testNonEmbeddedFontDoesNotHint() throws IOException + { + PDType0Font font = nonEmbedded(); + assertFalse(font.getDescendantFont().isEmbedded(), "font should not be embedded"); + // the substituted font still draws, so a null hinted path is the embedded check talking + // rather than a font that cannot produce an outline at all + int gid = font.getDescendantFont().codeToGID('H', font); + assertFalse(font.getNormalizedPath(gid).getPathIterator(null).isDone(), + "expected the substitute font to produce an outline"); + assertNull(font.getHintedNormalizedPath(gid, 16)); + } + + /** Builds the Type0/CIDFontType2 dictionary pair a PDF uses when it does not embed the font. */ + private static PDType0Font nonEmbedded() throws IOException + { + COSDictionary cid = new COSDictionary(); + cid.setItem(COSName.TYPE, COSName.FONT); + cid.setItem(COSName.SUBTYPE, COSName.CID_FONT_TYPE2); + cid.setName(COSName.BASE_FONT, "Helvetica"); + + COSArray descendants = new COSArray(); + descendants.add(cid); + + COSDictionary type0 = new COSDictionary(); + type0.setItem(COSName.TYPE, COSName.FONT); + type0.setItem(COSName.SUBTYPE, COSName.TYPE0); + type0.setName(COSName.BASE_FONT, "Helvetica"); + type0.setItem(COSName.ENCODING, COSName.IDENTITY_H); + type0.setItem(COSName.DESCENDANT_FONTS, descendants); + return new PDType0Font(type0, null); + } + + private static double[] flatten(GeneralPath path) + { + double[] coords = new double[6]; + List out = new ArrayList<>(); + for (PathIterator it = path.getPathIterator(null); !it.isDone(); it.next()) + { + out.add((double) it.currentSegment(coords)); + for (double c : coords) + { + out.add(c); + } + } + double[] array = new double[out.size()]; + for (int i = 0; i < array.length; i++) + { + array[i] = out.get(i); + } + return array; + } +} diff --git a/pdfbox/src/test/java/org/apache/pdfbox/rendering/HintingPpemTest.java b/pdfbox/src/test/java/org/apache/pdfbox/rendering/HintingPpemTest.java new file mode 100644 index 00000000000..63772a124dc --- /dev/null +++ b/pdfbox/src/test/java/org/apache/pdfbox/rendering/HintingPpemTest.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.rendering; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.awt.geom.AffineTransform; + +import org.junit.jupiter.api.Test; + +/** + * Tests {@link PageDrawer#hintingPpem(AffineTransform)} - the derivation of the grid-fitting ppem from + * the glyph-space-to-device transform, including the rule that rotated/sheared transforms are not + * hinted. The input transform maps 1000-units-per-em glyph coordinates to device pixels. + */ +class HintingPpemTest +{ + @Test + void testUprightScaleGivesPpem() + { + // 16px em: one normalized unit is 16/1000 device pixels + assertEquals(16, PageDrawer.hintingPpem(AffineTransform.getScaleInstance(0.016, 0.016))); + assertEquals(11, PageDrawer.hintingPpem(AffineTransform.getScaleInstance(0.011, 0.011))); + } + + @Test + void testYFlipIsStillHinted() + { + // typical PDF-to-device flip has a negative y scale but is still axis-aligned + AffineTransform at = new AffineTransform(0.024, 0, 0, -0.024, 100, 200); + assertEquals(24, PageDrawer.hintingPpem(at)); + } + + @Test + void testAnisotropicUsesVerticalPpem() + { + // ppem is taken from the vertical scale + AffineTransform at = new AffineTransform(0.020, 0, 0, -0.016, 0, 0); + assertEquals(16, PageDrawer.hintingPpem(at)); + } + + @Test + void testRotationIsStillHinted() + { + // rotated text (e.g. 90-degree vertical CJK) is grid-fit in upright glyph space at the + // transform's scale; the rotation is applied afterwards. ppem is the rotation-invariant scale. + AffineTransform at = AffineTransform.getScaleInstance(0.016, 0.016); + at.rotate(Math.toRadians(30)); + assertEquals(16, PageDrawer.hintingPpem(at)); + + AffineTransform vertical = new AffineTransform(0, 0.016, -0.016, 0, 0, 0); // 90-degree rotation + assertEquals(16, PageDrawer.hintingPpem(vertical)); + } + + @Test + void testShearTakesVerticalScale() + { + // a sheared (fake-italic) transform is hinted at its vertical-basis magnitude + AffineTransform at = new AffineTransform(0.016, 0, 0.006, 0.016, 0, 0); + assertEquals((int) Math.round(1000 * Math.hypot(0.006, 0.016)), PageDrawer.hintingPpem(at)); + } + + @Test + void testDegenerateTransformIsNotHinted() + { + assertEquals(0, PageDrawer.hintingPpem(AffineTransform.getScaleInstance(0, 0))); + // sub-half-pixel em rounds to 0 ppem -> no hinting + assertEquals(0, PageDrawer.hintingPpem(AffineTransform.getScaleInstance(0.0004, 0.0004))); + } +} diff --git a/pdfbox/src/test/java/org/apache/pdfbox/rendering/RenderHintingIntegrationTest.java b/pdfbox/src/test/java/org/apache/pdfbox/rendering/RenderHintingIntegrationTest.java new file mode 100644 index 00000000000..0dc39d2c11d --- /dev/null +++ b/pdfbox/src/test/java/org/apache/pdfbox/rendering/RenderHintingIntegrationTest.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.rendering; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +import org.apache.fontbox.ttf.TrueTypeFont; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.font.PDTrueTypeFont; +import org.apache.pdfbox.pdmodel.font.encoding.WinAnsiEncoding; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Isolated; + +/** + * Proves that hinting is actually wired into the render path: when hinting is enabled, a page of + * embedded TrueType text rasterizes to a different image than with it disabled. + */ +@Isolated // TrueTypeFont hinting is a global switch; other classes must not render while it is on +class RenderHintingIntegrationTest +{ + private static final File FONT = + new File("src/test/resources/org/apache/pdfbox/ttf/LiberationSans-Regular.ttf"); + + @AfterEach + void restoreHinting() + { + TrueTypeFont.setHintingEnabled(false); + } + + @Test + void testHintingChangesRenderedPixels() throws IOException + { + byte[] pdf = buildPdf(); + BufferedImage off = render(pdf); + TrueTypeFont.setHintingEnabled(true); + BufferedImage on = render(pdf); + + assertEquals(off.getWidth(), on.getWidth()); + assertEquals(off.getHeight(), on.getHeight()); + assertTrue(countDifferences(off, on) > 0, + "enabling hinting should change the rendered glyph pixels"); + } + + /** A page must rasterize identically across two renders when hinting stays disabled. */ + @Test + void testDisabledHintingIsDeterministic() throws IOException + { + byte[] pdf = buildPdf(); + assertEquals(0, countDifferences(render(pdf), render(pdf))); + } + + private static byte[] buildPdf() throws IOException + { + try (PDDocument doc = new PDDocument()) + { + PDPage page = new PDPage(new PDRectangle(160, 60)); + doc.addPage(page); + PDTrueTypeFont font = PDTrueTypeFont.load(doc, FONT, WinAnsiEncoding.INSTANCE); + try (PDPageContentStream cs = new PDPageContentStream(doc, page)) + { + cs.beginText(); + cs.setFont(font, 11); + cs.newLineAtOffset(8, 24); + cs.showText("Hamburgefons 123"); + cs.endText(); + } + java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream(); + doc.save(out); + return out.toByteArray(); + } + } + + private static BufferedImage render(byte[] pdf) throws IOException + { + try (PDDocument doc = org.apache.pdfbox.Loader.loadPDF(pdf)) + { + return new PDFRenderer(doc).renderImageWithDPI(0, 96); + } + } + + private static int countDifferences(BufferedImage a, BufferedImage b) + { + int diff = 0; + for (int y = 0; y < a.getHeight(); y++) + { + for (int x = 0; x < a.getWidth(); x++) + { + if (a.getRGB(x, y) != b.getRGB(x, y)) + { + diff++; + } + } + } + return diff; + } +} diff --git a/pdfbox/src/test/resources/org/apache/pdfbox/ttf/LiberationSans-Regular.ttf b/pdfbox/src/test/resources/org/apache/pdfbox/ttf/LiberationSans-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e6339859d0b24bee79ae3f64e0071900170224ba GIT binary patch literal 410712 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 U^4IX6nfTvc!IeM%9f$n?0IO~PegFUf literal 0 HcmV?d00001 From 00c6ce0b3d0805f5d0a28ce4dde06573dfddd3c4 Mon Sep 17 00:00:00 2001 From: Patrick Corless Date: Tue, 8 Sep 2026 20:55:03 -0600 Subject: [PATCH 5/8] PDFBOX-3293: verify hinted output against FreeType Unit tests establish that each opcode does what the specification says. They cannot establish that a glyph comes out where FreeType puts it, which is the only definition of correct that matters here, so the output is compared against FreeType directly. ttf/hinting/ holds the harness: a small C program links against FreeType and dumps the grid-fitted points of a glyph, a Python script drives it to produce the reference files, and a second script diffs a per-instruction trace from this interpreter against the equivalent FreeType trace to localise a divergence to the instruction that caused it. The reference files are checked in so the tests need neither FreeType nor Python to run; the README records how to regenerate them, and the generator emits the license header so a regeneration does not drop it. GoldenHintingTest compares LiberationSans at 11, 13, 16 and 24 ppem against those references. Every x coordinate agrees to within one 64th of a pixel for simple and composite glyphs alike. Vertical positions are checked for grid alignment without collapse rather than for exact equality, since the y axis is where the v40 backward-compatibility rules deliberately diverge. The harness found six bugs that the unit tests could not: interpolation using scaled rather than unscaled originals, grid-rounded composite component offsets, SHP/SHC/SHZ moving the reference point, DIV rounding instead of truncating, swapped MDRP/MIRP round and minimum-distance flag bits, and composite component originals taken from the unhinted rather than the assembled outline. GlyphTraceTool is the developer entry point for the trace side; it is not a test and asserts nothing. Co-Authored-By: Claude Opus 5 --- .../apache/fontbox/ttf/GlyphTraceTool.java | 72 +++++ .../apache/fontbox/ttf/GoldenHintingTest.java | 248 ++++++++++++++++++ .../ttf/hinting/LiberationSans-Regular-11.txt | 149 +++++++++++ .../ttf/hinting/LiberationSans-Regular-13.txt | 149 +++++++++++ .../ttf/hinting/LiberationSans-Regular-16.txt | 149 +++++++++++ .../ttf/hinting/LiberationSans-Regular-24.txt | 149 +++++++++++ .../src/test/resources/ttf/hinting/README.md | 103 ++++++++ .../resources/ttf/hinting/ft_point_trace.c | 109 ++++++++ .../resources/ttf/hinting/generate_golden.py | 108 ++++++++ .../test/resources/ttf/hinting/trace_diff.py | 150 +++++++++++ 10 files changed, 1386 insertions(+) create mode 100644 fontbox/src/test/java/org/apache/fontbox/ttf/GlyphTraceTool.java create mode 100644 fontbox/src/test/java/org/apache/fontbox/ttf/GoldenHintingTest.java create mode 100644 fontbox/src/test/resources/ttf/hinting/LiberationSans-Regular-11.txt create mode 100644 fontbox/src/test/resources/ttf/hinting/LiberationSans-Regular-13.txt create mode 100644 fontbox/src/test/resources/ttf/hinting/LiberationSans-Regular-16.txt create mode 100644 fontbox/src/test/resources/ttf/hinting/LiberationSans-Regular-24.txt create mode 100644 fontbox/src/test/resources/ttf/hinting/README.md create mode 100644 fontbox/src/test/resources/ttf/hinting/ft_point_trace.c create mode 100644 fontbox/src/test/resources/ttf/hinting/generate_golden.py create mode 100644 fontbox/src/test/resources/ttf/hinting/trace_diff.py diff --git a/fontbox/src/test/java/org/apache/fontbox/ttf/GlyphTraceTool.java b/fontbox/src/test/java/org/apache/fontbox/ttf/GlyphTraceTool.java new file mode 100644 index 00000000000..ac9f7ec947f --- /dev/null +++ b/fontbox/src/test/java/org/apache/fontbox/ttf/GlyphTraceTool.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import java.io.InputStream; +import java.io.PrintStream; + +import org.apache.pdfbox.io.RandomAccessReadBuffer; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; + +/** + * Developer tool (not a real test): dumps the interpreter's per-instruction execution trace for one + * glyph, to be diffed against a FreeType {@code ttinterp} trace by {@code trace_diff.py}. Skipped + * unless the {@code trace.gid} system property is set, e.g. + * + *

+ *   mvn -pl fontbox test -Dtest=GlyphTraceTool \
+ *       -Dtrace.font=src/test/resources/ttf/LiberationSans-Regular.ttf \
+ *       -Dtrace.gid=22 -Dtrace.ppem=11 -Dtrace.out=/tmp/our-trace.txt
+ * 
+ */ +class GlyphTraceTool +{ + @Test + void dumpTrace() throws Exception + { + String gidProp = System.getProperty("trace.gid"); + Assumptions.assumeTrue(gidProp != null, "set -Dtrace.gid to dump a trace"); + + int gid = Integer.parseInt(gidProp); + int ppem = Integer.parseInt(System.getProperty("trace.ppem", "11")); + int point = Integer.parseInt(System.getProperty("trace.point", "-1")); + String fontPath = System.getProperty("trace.font", + "src/test/resources/ttf/LiberationSans-Regular.ttf"); + String outPath = System.getProperty("trace.out"); + + TrueTypeFont font; + try (InputStream is = new java.io.FileInputStream(fontPath)) + { + // isEmbedded=true tolerates subset fonts that drop the otherwise-mandatory 'post' table + font = new TTFParser(true).parse(new RandomAccessReadBuffer(is)); + } + + PrintStream out = outPath != null ? new PrintStream(outPath, "UTF-8") : System.out; + try + { + new GlyphHinter(font).traceGlyph(gid, ppem, out, point); + } + finally + { + if (outPath != null) + { + out.close(); + } + } + } +} diff --git a/fontbox/src/test/java/org/apache/fontbox/ttf/GoldenHintingTest.java b/fontbox/src/test/java/org/apache/fontbox/ttf/GoldenHintingTest.java new file mode 100644 index 00000000000..ae318b8ae1f --- /dev/null +++ b/fontbox/src/test/java/org/apache/fontbox/ttf/GoldenHintingTest.java @@ -0,0 +1,248 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.io.RandomAccessReadBuffer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Golden (Tier 3) test comparing the FontBox interpreter's grid-fitted glyph points against a FreeType + * reference dump (same font, glyph and ppem). The reference lives in {@code ttf/hinting/-.txt}, + * produced offline by {@code generate_golden.py}; FreeType is never a build or runtime dependency. + *

+ * The reference is generated with FreeType's grayscale target ({@code FT_LOAD_TARGET_NORMAL}), + * i.e. the v40 "minimal" subpixel interpreter with backward compatibility, because PDFBox always + * rasterizes antialiased (Java2D). That mode is the right target for appearance but is a pile of + * heuristics rather than a clean algorithm, so the assertions here are property based rather than + * byte-exact coordinate matching: + *

    + *
  • Horizontal grid-fitting matches FreeType to within 1/64 px on every coordinate. This is + * the part that matters for weight: backward compatibility suppresses x grid-fitting so stems are + * not darkened, and we reproduce it exactly.
  • + *
  • Vertical extent tracks FreeType (the glyph bounding box matches within about half a pixel), + * so the baseline/cap snap to the grid and nothing collapses. Interior y coordinates may differ by a + * fraction of a pixel because we do not replicate every grayscale backward-compatibility heuristic; + * a soft bound keeps the bulk of them close.
  • + *
+ * Coordinates are integer F26Dot6 (64 units per pixel). + */ +class GoldenHintingTest +{ + // hinting is off by default, so the golden comparison has to turn the feature on first + @BeforeEach + void enableHinting() + { + TrueTypeFont.setHintingEnabled(true); + } + + @AfterEach + void restoreHinting() + { + TrueTypeFont.setHintingEnabled(false); + } + + private static final int[] PPEMS = { 11, 13, 16, 24 }; + + /** + * Horizontal grid-fitting is an exact match to FreeType's grayscale (v40) output: with backward + * compatibility, x moves are suppressed so horizontal stems keep their sub-pixel position and are not + * darkened by antialiasing. Every x coordinate agrees to within 1/64 px for simple and composite + * glyphs alike. + */ + @Test + void testHorizontalGridFittingMatchesFreeType() throws IOException + { + Stats simple = compare(false); + Stats composite = compare(true); + assertTrue(simple.maxDx <= 1, simple.summary("simple")); + assertTrue(composite.maxDx <= 1, composite.summary("composite")); + } + + /** + * Vertical hinting snaps the glyph to the pixel grid without collapsing it: the hinted y bounding box + * matches FreeType within one pixel (so the baseline and cap/x-height land on grid rows, and a + * degenerate outline - the symptom of the twilight-zone IP bug - would be caught), and the majority of + * interior y coordinates stay within 1/64 px of FreeType. + */ + @Test + void testVerticalHintingTracksFreeTypeWithoutCollapse() throws IOException + { + Stats simple = compare(false); + Stats composite = compare(true); + assertTrue(simple.maxBboxYDelta <= 64, simple.summary("simple")); + assertTrue(composite.maxBboxYDelta <= 64, composite.summary("composite")); + assertTrue(simple.yWithinOnePercent() >= 50, simple.summary("simple")); + assertTrue(composite.yWithinOnePercent() >= 50, composite.summary("composite")); + } + + private Stats compare(boolean composite) throws IOException + { + TrueTypeFont font; + try (InputStream is = getClass().getResourceAsStream("/ttf/LiberationSans-Regular.ttf")) + { + font = new TTFParser().parse(new RandomAccessReadBuffer(is)); + } + GlyphHinter hinter = new GlyphHinter(font); + Stats s = new Stats(); + + for (int ppem : PPEMS) + { + List golden = loadGolden("/ttf/hinting/LiberationSans-Regular-" + ppem + ".txt"); + assertNotNull(golden); + for (GoldenGlyph g : golden) + { + boolean isComposite = font.getGlyph().getGlyph(g.gid).getNumberOfContours() < 0; + if (isComposite != composite) + { + continue; + } + int[][] points = hinter.getHintedPointsF26Dot6(g.gid, ppem); + assertNotNull(points, "no hinted points for gid " + g.gid + " at " + ppem + "ppem"); + assertTrue(points[0].length == g.x.length, + "point count mismatch for '" + g.ch + "' at " + ppem + "ppem: ours=" + + points[0].length + " freetype=" + g.x.length); + s.recordGlyph(g, ppem, points); + } + } + return s; + } + + private static final class Stats + { + private int compared; + private int yWithinOne; + private int maxDx; + private int maxDy; + private int maxBboxYDelta; + private String worstWhere = "none"; + + void recordGlyph(GoldenGlyph g, int ppem, int[][] points) + { + int ourMinY = Integer.MAX_VALUE; + int ourMaxY = Integer.MIN_VALUE; + int ftMinY = Integer.MAX_VALUE; + int ftMaxY = Integer.MIN_VALUE; + for (int i = 0; i < g.x.length; i++) + { + compared++; + maxDx = Math.max(maxDx, Math.abs(points[0][i] - g.x[i])); + int dy = Math.abs(points[1][i] - g.y[i]); + if (dy <= 1) + { + yWithinOne++; + } + if (dy > maxDy) + { + maxDy = dy; + worstWhere = "'" + g.ch + "' (gid " + g.gid + ") point " + i + " @" + ppem + + "ppem: ours=(" + points[0][i] + "," + points[1][i] + ") freetype=(" + + g.x[i] + "," + g.y[i] + ")"; + } + ourMinY = Math.min(ourMinY, points[1][i]); + ourMaxY = Math.max(ourMaxY, points[1][i]); + ftMinY = Math.min(ftMinY, g.y[i]); + ftMaxY = Math.max(ftMaxY, g.y[i]); + } + maxBboxYDelta = Math.max(maxBboxYDelta, + Math.max(Math.abs(ourMinY - ftMinY), Math.abs(ourMaxY - ftMaxY))); + } + + int yWithinOnePercent() + { + return compared == 0 ? 100 : 100 * yWithinOne / compared; + } + + String summary(String kind) + { + return kind + ": compared " + compared + " coords; maxDx=" + maxDx + "/64 maxDy=" + maxDy + + "/64 (y within 1/64 = " + yWithinOnePercent() + "%); max bbox-y delta " + + maxBboxYDelta + "/64; worst y at " + worstWhere; + } + } + + private List loadGolden(String resource) throws IOException + { + List glyphs = new ArrayList<>(); + try (InputStream is = getClass().getResourceAsStream(resource); + BufferedReader reader = new BufferedReader( + new InputStreamReader(is, StandardCharsets.UTF_8))) + { + GoldenGlyph current = null; + String line; + while ((line = reader.readLine()) != null) + { + if (line.startsWith("glyph ")) + { + String[] parts = line.split(" "); + current = new GoldenGlyph(Integer.parseInt(parts[1]), parts[2]); + glyphs.add(current); + } + else if (line.startsWith("x ")) + { + current.x = parseInts(line.substring(2)); + } + else if (line.startsWith("y ")) + { + current.y = parseInts(line.substring(2)); + } + } + } + return glyphs; + } + + private static int[] parseInts(String s) + { + if (s.isEmpty()) + { + return new int[0]; + } + String[] tokens = s.split(" "); + int[] values = new int[tokens.length]; + for (int i = 0; i < tokens.length; i++) + { + values[i] = Integer.parseInt(tokens[i]); + } + return values; + } + + private static final class GoldenGlyph + { + private final int gid; + private final String ch; + private int[] x; + private int[] y; + + GoldenGlyph(int gid, String ch) + { + this.gid = gid; + this.ch = ch; + } + } +} diff --git a/fontbox/src/test/resources/ttf/hinting/LiberationSans-Regular-11.txt b/fontbox/src/test/resources/ttf/hinting/LiberationSans-Regular-11.txt new file mode 100644 index 00000000000..cf92904ba8b --- /dev/null +++ b/fontbox/src/test/resources/ttf/hinting/LiberationSans-Regular-11.txt @@ -0,0 +1,149 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +font LiberationSans-Regular.ttf +ppem 11 +freetype 2.13.2 +glyph 43 H +contours 11 +x 385 385 123 123 58 58 123 123 385 385 451 451 +y 0 265 265 0 0 512 512 320 320 512 512 0 +glyph 44 I +contours 3 +x 65 65 131 131 +y 0 512 512 0 +glyph 47 L +contours 5 +x 58 58 123 123 368 368 +y 0 512 512 54 54 0 +glyph 40 E +contours 11 +x 58 58 425 425 123 123 405 405 123 123 439 439 +y 0 512 512 458 458 320 320 267 267 54 54 0 +glyph 55 T +contours 7 +x 248 248 182 182 16 16 414 414 +y 458 0 0 458 458 512 512 458 +glyph 75 h +contours 24 +x 109 129 185 228 289 346 346 346 284 284 284 270 237 207 164 111 111 111 49 49 111 111 111 108 108 +y 313 350 384 384 384 323 251 0 0 239 279 318 336 336 336 275 223 0 0 512 512 381 360 316 313 +glyph 82 o +contours 10,22 +x 362 362 276 194 113 30 30 30 197 282 362 297 297 251 198 143 95 95 95 143 194 249 297 +y 192 95 0 0 0 99 192 384 384 384 291 192 269 338 338 338 267 192 119 46 46 46 117 +glyph 91 x +contours 11 +x 275 175 75 8 140 14 83 175 267 337 210 344 +y 0 158 0 0 197 384 384 235 384 384 198 0 +glyph 81 n +contours 26 +x 284 284 284 270 238 207 163 111 111 111 49 49 49 47 105 106 106 107 108 109 130 186 228 289 346 346 346 +y 0 239 277 318 336 336 336 274 219 0 0 302 369 384 384 382 365 343 313 313 352 384 384 384 322 251 0 +glyph 19 0 +contours 11,23 +x 364 364 279 195 112 28 28 28 109 197 283 364 301 301 253 197 140 90 90 90 141 195 250 301 +y 256 131 0 0 0 131 256 384 512 512 512 383 256 365 462 462 462 366 256 150 51 51 51 152 +glyph 20 1 +contours 10 +x 54 54 177 177 68 68 182 239 239 357 357 +y 0 53 53 453 384 443 512 512 53 53 0 +glyph 21 2 +contours 30 +x 35 35 53 103 159 214 258 285 285 285 238 196 157 105 101 38 45 130 197 270 349 349 349 323 272 200 160 113 103 356 356 +y 0 44 86 150 202 247 291 340 371 414 461 461 461 418 379 385 443 512 512 512 440 373 344 286 227 166 132 78 53 53 0 +glyph 22 3 +contours 40 +x 361 361 276 197 123 35 27 91 103 196 243 296 296 296 235 178 143 143 177 228 284 284 284 238 193 152 101 97 35 42 127 193 266 347 347 347 295 246 246 300 361 +y 162 85 0 0 0 66 131 137 51 51 51 107 163 212 266 266 266 320 320 320 357 390 423 461 461 461 419 381 386 445 512 512 512 452 398 356 304 295 293 287 216 +glyph 23 4 +contours 10,18 +x 303 303 244 244 16 16 238 303 303 371 371 244 244 225 221 97 78 73 244 +y 143 0 0 143 143 190 512 512 192 192 143 443 441 409 403 224 199 192 192 +glyph 24 5 +contours 28 +x 362 362 271 190 122 39 28 91 111 192 242 298 298 298 241 193 168 125 103 42 58 334 334 115 106 146 206 277 362 +y 167 89 0 0 0 59 116 123 51 51 51 112 165 212 269 269 269 253 234 234 512 512 459 459 289 320 320 320 235 +glyph 25 6 +contours 22,34 +x 361 361 278 205 123 36 36 36 126 209 319 347 288 270 208 155 97 97 114 175 215 282 361 298 298 246 200 156 103 103 103 159 202 247 298 +y 165 89 0 0 0 125 244 374 512 512 512 413 402 462 462 462 356 256 287 320 320 320 236 163 215 272 272 272 222 177 121 50 50 50 110 +glyph 26 7 +contours 11 +x 356 282 221 190 190 125 125 204 296 36 36 356 +y 461 341 204 71 0 0 99 317 459 459 512 512 +glyph 27 8 +contours 25,36,47 +x 361 361 276 196 119 31 31 31 85 127 127 88 42 42 42 125 195 266 349 349 349 303 263 263 309 361 285 285 195 151 105 105 105 152 195 239 285 297 297 243 195 148 95 95 95 197 248 297 +y 161 85 0 0 0 83 160 214 287 295 296 305 361 399 449 512 512 512 451 399 361 305 298 296 288 216 394 466 466 466 430 394 358 320 320 320 355 166 220 274 274 274 216 165 46 46 46 104 +glyph 28 9 +contours 23,36 +x 358 358 267 183 126 58 43 102 121 184 237 296 297 283 217 177 111 33 33 33 118 194 275 358 291 291 237 192 148 96 96 96 148 192 218 265 291 +y 266 138 0 0 0 48 101 110 50 50 50 159 261 230 192 192 192 280 352 427 512 512 512 389 336 393 462 462 462 403 352 301 241 241 241 265 308 +glyph 163 á +contours 35,48,54 +x 142 86 30 30 30 106 190 274 274 274 235 194 153 115 111 46 62 196 266 337 337 337 337 351 372 381 392 392 369 344 309 278 276 274 250 187 157 191 243 274 274 274 206 163 118 94 94 94 127 142 142 217 288 288 174 +y 0 0 65 121 184 252 254 256 272 307 337 337 337 329 320 324 384 384 384 319 258 98 70 42 42 42 45 6 0 0 0 38 78 78 35 0 46 46 82 144 178 213 211 211 191 152 119 84 46 448 456 576 576 564 448 +glyph 162 à +contours 35,48,54 +x 142 86 30 30 30 106 190 274 274 274 235 194 153 115 111 46 62 196 266 337 337 337 337 351 372 381 392 392 369 344 309 278 276 274 250 187 157 191 243 274 274 274 206 163 118 94 94 94 127 215 101 101 173 247 247 +y 0 0 65 121 184 252 254 256 272 307 337 337 337 329 320 324 384 384 384 319 258 98 70 42 42 42 45 6 0 0 0 38 78 78 35 0 46 46 82 144 178 213 211 211 191 152 119 84 46 448 564 576 576 456 448 +glyph 164 â +contours 35,48,58 +x 142 86 30 30 30 106 190 274 274 274 235 194 153 115 111 46 62 196 266 337 337 337 337 351 372 381 392 392 369 344 309 278 276 274 250 187 157 191 243 274 274 274 206 163 118 94 94 94 127 303 303 267 191 191 111 75 75 155 226 +y 0 0 65 121 184 252 254 256 272 307 337 337 337 329 320 324 384 384 384 319 258 98 70 42 42 42 45 6 0 0 0 38 78 78 35 0 46 46 82 144 178 213 211 211 191 152 119 84 46 457 448 448 523 523 448 448 457 576 576 +glyph 166 ä +contours 35,48,52,56 +x 142 86 30 30 30 106 190 274 274 274 235 194 153 115 111 46 62 196 266 337 337 337 337 351 372 381 392 392 369 344 309 278 276 274 250 187 157 191 243 274 274 274 206 163 118 94 94 94 127 232 232 288 288 96 96 153 153 +y 0 0 65 121 184 252 254 256 272 307 337 337 337 329 320 324 384 384 384 319 258 98 70 42 42 42 45 6 0 0 0 38 78 78 35 0 46 46 82 144 178 213 211 211 191 152 119 84 46 448 511 511 448 448 511 511 448 +glyph 165 ã +contours 35,48,72 +x 142 86 30 30 30 106 190 274 274 274 235 194 153 115 111 46 62 196 266 337 337 337 337 351 372 381 392 392 369 344 309 278 276 274 250 187 157 191 243 274 274 274 206 163 118 94 94 94 127 253 239 210 183 159 148 129 110 107 76 80 96 124 146 161 190 217 241 251 285 291 323 317 283 +y 0 0 65 121 184 252 254 256 272 307 337 337 337 329 320 324 384 384 384 319 258 98 70 42 42 42 45 6 0 0 0 38 78 78 35 0 46 46 82 144 178 213 211 211 191 152 119 84 46 448 448 472 501 525 525 525 485 448 448 497 547 576 576 576 552 523 499 499 499 576 576 505 448 +glyph 171 é +contours 18,25,31 +x 95 95 148 199 239 287 296 350 317 199 116 30 30 30 116 196 360 360 360 296 291 242 195 150 98 96 152 152 227 298 298 184 +y 209 131 46 46 46 92 128 109 0 0 0 98 194 286 384 384 384 216 209 256 299 338 338 338 294 256 448 456 576 576 564 448 +glyph 170 è +contours 18,25,31 +x 95 95 148 199 239 287 296 350 317 199 116 30 30 30 116 196 360 360 360 296 291 242 195 150 98 96 226 112 112 184 258 258 +y 209 131 46 46 46 92 128 109 0 0 0 98 194 286 384 384 384 216 209 256 299 338 338 338 294 256 448 564 576 576 456 448 +glyph 169 ç +contours 25,44 +x 95 95 142 189 222 266 271 333 326 249 190 112 30 30 30 112 189 246 321 331 268 263 224 188 139 95 258 258 164 144 133 133 150 163 213 213 213 167 153 148 170 207 193 226 258 +y 194 120 49 49 49 88 128 124 67 0 0 0 98 192 286 384 384 384 319 261 256 293 336 336 336 271 -88 -192 -192 -192 -191 -157 -159 -159 -159 -110 -64 -64 -64 -65 0 0 0 -2 -48 +glyph 179 ñ +contours 26,50 +x 285 285 285 271 239 208 164 112 112 112 50 50 50 48 106 107 107 108 109 110 131 187 229 290 347 347 347 253 239 210 183 159 148 129 110 107 76 80 96 124 146 161 190 217 241 251 285 291 323 317 283 +y 0 239 277 318 336 336 336 274 219 0 0 302 369 384 384 382 365 343 313 313 352 384 384 384 322 251 0 448 448 472 501 525 525 525 485 448 448 497 547 576 576 576 552 523 499 499 499 576 576 505 448 +glyph 190 ü +contours 26,30,34 +x 110 110 110 124 156 187 231 283 283 283 345 345 345 347 289 288 288 286 286 285 264 208 166 105 48 48 48 232 232 288 288 96 96 153 153 +y 384 145 107 66 48 48 48 110 165 384 384 82 15 0 0 2 19 41 71 71 32 0 0 0 62 133 384 448 511 511 448 448 511 511 448 +glyph 131 Á +contours 7,16,22 +x 401 346 125 69 1 199 274 468 235 232 223 207 145 326 264 254 244 188 188 263 334 334 227 +y 0 141 141 0 0 512 512 0 463 452 420 370 192 192 371 398 431 576 586 704 704 689 576 +glyph 139 É +contours 11,17 +x 58 58 425 425 123 123 405 405 123 123 439 439 190 190 265 336 336 229 +y 0 512 512 458 458 320 320 267 267 54 54 0 576 586 704 704 689 576 +glyph 147 Ñ +contours 13,37 +x 372 113 114 116 116 58 58 134 396 392 392 392 451 451 312 298 269 242 217 207 188 169 166 135 139 155 183 205 220 249 276 299 310 344 350 382 376 342 +y 0 441 405 344 0 0 512 512 69 141 173 512 512 0 576 576 600 629 653 653 653 613 576 576 625 675 704 704 704 680 651 627 627 627 704 704 633 576 +glyph 158 Ü +contours 19,23,27 +x 251 192 103 54 54 54 120 120 120 187 251 316 389 389 389 454 454 454 404 313 293 293 349 349 157 157 214 214 +y 0 0 45 131 191 512 512 195 125 53 53 53 128 199 512 512 197 136 47 0 576 639 639 576 576 639 639 576 diff --git a/fontbox/src/test/resources/ttf/hinting/LiberationSans-Regular-13.txt b/fontbox/src/test/resources/ttf/hinting/LiberationSans-Regular-13.txt new file mode 100644 index 00000000000..762cae84615 --- /dev/null +++ b/fontbox/src/test/resources/ttf/hinting/LiberationSans-Regular-13.txt @@ -0,0 +1,149 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +font LiberationSans-Regular.ttf +ppem 13 +freetype 2.13.2 +glyph 43 H +contours 11 +x 455 455 146 146 68 68 146 146 455 455 533 533 +y 0 319 319 0 0 640 640 384 384 640 640 0 +glyph 44 I +contours 3 +x 77 77 154 154 +y 0 640 640 0 +glyph 47 L +contours 5 +x 68 68 146 146 435 435 +y 0 640 640 63 63 0 +glyph 40 E +contours 11 +x 68 68 503 503 146 146 478 478 146 146 519 519 +y 0 640 640 577 577 384 384 321 321 63 63 0 +glyph 55 T +contours 7 +x 293 293 215 215 19 19 489 489 +y 577 0 0 577 577 640 640 577 +glyph 75 h +contours 24 +x 129 153 219 270 341 409 409 409 335 335 335 318 279 245 193 131 131 131 58 58 131 131 131 128 128 +y 365 408 448 448 448 377 293 0 0 279 326 371 392 392 392 320 260 0 0 640 640 459 431 370 365 +glyph 82 o +contours 10,22 +x 428 428 326 230 133 35 35 35 232 333 428 351 351 297 233 169 112 112 112 168 229 295 351 +y 224 111 0 0 0 115 224 448 448 448 339 224 313 394 394 394 312 224 139 54 54 54 137 +glyph 91 x +contours 11 +x 325 207 88 9 166 17 98 207 316 398 249 407 +y 0 184 0 0 230 448 448 274 448 448 231 0 +glyph 81 n +contours 26 +x 335 335 335 318 281 245 192 131 131 131 58 58 58 55 125 125 126 127 128 129 154 221 270 342 409 409 409 +y 0 279 323 371 392 392 392 320 255 0 0 352 431 448 448 446 426 400 365 365 410 448 448 448 376 293 0 +glyph 19 0 +contours 11,23 +x 430 430 329 230 132 33 33 33 129 233 334 430 356 356 299 233 165 106 106 106 166 231 296 356 +y 320 164 0 0 0 163 320 480 640 640 640 478 320 458 581 581 581 459 320 185 60 60 60 188 +glyph 20 1 +contours 10 +x 63 63 209 209 80 80 215 283 283 422 422 +y 0 62 62 571 448 517 640 640 62 62 0 +glyph 21 2 +contours 30 +x 42 42 63 122 188 253 305 337 337 337 282 233 186 125 120 45 53 154 232 319 412 412 412 381 321 236 190 134 122 421 421 +y 0 51 104 185 250 307 363 424 463 519 580 580 580 529 483 490 559 640 640 640 549 465 428 355 282 205 162 94 62 62 0 +glyph 22 3 +contours 40 +x 426 426 325 232 145 42 32 107 122 232 287 350 350 350 278 210 169 169 209 269 335 335 335 281 228 180 120 115 41 49 150 228 314 410 410 410 349 290 290 354 426 +y 195 102 0 0 0 78 155 162 61 61 61 129 197 255 321 321 321 384 384 384 436 482 527 580 580 580 530 485 491 561 640 640 640 561 490 435 367 355 353 345 260 +glyph 23 4 +contours 10,18 +x 358 358 289 289 19 19 281 358 358 438 438 289 288 267 262 115 93 87 289 +y 134 0 0 134 134 199 640 640 192 192 134 558 555 509 500 238 202 192 192 +glyph 24 5 +contours 28 +x 428 428 320 225 144 46 33 107 130 226 285 352 352 352 285 228 198 147 121 50 69 394 394 136 125 173 243 328 428 +y 200 107 0 0 0 70 136 145 60 60 60 134 199 255 324 324 324 305 282 282 640 640 578 578 348 384 384 384 282 +glyph 25 6 +contours 22,34 +x 426 426 328 241 144 42 42 42 148 247 376 410 340 319 246 184 115 115 135 207 254 333 426 352 352 291 236 185 122 122 122 188 239 292 352 +y 198 106 0 0 0 156 305 467 640 640 640 524 511 581 581 581 441 309 346 384 384 384 283 195 259 328 328 328 267 213 145 59 59 59 132 +glyph 26 7 +contours 11 +x 421 333 261 225 225 148 148 241 350 43 43 421 +y 581 429 257 90 0 0 124 399 578 578 640 640 +glyph 27 8 +contours 25,36,47 +x 427 427 326 232 140 36 36 36 100 150 150 104 50 50 50 148 230 314 412 412 412 358 311 311 366 427 336 336 230 178 124 124 124 180 230 282 336 351 351 288 230 175 112 112 112 233 292 351 +y 193 102 0 0 0 100 193 257 346 355 357 368 442 492 558 640 640 640 559 490 441 366 357 355 345 258 487 586 586 586 536 487 437 384 384 384 433 200 264 329 329 329 259 198 55 55 55 124 +glyph 28 9 +contours 23,36 +x 423 423 315 216 149 68 51 121 143 218 280 349 351 335 256 209 132 39 39 39 140 229 325 423 344 344 280 227 174 113 113 113 174 226 258 313 344 +y 332 172 0 0 0 56 119 130 59 59 59 170 273 236 192 192 192 315 416 521 640 640 640 486 392 477 581 581 581 492 417 340 250 250 250 286 351 +glyph 163 á +contours 35,48,54 +x 168 102 35 35 35 125 225 324 324 324 278 230 180 135 131 55 74 231 314 398 398 398 398 415 439 450 463 463 435 406 366 328 326 324 296 221 185 225 288 324 324 324 244 193 139 111 111 111 150 167 167 256 340 340 205 +y 0 0 65 121 184 252 254 256 282 341 392 392 392 358 320 327 448 448 448 373 301 114 82 49 49 49 52 6 0 0 0 44 91 91 41 0 55 55 87 143 173 205 204 203 186 150 121 89 55 512 520 640 640 628 512 +glyph 162 à +contours 35,48,54 +x 168 102 35 35 35 125 225 324 324 324 278 230 180 135 131 55 74 231 314 398 398 398 398 415 439 450 463 463 435 406 366 328 326 324 296 221 185 225 288 324 324 324 244 193 139 111 111 111 150 253 119 119 203 291 291 +y 0 0 65 121 184 252 254 256 282 341 392 392 392 358 320 327 448 448 448 373 301 114 82 49 49 49 52 6 0 0 0 44 91 91 41 0 55 55 87 143 173 205 204 203 186 150 121 89 55 512 628 640 640 520 512 +glyph 164 â +contours 35,48,58 +x 168 102 35 35 35 125 225 324 324 324 278 230 180 135 131 55 74 231 314 398 398 398 398 415 439 450 463 463 435 406 366 328 326 324 296 221 185 225 288 324 324 324 244 193 139 111 111 111 150 358 358 315 226 226 131 89 89 184 267 +y 0 0 65 121 184 252 254 256 282 341 392 392 392 358 320 327 448 448 448 373 301 114 82 49 49 49 52 6 0 0 0 44 91 91 41 0 55 55 87 143 173 205 204 203 186 150 121 89 55 521 512 512 587 587 512 512 521 640 640 +glyph 166 ä +contours 35,48,52,56 +x 168 102 35 35 35 125 225 324 324 324 278 230 180 135 131 55 74 231 314 398 398 398 398 415 439 450 463 463 435 406 366 328 326 324 296 221 185 225 288 324 324 324 244 193 139 111 111 111 150 273 273 340 340 113 113 180 180 +y 0 0 65 121 184 252 254 256 282 341 392 392 392 358 320 327 448 448 448 373 301 114 82 49 49 49 52 6 0 0 0 44 91 91 41 0 55 55 87 143 173 205 204 203 186 150 121 89 55 512 587 587 512 512 587 587 512 +glyph 165 ã +contours 35,48,72 +x 168 102 35 35 35 125 225 324 324 324 278 230 180 135 131 55 74 231 314 398 398 398 398 415 439 450 463 463 435 406 366 328 326 324 296 221 185 225 288 324 324 324 244 193 139 111 111 111 150 300 283 249 217 188 176 154 132 128 91 95 115 148 173 191 225 257 285 297 338 345 382 375 335 +y 0 0 65 121 184 252 254 256 282 341 392 392 392 358 320 327 448 448 448 373 301 114 82 49 49 49 52 6 0 0 0 44 91 91 41 0 55 55 87 143 173 205 204 203 186 150 121 89 55 512 512 533 558 579 579 579 544 512 512 561 611 640 640 640 619 594 573 573 573 640 640 569 512 +glyph 171 é +contours 18,25,31 +x 112 112 175 235 282 340 350 414 375 235 137 35 35 35 137 232 426 426 426 350 344 285 231 178 115 113 179 179 268 352 352 217 +y 200 130 55 55 55 96 128 109 0 0 0 114 227 334 448 448 448 210 200 256 328 394 394 394 320 256 512 520 640 640 628 512 +glyph 170 è +contours 18,25,31 +x 112 112 175 235 282 340 350 414 375 235 137 35 35 35 137 232 426 426 426 350 344 285 231 178 115 113 267 133 133 217 305 305 +y 200 130 55 55 55 96 128 109 0 0 0 114 227 334 448 448 448 210 200 256 328 394 394 394 320 256 512 628 640 640 520 512 +glyph 169 ç +contours 25,44 +x 112 112 167 223 262 314 320 394 385 294 224 132 35 35 35 133 224 291 381 392 316 310 264 222 164 112 305 305 194 170 157 157 177 192 252 252 252 198 181 175 201 245 229 267 305 +y 226 141 58 58 58 92 128 124 67 0 0 0 115 224 333 448 448 448 383 325 320 353 391 391 391 316 -88 -192 -192 -192 -190 -151 -153 -153 -153 -107 -64 -64 -64 -65 0 0 0 -2 -48 +glyph 179 ñ +contours 26,50 +x 337 337 337 320 283 247 194 133 133 133 60 60 60 57 127 127 128 129 130 131 156 223 272 344 411 411 411 300 283 249 217 188 176 154 132 128 91 95 115 148 173 191 225 257 285 297 338 345 382 375 335 +y 0 279 323 371 392 392 392 320 255 0 0 352 431 448 448 446 426 400 365 365 410 448 448 448 376 293 0 512 512 533 558 579 579 579 544 512 512 561 611 640 640 640 619 594 573 573 573 640 640 569 512 +glyph 190 ü +contours 26,30,34 +x 130 130 130 147 184 220 273 334 334 334 407 407 407 409 340 340 339 338 337 336 311 244 195 123 56 56 56 274 274 341 341 114 114 181 181 +y 448 169 125 77 56 56 56 128 193 448 448 96 17 0 0 2 22 48 83 83 38 0 0 0 72 155 448 512 587 587 512 512 587 587 512 +glyph 131 Á +contours 7,16,22 +x 474 409 148 82 2 235 323 553 278 274 264 244 171 386 312 300 289 222 222 311 395 395 269 +y 0 195 195 0 0 640 640 0 582 569 531 470 256 256 471 503 544 704 714 832 832 817 704 +glyph 139 É +contours 11,17 +x 68 68 503 503 146 146 478 478 146 146 519 519 224 224 313 397 397 271 +y 0 640 640 577 577 384 384 321 321 63 63 0 704 714 832 832 817 704 +glyph 147 Ñ +contours 13,37 +x 440 133 135 137 137 68 68 158 468 463 463 463 533 533 369 352 318 286 257 245 223 201 197 160 164 184 217 242 260 295 326 354 366 407 414 451 444 404 +y 0 556 511 434 0 0 640 640 82 173 213 640 640 0 704 704 725 750 771 771 771 736 704 704 753 803 832 832 832 811 786 765 765 765 832 832 761 704 +glyph 158 Ü +contours 19,23,27 +x 297 227 122 64 64 64 142 142 142 221 296 373 459 459 459 537 537 537 478 370 346 346 413 413 186 186 253 253 +y 0 0 56 164 238 640 640 241 154 63 63 63 157 247 640 640 246 170 59 0 704 779 779 704 704 779 779 704 diff --git a/fontbox/src/test/resources/ttf/hinting/LiberationSans-Regular-16.txt b/fontbox/src/test/resources/ttf/hinting/LiberationSans-Regular-16.txt new file mode 100644 index 00000000000..eb1d3879721 --- /dev/null +++ b/fontbox/src/test/resources/ttf/hinting/LiberationSans-Regular-16.txt @@ -0,0 +1,149 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +font LiberationSans-Regular.ttf +ppem 16 +freetype 2.13.2 +glyph 43 H +contours 11 +x 561 561 180 180 84 84 180 180 561 561 656 656 +y 0 384 384 0 0 768 768 464 464 768 768 0 +glyph 44 I +contours 3 +x 95 95 190 190 +y 0 768 768 0 +glyph 47 L +contours 5 +x 84 84 180 180 536 536 +y 0 768 768 78 78 0 +glyph 40 E +contours 11 +x 84 84 619 619 180 180 589 589 180 180 639 639 +y 0 768 768 690 690 464 464 387 387 78 78 0 +glyph 55 T +contours 7 +x 360 360 265 265 23 23 602 602 +y 690 0 0 690 690 768 768 690 +glyph 75 h +contours 24 +x 159 188 269 332 420 503 503 503 413 413 413 392 344 301 238 161 161 161 71 71 161 161 161 158 157 +y 469 524 576 576 576 485 377 0 0 360 420 479 506 506 506 414 335 0 0 768 768 571 540 474 469 +glyph 82 o +contours 10,22 +x 527 527 402 283 164 43 43 43 286 410 527 432 432 366 287 208 138 138 138 207 282 363 432 +y 289 143 0 0 0 148 289 576 576 576 436 289 404 509 509 509 402 289 178 67 67 67 174 +glyph 91 x +contours 11 +x 401 255 109 12 204 21 120 255 389 490 306 501 +y 0 236 0 0 296 576 576 352 576 576 297 0 +glyph 81 n +contours 26 +x 413 413 413 392 346 301 236 161 161 161 71 71 71 68 153 154 155 156 157 159 190 271 332 421 503 503 503 +y 0 360 417 479 506 506 506 412 329 0 0 453 554 576 576 573 548 515 469 469 527 576 576 576 483 377 0 +glyph 19 0 +contours 11,23 +x 530 530 405 284 162 40 40 40 159 287 411 530 438 438 368 287 204 131 131 131 205 285 364 438 +y 384 197 0 0 0 196 384 576 768 768 768 574 384 548 695 695 695 550 384 223 74 74 74 226 +glyph 20 1 +contours 10 +x 78 78 258 258 99 99 265 348 348 520 520 +y 0 77 77 682 576 661 768 768 77 77 0 +glyph 21 2 +contours 30 +x 52 52 78 151 232 312 376 415 415 415 347 287 229 155 148 56 66 189 286 393 507 507 507 470 396 291 234 166 151 518 518 +y 0 64 127 224 302 369 436 510 556 622 694 694 694 632 575 584 668 768 768 768 659 559 515 427 339 247 197 115 77 77 0 +glyph 22 3 +contours 40 +x 525 525 401 286 179 51 39 132 150 286 354 431 431 431 343 259 208 208 257 331 413 413 413 347 281 222 148 142 51 61 185 282 388 505 505 505 430 358 358 437 525 +y 236 123 0 0 0 97 191 200 75 75 75 157 237 307 386 386 386 464 464 464 525 579 632 694 694 694 633 578 585 671 768 768 768 673 589 524 442 428 426 416 313 +glyph 23 4 +contours 10,18 +x 441 441 356 356 24 24 347 441 441 540 540 356 355 329 323 142 115 107 356 +y 185 0 0 185 185 260 768 768 256 256 185 666 663 611 601 308 267 256 256 +glyph 24 5 +contours 28 +x 527 527 394 277 178 57 41 132 161 279 351 433 433 433 351 281 244 181 150 62 85 486 486 167 154 213 300 404 527 +y 267 143 0 0 0 67 130 138 74 74 74 176 265 342 438 438 438 415 388 388 768 768 691 691 467 512 512 512 376 +glyph 25 6 +contours 22,34 +x 525 525 404 297 178 52 52 52 183 304 464 505 419 393 303 226 142 142 167 256 313 411 525 433 433 358 291 228 151 151 151 231 294 359 433 +y 265 142 0 0 0 187 367 560 768 768 768 625 609 695 695 695 553 419 465 512 512 512 378 260 347 442 442 442 358 285 192 73 73 73 173 +glyph 26 7 +contours 11 +x 518 410 321 277 277 183 183 297 431 53 53 518 +y 695 513 308 107 0 0 149 477 691 691 768 768 +glyph 27 8 +contours 25,36,47 +x 525 525 401 285 172 45 45 45 124 185 185 128 61 61 61 182 283 387 508 508 508 441 383 383 450 525 414 414 283 220 153 153 153 222 284 348 414 432 432 354 284 215 138 138 138 287 360 432 +y 225 119 0 0 0 116 224 299 401 412 414 428 520 583 665 768 768 768 667 581 519 427 415 413 401 300 577 701 701 701 639 577 514 448 448 448 509 233 306 380 380 380 300 230 68 68 68 147 +glyph 28 9 +contours 23,36 +x 521 521 389 266 184 84 63 149 176 268 345 430 432 412 315 257 162 48 48 48 172 283 400 521 423 423 345 280 215 140 140 140 215 279 318 385 423 +y 399 207 0 0 0 70 147 161 73 73 73 219 355 310 256 256 256 396 512 632 768 768 768 584 485 580 695 695 695 596 512 427 327 327 327 367 439 +glyph 163 á +contours 35,48,54 +x 208 126 44 44 44 155 278 399 399 399 343 283 223 168 162 68 91 285 387 490 490 490 490 511 541 554 570 570 536 501 451 405 402 399 365 273 228 277 354 399 399 399 300 237 171 136 136 136 184 206 206 315 418 418 253 +y 0 0 81 152 231 315 318 320 356 437 507 507 507 479 448 455 576 576 576 479 387 145 103 61 61 61 64 8 0 0 0 57 117 117 53 0 68 68 109 179 217 257 255 254 233 188 151 111 68 640 653 832 832 814 640 +glyph 162 à +contours 35,48,54 +x 208 126 44 44 44 155 278 399 399 399 343 283 223 168 162 68 91 285 387 490 490 490 490 511 541 554 570 570 536 501 451 405 402 399 365 273 228 277 354 399 399 399 300 237 171 136 136 136 184 312 147 147 251 359 359 +y 0 0 81 152 231 315 318 320 356 437 507 507 507 479 448 455 576 576 576 479 387 145 103 61 61 61 64 8 0 0 0 57 117 117 53 0 68 68 109 179 217 257 255 254 233 188 151 111 68 640 814 832 832 653 640 +glyph 164 â +contours 35,48,58 +x 208 126 44 44 44 155 278 399 399 399 343 283 223 168 162 68 91 285 387 490 490 490 490 511 541 554 570 570 536 501 451 405 402 399 365 273 228 277 354 399 399 399 300 237 171 136 136 136 184 440 440 388 278 277 161 109 109 226 328 +y 0 0 81 152 231 315 318 320 356 437 507 507 507 479 448 455 576 576 576 479 387 145 103 61 61 61 64 8 0 0 0 57 117 117 53 0 68 68 109 179 217 257 255 254 233 188 151 111 68 653 640 640 752 752 640 640 653 832 832 +glyph 166 ä +contours 35,48,52,56 +x 208 126 44 44 44 155 278 399 399 399 343 283 223 168 162 68 91 285 387 490 490 490 490 511 541 554 570 570 536 501 451 405 402 399 365 273 228 277 354 399 399 399 300 237 171 136 136 136 184 338 338 419 419 141 141 223 223 +y 0 0 81 152 231 315 318 320 356 437 507 507 507 479 448 455 576 576 576 479 387 145 103 61 61 61 64 8 0 0 0 57 117 117 53 0 68 68 109 179 217 257 255 254 233 188 151 111 68 640 732 732 640 640 732 732 640 +glyph 165 ã +contours 35,48,72 +x 208 126 44 44 44 155 278 399 399 399 343 283 223 168 162 68 91 285 387 490 490 490 490 511 541 554 570 570 536 501 451 405 402 399 365 273 228 277 354 399 399 399 300 237 171 136 136 136 184 369 348 306 267 231 216 188 161 157 111 117 141 181 213 235 277 316 350 365 415 424 470 461 411 +y 0 0 81 152 231 315 318 320 356 437 507 507 507 479 448 455 576 576 576 479 387 145 103 61 61 61 64 8 0 0 0 57 117 117 53 0 68 68 109 179 217 257 255 254 233 188 151 111 68 640 640 656 677 693 693 693 665 640 640 689 739 768 768 768 752 731 715 715 715 768 768 697 640 +glyph 171 é +contours 18,25,31 +x 138 138 215 289 348 418 431 510 462 290 170 44 44 44 169 286 524 524 524 431 424 352 284 219 142 139 220 220 329 432 432 267 +y 251 163 68 68 68 102 128 109 0 0 0 147 292 429 576 576 576 264 251 320 419 509 509 509 408 320 640 653 832 832 814 640 +glyph 170 è +contours 18,25,31 +x 138 138 215 289 348 418 431 510 462 290 170 44 44 44 169 286 524 524 524 431 424 352 284 219 142 139 329 164 164 268 376 376 +y 251 163 68 68 68 102 128 109 0 0 0 147 292 429 576 576 576 264 251 320 419 509 509 509 408 320 640 814 832 832 653 640 +glyph 169 ç +contours 25,44 +x 138 138 206 274 322 387 394 485 475 363 277 163 44 44 44 164 276 359 468 482 390 383 326 274 202 138 376 376 239 211 194 194 218 237 310 310 310 244 222 215 248 301 282 329 376 +y 290 179 71 71 71 99 128 124 67 0 0 0 147 289 429 576 576 576 511 453 448 474 505 505 505 407 -152 -256 -256 -256 -254 -205 -208 -208 -208 -167 -128 -128 -128 -129 0 0 -64 -66 -112 +glyph 179 ñ +contours 26,50 +x 415 415 415 394 348 303 238 163 163 163 73 73 73 70 155 156 157 158 159 161 192 273 334 423 505 505 505 369 348 306 267 231 216 188 161 157 111 117 141 181 213 235 277 316 350 365 415 424 470 461 411 +y 0 360 417 479 506 506 506 412 329 0 0 453 554 576 576 573 548 515 469 469 527 576 576 576 483 377 0 640 640 656 677 693 693 693 665 640 640 689 739 768 768 768 752 731 715 715 715 768 768 697 640 +glyph 190 ü +contours 26,30,34 +x 160 160 160 181 227 272 337 412 412 412 502 502 502 505 420 420 419 417 416 415 384 302 242 153 70 70 70 339 339 420 420 142 142 224 224 +y 576 216 159 97 70 70 70 164 247 576 576 123 22 0 0 3 27 59 103 103 47 0 0 0 93 199 576 640 732 732 640 640 732 732 640 +glyph 131 Á +contours 7,16,22 +x 584 503 182 101 2 290 398 681 343 338 326 301 211 475 385 371 357 274 274 383 486 486 331 +y 0 245 245 0 0 768 768 0 696 681 637 567 320 320 568 605 652 832 842 960 960 945 832 +glyph 139 É +contours 11,17 +x 84 84 619 619 180 180 589 589 180 180 639 639 276 276 385 488 488 333 +y 0 768 768 690 690 464 464 387 387 78 78 0 832 842 960 960 945 832 +glyph 147 Ñ +contours 13,37 +x 541 164 167 169 169 84 84 195 576 570 570 570 656 656 455 434 392 353 318 302 275 248 243 197 203 227 267 299 321 363 402 436 451 501 510 556 548 498 +y 0 663 609 517 0 0 768 768 101 209 258 768 768 0 832 832 848 869 885 885 885 857 832 832 881 931 960 960 960 944 923 907 907 907 960 960 889 832 +glyph 158 Ü +contours 19,23,27 +x 366 279 150 79 79 79 175 175 175 273 366 461 566 566 566 661 661 661 588 456 427 427 508 508 230 230 312 312 +y 0 0 68 197 286 768 768 291 186 78 78 78 190 298 768 768 296 204 70 0 832 924 924 832 832 924 924 832 diff --git a/fontbox/src/test/resources/ttf/hinting/LiberationSans-Regular-24.txt b/fontbox/src/test/resources/ttf/hinting/LiberationSans-Regular-24.txt new file mode 100644 index 00000000000..1dd286dd111 --- /dev/null +++ b/fontbox/src/test/resources/ttf/hinting/LiberationSans-Regular-24.txt @@ -0,0 +1,149 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +font LiberationSans-Regular.ttf +ppem 24 +freetype 2.13.2 +glyph 43 H +contours 11 +x 841 841 269 269 126 126 269 269 841 841 984 984 +y 0 520 520 0 0 1088 1088 640 640 1088 1088 0 +glyph 44 I +contours 3 +x 142 142 285 285 +y 0 1088 1088 0 +glyph 47 L +contours 5 +x 126 126 269 269 803 803 +y 0 1088 1088 117 117 0 +glyph 40 E +contours 11 +x 126 126 928 928 269 269 883 883 269 269 959 959 +y 0 1088 1088 971 971 640 640 524 524 117 117 0 +glyph 55 T +contours 7 +x 540 540 398 398 35 35 903 903 +y 971 0 0 971 971 1088 1088 971 +glyph 75 h +contours 24 +x 238 282 404 498 630 755 755 755 619 619 619 588 516 452 357 242 242 242 107 107 242 242 242 236 236 +y 677 757 832 832 832 700 544 0 0 519 605 689 728 728 728 595 482 0 0 1088 1088 818 775 684 677 +glyph 82 o +contours 10,22 +x 790 790 603 424 246 65 65 65 429 615 790 648 648 548 430 312 206 206 206 310 422 544 648 +y 417 206 0 0 0 214 417 832 832 832 630 417 582 732 732 732 579 417 259 100 100 100 254 +glyph 91 x +contours 11 +x 601 383 163 17 306 31 180 383 584 734 459 752 +y 0 341 0 0 428 832 832 508 832 832 430 0 +glyph 81 n +contours 26 +x 619 619 619 588 519 452 354 242 242 242 107 107 107 102 230 231 232 235 236 238 285 407 498 631 755 755 755 +y 0 519 599 689 728 728 728 593 474 0 0 654 800 832 832 828 791 743 677 677 762 832 832 832 698 544 0 +glyph 19 0 +contours 11,23 +x 794 794 607 425 243 60 60 60 238 430 616 794 657 657 551 430 306 197 197 197 307 427 546 657 +y 544 279 0 0 0 278 544 816 1088 1088 1088 813 544 772 978 978 978 775 544 319 110 110 110 323 +glyph 20 1 +contours 10 +x 117 117 386 386 148 148 398 522 522 779 779 +y 0 115 115 959 768 895 1088 1088 115 115 0 +glyph 21 2 +contours 30 +x 77 77 115 226 347 467 563 622 622 622 520 429 343 231 221 83 98 283 429 589 761 761 761 705 594 437 351 249 226 777 777 +y 0 95 184 320 431 525 620 723 789 878 977 977 977 883 798 811 938 1088 1088 1088 935 794 731 608 485 355 283 168 115 115 0 +glyph 22 3 +contours 40 +x 787 787 601 429 268 77 59 198 225 429 531 647 647 647 514 389 312 312 386 497 619 619 619 519 421 331 221 212 77 92 277 423 582 758 758 758 644 536 536 655 787 +y 323 169 0 0 0 145 286 299 112 112 112 220 326 419 523 523 523 640 640 640 729 808 886 977 977 977 885 802 813 942 1088 1088 1088 948 823 727 607 586 583 569 429 +glyph 23 4 +contours 10,18 +x 661 661 533 533 35 35 519 661 661 809 809 533 532 493 483 212 172 160 533 +y 277 0 0 277 277 381 1088 1088 384 384 277 936 932 862 848 454 399 384 384 +glyph 24 5 +contours 28 +x 790 790 591 415 267 86 62 198 241 418 527 650 650 650 526 421 366 271 224 92 128 728 728 251 230 318 448 605 790 +y 367 196 0 0 0 129 251 267 110 110 110 246 364 467 594 594 594 560 518 518 1088 1088 973 973 637 704 704 704 517 +glyph 25 6 +contours 22,34 +x 787 787 605 446 267 78 78 78 275 456 696 758 629 589 455 339 212 212 249 382 469 615 787 650 650 537 437 342 226 226 226 347 441 539 650 +y 364 195 0 0 0 266 519 794 1088 1088 1088 872 849 978 978 978 766 565 633 704 704 704 519 358 474 600 600 600 488 391 267 109 109 109 242 +glyph 26 7 +contours 11 +x 777 615 482 415 415 274 274 446 647 79 79 777 +y 978 722 433 151 0 0 209 672 973 973 1088 1088 +glyph 27 8 +contours 25,36,47 +x 788 788 602 428 258 67 67 67 186 278 278 192 92 92 92 273 425 580 761 761 761 661 574 574 675 788 621 621 425 330 230 230 230 333 426 521 621 647 647 530 424 322 206 206 206 429 539 647 +y 320 169 0 0 0 166 318 425 571 586 589 608 739 826 943 1088 1088 1088 946 825 738 608 591 588 571 428 817 987 987 987 901 817 730 640 640 640 723 332 435 539 539 539 427 329 101 101 101 211 +glyph 28 9 +contours 23,36 +x 782 782 583 399 276 126 94 223 263 401 518 645 648 618 473 386 243 72 72 72 258 424 600 782 635 635 518 419 322 209 209 209 322 418 476 577 635 +y 565 293 0 0 0 104 221 241 109 109 109 330 534 466 384 384 384 577 736 900 1088 1088 1088 827 700 825 978 978 978 848 736 623 491 491 491 543 639 +glyph 163 á +contours 35,48,54 +x 310 188 65 65 65 231 416 598 598 598 514 424 333 250 242 101 136 427 580 734 734 734 734 766 810 829 854 854 803 750 675 607 602 598 546 409 341 416 531 598 598 598 450 355 257 204 204 204 275 309 309 472 627 627 380 +y 0 0 130 242 369 504 509 512 554 648 729 729 729 656 576 590 832 832 832 692 560 211 152 91 91 91 97 12 0 0 0 81 168 168 76 0 101 101 169 287 350 417 414 412 376 301 240 173 101 896 913 1152 1152 1128 896 +glyph 162 à +contours 35,48,54 +x 310 188 65 65 65 231 416 598 598 598 514 424 333 250 242 101 136 427 580 734 734 734 734 766 810 829 854 854 803 750 675 607 602 598 546 409 341 416 531 598 598 598 450 355 257 204 204 204 275 468 221 221 376 539 539 +y 0 0 130 242 369 504 509 512 554 648 729 729 729 656 576 590 832 832 832 692 560 211 152 91 91 91 97 12 0 0 0 81 168 168 76 0 101 101 169 287 350 417 414 412 376 301 240 173 101 896 1128 1152 1152 913 896 +glyph 164 â +contours 35,48,58 +x 310 188 65 65 65 231 416 598 598 598 514 424 333 250 242 101 136 427 580 734 734 734 734 766 810 829 854 854 803 750 675 607 602 598 546 409 341 416 531 598 598 598 450 355 257 204 204 204 275 661 661 582 418 416 242 164 164 340 493 +y 0 0 130 242 369 504 509 512 554 648 729 729 729 656 576 590 832 832 832 692 560 211 152 91 91 91 97 12 0 0 0 81 168 168 76 0 101 101 169 287 350 417 414 412 376 301 240 173 101 914 896 896 1045 1045 896 896 914 1152 1152 +glyph 166 ä +contours 35,48,52,56 +x 310 188 65 65 65 231 416 598 598 598 514 424 333 250 242 101 136 427 580 734 734 734 734 766 810 829 854 854 803 750 675 607 602 598 546 409 341 416 531 598 598 598 450 355 257 204 204 204 275 505 505 628 628 210 210 334 334 +y 0 0 130 242 369 504 509 512 554 648 729 729 729 656 576 590 832 832 832 692 560 211 152 91 91 91 97 12 0 0 0 81 168 168 76 0 101 101 169 287 350 417 414 412 376 301 240 173 101 896 1034 1034 896 896 1034 1034 896 +glyph 165 ã +contours 35,48,72 +x 310 188 65 65 65 231 416 598 598 598 514 424 333 250 242 101 136 427 580 734 734 734 734 766 810 829 854 854 803 750 675 607 602 598 546 409 341 416 531 598 598 598 450 355 257 204 204 204 275 553 522 459 400 347 324 283 242 235 167 175 211 272 319 352 415 474 526 548 623 636 705 692 617 +y 0 0 130 242 369 504 509 512 554 648 729 729 729 656 576 590 832 832 832 692 560 211 152 91 91 91 97 12 0 0 0 81 168 168 76 0 101 101 169 287 350 417 414 412 376 301 240 173 101 896 896 921 951 976 976 976 934 896 896 969 1044 1088 1088 1088 1063 1033 1008 1008 1008 1088 1088 982 896 +glyph 171 é +contours 18,25,31 +x 207 207 323 434 521 627 646 764 691 433 253 65 65 65 253 428 786 786 786 647 636 528 427 328 214 209 330 330 493 648 648 401 +y 408 261 101 101 101 189 256 218 0 0 0 212 421 620 832 832 832 425 408 512 627 732 732 732 615 512 896 913 1152 1152 1128 896 +glyph 170 è +contours 18,25,31 +x 207 207 323 434 521 627 646 764 691 433 253 65 65 65 253 428 786 786 786 647 636 528 427 328 214 209 493 246 246 401 564 564 +y 408 261 101 101 101 189 256 218 0 0 0 212 421 620 832 832 832 425 408 512 627 732 732 732 615 512 896 1128 1152 1152 913 896 +glyph 169 ç +contours 25,44 +x 206 206 308 411 483 580 591 728 712 544 415 244 65 65 65 245 413 538 702 723 584 574 488 409 302 206 563 563 358 315 290 290 327 355 465 465 465 366 333 323 371 452 422 493 563 +y 420 260 107 107 107 180 256 247 135 0 0 0 213 417 619 832 832 832 701 587 576 645 726 726 726 586 -181 -320 -320 -320 -317 -243 -248 -248 -248 -186 -128 -128 -128 -130 0 0 -64 -66 -128 +glyph 179 ñ +contours 26,50 +x 622 622 622 591 522 455 357 245 245 245 110 110 110 105 233 234 235 238 239 241 288 410 501 634 758 758 758 553 522 459 400 347 324 283 242 235 167 175 211 272 319 352 415 474 526 548 623 636 705 692 617 +y 0 519 599 689 728 728 728 593 474 0 0 654 800 832 832 828 791 743 677 677 762 832 832 832 698 544 0 896 896 921 951 976 976 976 934 896 896 969 1044 1088 1088 1088 1063 1033 1008 1008 1008 1088 1088 982 896 +glyph 190 ü +contours 26,30,34 +x 241 241 241 272 341 408 506 618 618 618 753 753 753 757 630 629 628 626 624 622 575 453 362 229 105 105 105 507 507 630 630 212 212 336 336 +y 832 313 233 143 104 104 104 239 358 832 832 178 32 0 0 4 41 88 154 154 70 0 0 0 134 288 832 896 1034 1034 896 896 1034 1034 896 +glyph 131 Á +contours 7,16,22 +x 875 755 273 152 3 434 597 1022 514 507 489 452 317 712 576 555 534 410 410 573 728 728 496 +y 0 336 336 0 0 1088 1088 0 980 959 896 798 448 448 799 852 917 1152 1168 1344 1344 1321 1152 +glyph 139 É +contours 11,17 +x 126 126 928 928 269 269 883 883 269 269 959 959 413 413 576 731 731 499 +y 0 1088 1088 971 971 640 640 524 524 117 117 0 1152 1168 1344 1344 1321 1152 +glyph 147 Ñ +contours 13,37 +x 812 246 250 254 254 126 126 293 864 855 855 855 984 984 682 651 588 529 476 453 411 371 364 296 304 340 401 448 481 544 603 655 677 752 765 834 821 746 +y 0 931 856 726 0 0 1088 1088 151 303 371 1088 1088 0 1152 1152 1177 1207 1232 1232 1232 1190 1152 1152 1225 1300 1344 1344 1344 1319 1289 1264 1264 1264 1344 1344 1238 1152 +glyph 158 Ü +contours 19,23,27 +x 549 419 225 119 119 119 262 262 262 409 548 690 848 848 848 991 991 991 882 684 640 640 763 763 345 345 469 469 +y 0 0 96 279 405 1088 1088 416 269 116 116 116 274 426 1088 1088 419 289 100 0 1152 1290 1290 1152 1152 1290 1290 1152 diff --git a/fontbox/src/test/resources/ttf/hinting/README.md b/fontbox/src/test/resources/ttf/hinting/README.md new file mode 100644 index 00000000000..66e4aff6f66 --- /dev/null +++ b/fontbox/src/test/resources/ttf/hinting/README.md @@ -0,0 +1,103 @@ + + +# TrueType hinting verification tooling + +These are developer/debugging tools for the FontBox TrueType bytecode interpreter +(`org.apache.fontbox.ttf.instruction`). They use **FreeType only as an offline oracle** — FreeType is +never linked, shipped, or a build dependency. The committed data files are plain coordinate/trace +facts, not derivatives of FreeType (see `hinting_plan.md` "Oracle licensing"). + +## Files + +| File | Purpose | +|------|---------| +| `generate_golden.py` | Dumps FreeType's post-hinting outline points for the Tier-A fonts to `-.txt`. These back `GoldenHintingTest`. | +| `LiberationSans-Regular-*.txt` | The committed golden coordinate data (one file per ppem). | +| `trace_diff.py` | Aligns FreeType's per-instruction trace against the FontBox interpreter's trace and reports the first divergence (program counter, operand stack, or point coordinate). | +| `ft_point_trace.c` | FreeType single-stepper: dumps one glyph point's coordinate per instruction, for localizing *silent* point-position divergence. | +| `README.md` | This file. | + +## Golden coordinate test (CI) + +`GoldenHintingTest` compares the interpreter's grid-fitted points against the committed `.txt` dumps. +To regenerate the dumps after intentional changes: + +```sh +pip install --user freetype-py +python3 generate_golden.py +``` + +## Trace-diff harness (manual debugging) + +When a glyph's final points differ from FreeType, the trace diff localizes the cause to a single +instruction far faster than staring at coordinates. It needs a FreeType built **with tracing** (the +stock library has it compiled out): + +```sh +curl -LO https://download.savannah.gnu.org/releases/freetype/freetype-2.13.2.tar.gz +tar xzf freetype-2.13.2.tar.gz && cd freetype-2.13.2 +./configure CFLAGS="-DFT_DEBUG_LEVEL_TRACE -g -O1" --disable-static +make -j +# point freetype-py at the result (replace its bundled copy or LD_PRELOAD it): +cp objs/.libs/libfreetype.so.6.* "$(python3 -c 'import freetype,os;print(os.path.dirname(freetype.__file__))')/libfreetype.so" +``` + +Then dump the FontBox trace for a glyph and diff it: + +```sh +# 1. FontBox trace (from the fontbox module dir): +mvn -pl fontbox test -Dtest=GlyphTraceTool -Denforcer.skip=true \ + -Dtrace.gid=164 -Dtrace.ppem=11 -Dtrace.out=/tmp/our-trace.txt + +# 2. diff against FreeType (from this directory): +FT2_DEBUG=ttinterp:7 python3 trace_diff.py --gid 164 --ppem 11 --ours /tmp/our-trace.txt --stack +``` + +Both traces use the line format ` # `. The tool compares the +program counter (catches control-flow divergence) and, with `--stack`, the operand window (catches a +diverging computed value — this is how the `DIV`-rounding bug was found). If neither diverges, the +remaining difference is a *silent* point-position computation in a point-moving opcode (MDRP/MIRP/IP/…) +that never reaches the stack — compare final point positions to find it. + +The interpreter side is driven by `TrueTypeInterpreter.setTracer(ExecutionTracer)`; it is off in +normal operation. + +### Localizing a *silent* point divergence (points extension) + +When the stack matches FreeType end-to-end but the final points still differ, the divergence is a +point-moving opcode computing a slightly different displacement from an input that never reaches the +stack (e.g. a CVT value). To find which instruction, compare the point coordinate itself per +instruction. `ft_point_trace.c` is the FreeType half (build instructions are in its header comment); +it needs a **static** FreeType built with the bytecode interpreter so it can link the internal +`TT_RunIns` and single-step via the debug hook. + +```sh +# FreeType per-instruction point trace (point 8 of glyph 648): +./ft_point_trace ../LiberationSans-Regular.ttf 648 11 8 > /tmp/ft-pt.txt + +# FontBox per-instruction point trace for the same point: +mvn -pl fontbox test -Dtest=GlyphTraceTool -Denforcer.skip=true \ + -Dtrace.gid=648 -Dtrace.ppem=11 -Dtrace.point=8 -Dtrace.out=/tmp/our-pt.txt + +# diff the point column: +python3 trace_diff.py --gid 648 --ppem 11 --ours /tmp/our-pt.txt --ft /tmp/ft-pt.txt --point +``` + +The output names the exact instruction whose result first differs - e.g. it localized the +`a-circumflex` residual to a single `MIRP` (the circumflex height), where the point goes in equal +(80,513) and comes out (80,512) in FreeType versus (80,484) in FontBox. diff --git a/fontbox/src/test/resources/ttf/hinting/ft_point_trace.c b/fontbox/src/test/resources/ttf/hinting/ft_point_trace.c new file mode 100644 index 00000000000..35f167b35c6 --- /dev/null +++ b/fontbox/src/test/resources/ttf/hinting/ft_point_trace.c @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * FreeType per-instruction point tracer - the FreeType half of the "points extension" to the + * trace-diff harness. It single-steps the TrueType bytecode interpreter via the debug hook and prints, + * for each instruction, the current coordinate of one glyph point. Diffing this against the FontBox + * trace (GlyphTraceTool with -Dtrace.point) localizes a *silent* point-position divergence (one a + * point-moving opcode produces without it ever reaching the operand stack) to a single instruction. + * + * FreeType is used here only as an offline debugging oracle - never shipped or a build dependency. + * + * Build (against a static FreeType built with the bytecode interpreter; internal headers required): + * + * FT=/path/to/freetype-2.13.2 + * ./$FT/configure CFLAGS="-DFT_DEBUG_LEVEL_TRACE -DFT_DEBUG_LEVEL_DEBUG -g -O0" \ + * --enable-static --disable-shared --without-zlib --without-png --without-harfbuzz \ + * --without-brotli --without-bzip2 && make -C $FT -j + * gcc -DFT2_BUILD_LIBRARY -I$FT/include -I$FT/src ft_point_trace.c \ + * $FT/objs/.libs/libfreetype.a -lm -o ft_point_trace + * + * Run: + * ./ft_point_trace LiberationSans-Regular.ttf # lines: " op=0xNN Pn=(x,y)" + */ +#include +#include + +#include +#include FT_FREETYPE_H +#include /* FT_Set_Debug_Hook, FT_DEBUG_HOOK_TRUETYPE */ +#include "truetype/ttinterp.h" /* TT_ExecContext, TT_RunIns (internal) */ + +static int g_point = -1; + +static FT_Error +trace_hook( void* exec ) +{ + TT_ExecContext exc = (TT_ExecContext)exec; + FT_Error err = FT_Err_Ok; + + + exc->instruction_trap = 1; /* make TT_RunIns return after each instruction */ + + while ( exc->IP < exc->codeSize ) + { + long ip = exc->IP; + FT_Byte op = exc->code[ip]; + + + if ( g_point >= 0 && exc->pts.n_points > g_point ) + printf( "%06ld op=0x%02X P%d=(%ld,%ld)\n", ip, op, g_point, + (long)exc->pts.cur[g_point].x, (long)exc->pts.cur[g_point].y ); + else + printf( "%06ld op=0x%02X\n", ip, op ); + + err = TT_RunIns( exec ); + if ( err ) + break; + } + + return err; +} + + +int +main( int argc, char** argv ) +{ + FT_Library lib; + FT_Face face; + + + if ( argc < 5 ) + { + fprintf( stderr, "usage: %s \n", argv[0] ); + return 2; + } + + g_point = atoi( argv[4] ); + + if ( FT_Init_FreeType( &lib ) ) + return 1; + + FT_Set_Debug_Hook( lib, FT_DEBUG_HOOK_TRUETYPE, (FT_DebugHook_Func)trace_hook ); + + if ( FT_New_Face( lib, argv[1], 0, &face ) ) + return 1; + + FT_Set_Pixel_Sizes( face, 0, atoi( argv[3] ) ); + FT_Load_Glyph( face, atoi( argv[2] ), + FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO ); + + FT_Done_Face( face ); + FT_Done_FreeType( lib ); + return 0; +} diff --git a/fontbox/src/test/resources/ttf/hinting/generate_golden.py b/fontbox/src/test/resources/ttf/hinting/generate_golden.py new file mode 100644 index 00000000000..3289f47f6d5 --- /dev/null +++ b/fontbox/src/test/resources/ttf/hinting/generate_golden.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Generates TrueType hinting golden reference data from FreeType, for the Java golden tests. + +FreeType is used here ONLY as an offline verification oracle - it is never shipped, linked, or made a +build dependency. The committed JSON files are plain coordinate facts about the fonts (the F26Dot6 +points a correct bytecode interpreter must produce), not a derivative of FreeType. See +hinting_plan.md "Oracle licensing". + +Run with freetype-py installed (`pip install freetype-py`): + + python3 generate_golden.py + +It writes -.txt next to this script in a compact, dependency-free format (so the Java +test needs no JSON library). The glyphs are loaded with the monochrome hinting target so FreeType runs +the native TrueType bytecode interpreter in full-pixel (non-subpixel) mode, which is what the FontBox +interpreter implements. + +Format (one file per ppem): + + font + ppem + freetype + glyph + contours + x ... + y ... + glyph ... +""" +import os + +import freetype + +HERE = os.path.dirname(os.path.abspath(__file__)) +FONT_DIR = os.path.normpath(os.path.join(HERE, "..")) + +FONTS = ["LiberationSans-Regular.ttf"] +PPEMS = [11, 13, 16, 24] +# simple, well-hinted glyphs plus common composites (accented letters: base glyph + diacritic) +CHARS = "HILEThoxn0123456789" + "áàâäãéèçñüÁÉÑÜ" + +# native bytecode hinting (no autohinter), grayscale target => FreeType's v40 "minimal" subpixel +# interpreter with backward-compatibility (no x grid-fitting, y frozen post-IUP). This matches how +# PDFBox rasterizes (Java2D is always antialiased); see GlyphHinter / ExecutionContext.movePoint. +LOAD_FLAGS = freetype.FT_LOAD_NO_AUTOHINT | freetype.FT_LOAD_TARGET_NORMAL + +# Prepended to every generated file: these are checked into an Apache project, and the parser in +# GoldenHintingTest ignores any line that is not "glyph "/"x "/"y ", so comments cost nothing. +LICENSE_HEADER = [ + "# Licensed to the Apache Software Foundation (ASF) under one or more", + "# contributor license agreements. See the NOTICE file distributed with", + "# this work for additional information regarding copyright ownership.", + "# The ASF licenses this file to You under the Apache License, Version 2.0", + "# (the \"License\"); you may not use this file except in compliance with", + "# the License. You may obtain a copy of the License at", + "#", + "# http://www.apache.org/licenses/LICENSE-2.0", + "#", + "# Unless required by applicable law or agreed to in writing, software", + "# distributed under the License is distributed on an \"AS IS\" BASIS,", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "# See the License for the specific language governing permissions and", + "# limitations under the License.", +] + + +def dump_font(font_name): + face = freetype.Face(os.path.join(FONT_DIR, font_name)) + version = ".".join(str(v) for v in freetype.version()) + for ppem in PPEMS: + face.set_pixel_sizes(0, ppem) + lines = list(LICENSE_HEADER) + lines += [f"font {font_name}", f"ppem {ppem}", f"freetype {version}"] + count = 0 + for ch in CHARS: + gid = face.get_char_index(ord(ch)) + if gid == 0: + continue + face.load_glyph(gid, LOAD_FLAGS) + outline = face.glyph.outline + lines.append(f"glyph {gid} {ch}") + lines.append("contours " + ",".join(str(c) for c in outline.contours)) + lines.append("x " + " ".join(str(p[0]) for p in outline.points)) + lines.append("y " + " ".join(str(p[1]) for p in outline.points)) + count += 1 + out = os.path.join(HERE, f"{os.path.splitext(font_name)[0]}-{ppem}.txt") + with open(out, "w") as fh: + fh.write("\n".join(lines) + "\n") + print(f"wrote {out} ({count} glyphs)") + + +if __name__ == "__main__": + for font in FONTS: + dump_font(font) diff --git a/fontbox/src/test/resources/ttf/hinting/trace_diff.py b/fontbox/src/test/resources/ttf/hinting/trace_diff.py new file mode 100644 index 00000000000..93a9d7a5816 --- /dev/null +++ b/fontbox/src/test/resources/ttf/hinting/trace_diff.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +TrueType hinting trace-diff harness. + +Captures FreeType's per-instruction ``ttinterp`` trace for one glyph and aligns it, instruction by +instruction, against the FontBox interpreter's trace, reporting the first instruction where the +program counter, opcode, or operand stack diverges. This localizes a hinting bug to the exact +instruction far faster than comparing final outlines. + +FreeType is used here only as an offline debugging oracle - never shipped or a build dependency. + +Prerequisites +------------- +1. A FreeType built with tracing (the stock library has it compiled out). For example: + + curl -LO https://download.savannah.gnu.org/releases/freetype/freetype-2.13.2.tar.gz + tar xzf freetype-2.13.2.tar.gz && cd freetype-2.13.2 + ./configure CFLAGS="-DFT_DEBUG_LEVEL_TRACE -g -O1" --disable-static + make -j + Then point freetype-py at objs/.libs/libfreetype.so (replace its bundled copy, or LD_PRELOAD it), + and ``pip install freetype-py``. + +2. The FontBox trace for the same glyph, produced by GlyphTraceTool: + + mvn -pl fontbox test -Dtest=GlyphTraceTool -Denforcer.skip=true \ + -Dtrace.gid=22 -Dtrace.ppem=11 -Dtrace.out=/tmp/our-trace.txt + +Usage +----- + FT2_DEBUG=ttinterp:7 python3 trace_diff.py --gid 22 --ppem 11 --ours /tmp/our-trace.txt + +Both traces use a line format starting with `` `` and optionally carrying a point column +``Pn=(x,y)`` and/or a stack window ``# ...``. The program counter is always compared (catching +control-flow divergence). Pass --stack to also compare the operand window. To localize a *silent* +point-position divergence, pass --ft a FreeType point trace produced by ft_point_trace (which carries +``Pn=(x,y)``) and dump the FontBox trace with ``-Dtrace.point=n``; the point column is then compared. +""" +import argparse +import os +import re +import sys + +PC = re.compile(r"^\s*(\d+)\s+(\S+)") +POINT = re.compile(r"P\d+=\((-?\d+),(-?\d+)\)") +STACK = re.compile(r"#(.*)$") + + +def parse_trace(lines): + out = [] + for line in lines: + m = PC.match(line) + if not m: + continue + pc = int(m.group(1)) + op = m.group(2) + pm = POINT.search(line) + point = (int(pm.group(1)), int(pm.group(2))) if pm else None + sm = STACK.search(line) + stack = sm.group(1).split() if sm else [] + out.append((pc, op, point, stack)) + return out + + +def freetype_trace(font, gid, ppem): + import freetype # imported lazily so the rest of the tool works without it + face = freetype.Face(font) + face.set_pixel_sizes(0, ppem) + # FT2_DEBUG must be set in the environment before FreeType is first used + import io + import contextlib + # FreeType writes the trace to stderr; capture it + err_fd = os.dup(2) + r, w = os.pipe() + os.dup2(w, 2) + try: + face.load_glyph(gid, freetype.FT_LOAD_NO_AUTOHINT | freetype.FT_LOAD_TARGET_MONO) + finally: + os.dup2(err_fd, 2) + os.close(w) + data = os.read(r, 1 << 22).decode("latin1", "replace") + os.close(r) + os.close(err_fd) + return data.splitlines() + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--font", default=os.path.join(os.path.dirname(__file__), "..", + "LiberationSans-Regular.ttf")) + ap.add_argument("--gid", type=int, required=True) + ap.add_argument("--ppem", type=int, default=11) + ap.add_argument("--ours", required=True, help="FontBox trace file from GlyphTraceTool") + ap.add_argument("--ft", help="FreeType trace file (e.g. from ft_point_trace); " + "if omitted, FreeType's ttinterp trace is captured via freetype-py") + ap.add_argument("--stack", action="store_true", help="also compare the operand stack window") + ap.add_argument("--point", action="store_true", help="compare the Pn=(x,y) point column") + args = ap.parse_args() + + if args.ft: + with open(args.ft, encoding="utf-8") as fh: + ft = parse_trace(fh.readlines()) + else: + ft = parse_trace(freetype_trace(args.font, args.gid, args.ppem)) + with open(args.ours, encoding="utf-8") as fh: + ours = parse_trace(fh.readlines()) + + # FreeType traces fpgm + prep + glyph; FontBox traces only the glyph program. Align on the tail. + print(f"FreeType instructions: {len(ft)} FontBox instructions: {len(ours)}") + ft_tail = ft[-len(ours):] if len(ft) >= len(ours) else ft + + for i, (a, b) in enumerate(zip(ft_tail, ours)): + pc_a, op_a, pt_a, st_a = a + pc_b, op_b, pt_b, st_b = b + mismatch = pc_a != pc_b + if args.stack and st_a[: len(st_b)] != st_b[: len(st_a)]: + mismatch = True + if args.point and pt_a is not None and pt_b is not None and pt_a != pt_b: + mismatch = True + if mismatch: + print(f"\nFirst divergence at aligned instruction {i} (the instruction that produced it " + "is the one before, where the point/stack still matched):") + print(f" FreeType: pc={pc_a:6d} {op_a:10s} pt={pt_a} # {' '.join(st_a)}") + print(f" FontBox : pc={pc_b:6d} {op_b:10s} pt={pt_b} # {' '.join(st_b)}") + print(" (context, FreeType | FontBox):") + for j in range(max(0, i - 4), i + 1): + print(f" {ft_tail[j][0]:6d} {ft_tail[j][1]:10s} {ft_tail[j][2]} | " + f"{ours[j][0]:6d} {ours[j][1]:10s} {ours[j][2]}") + return 1 + print(f"\nNo divergence over {min(len(ft_tail), len(ours))} aligned instructions for the compared " + "columns.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From 79ad09523f5b7c497747df32f4b9dc71ba5d4878 Mon Sep 17 00:00:00 2001 From: Patrick Corless Date: Wed, 16 Sep 2026 22:13:47 -0600 Subject: [PATCH 6/8] PDFBOX-3293 remove duplicate LiberationSans-Regular.ttf resource and touch up respective tests with source of truth. --- .../font/PDTrueTypeFontHintingTest.java | 14 +++++++++----- .../pdmodel/font/PDType0FontHintingTest.java | 17 ++++++++++------- .../RenderHintingIntegrationTest.java | 12 ++++++++---- .../pdfbox/ttf/LiberationSans-Regular.ttf | Bin 410712 -> 0 bytes 4 files changed, 27 insertions(+), 16 deletions(-) delete mode 100644 pdfbox/src/test/resources/org/apache/pdfbox/ttf/LiberationSans-Regular.ttf diff --git a/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFontHintingTest.java b/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFontHintingTest.java index 1480a9d5a4f..e0113f13aeb 100644 --- a/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFontHintingTest.java +++ b/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFontHintingTest.java @@ -24,8 +24,8 @@ import java.awt.geom.GeneralPath; import java.awt.geom.PathIterator; import java.awt.geom.Rectangle2D; -import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.util.Arrays; import org.apache.fontbox.ttf.TrueTypeFont; @@ -59,15 +59,19 @@ void restoreHinting() TrueTypeFont.setHintingEnabled(false); } - private static final File FONT = - new File("src/test/resources/org/apache/pdfbox/ttf/LiberationSans-Regular.ttf"); + // PDTrueTypeFont.load() consumes and closes the stream, so each caller needs a fresh one + private static InputStream fontStream() + { + return TrueTypeFont.class.getResourceAsStream( + "/org/apache/pdfbox/resources/ttf/LiberationSans-Regular.ttf"); + } @Test void testHintedNormalizedPathDiffersFromUnhinted() throws IOException { try (PDDocument doc = new PDDocument()) { - PDTrueTypeFont font = PDTrueTypeFont.load(doc, FONT, WinAnsiEncoding.INSTANCE); + PDTrueTypeFont font = PDTrueTypeFont.load(doc, fontStream(), WinAnsiEncoding.INSTANCE); int code = 'H'; GeneralPath hinted = font.getHintedNormalizedPath(code, 16); @@ -90,7 +94,7 @@ void testGaspGateReturnsNullAtSmallPpem() throws IOException { try (PDDocument doc = new PDDocument()) { - PDTrueTypeFont font = PDTrueTypeFont.load(doc, FONT, WinAnsiEncoding.INSTANCE); + PDTrueTypeFont font = PDTrueTypeFont.load(doc, fontStream(), WinAnsiEncoding.INSTANCE); // LiberationSans gasp disables grid-fitting at <= 8 ppem assertNull(font.getHintedNormalizedPath('H', 8)); assertNotNull(font.getHintedNormalizedPath('H', 16)); diff --git a/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDType0FontHintingTest.java b/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDType0FontHintingTest.java index 194f8b3fa5f..5b34500d57f 100644 --- a/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDType0FontHintingTest.java +++ b/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDType0FontHintingTest.java @@ -26,6 +26,8 @@ import java.awt.geom.Rectangle2D; import java.io.File; import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -50,9 +52,6 @@ @Isolated // TrueTypeFont hinting is a global switch; other classes must not render while it is on class PDType0FontHintingTest { - private static final File FONT = - new File("src/test/resources/org/apache/pdfbox/ttf/LiberationSans-Regular.ttf"); - @BeforeEach void enableHinting() { @@ -69,16 +68,20 @@ void restoreHinting() * Embeds the font whole (no subsetting) so the encoding is Identity and a character code is its * own glyph id, which keeps the test about hinting rather than about CID mapping. */ - private static PDType0Font load(PDDocument doc, int[] gidOut) throws IOException + private static PDType0Font load(PDDocument doc, int[] gidOut) throws IOException, URISyntaxException { - TrueTypeFont ttf = new TTFParser().parse(new RandomAccessReadBufferedFile(FONT)); + URL url = TrueTypeFont.class.getResource( + "/org/apache/pdfbox/resources/ttf/LiberationSans-Regular.ttf"); + File fontFile = new File(url.toURI()); + + TrueTypeFont ttf = new TTFParser().parse(new RandomAccessReadBufferedFile(fontFile)); gidOut[0] = ttf.getUnicodeCmapLookup().getGlyphId('H'); assertTrue(gidOut[0] > 0, "no glyph for 'H'"); return PDType0Font.load(doc, ttf, false); } @Test - void testHintedNormalizedPathDiffersFromUnhinted() throws IOException + void testHintedNormalizedPathDiffersFromUnhinted() throws IOException, URISyntaxException { try (PDDocument doc = new PDDocument()) { @@ -100,7 +103,7 @@ void testHintedNormalizedPathDiffersFromUnhinted() throws IOException } @Test - void testGaspGateReturnsNullAtSmallPpem() throws IOException + void testGaspGateReturnsNullAtSmallPpem() throws IOException, URISyntaxException { try (PDDocument doc = new PDDocument()) { diff --git a/pdfbox/src/test/java/org/apache/pdfbox/rendering/RenderHintingIntegrationTest.java b/pdfbox/src/test/java/org/apache/pdfbox/rendering/RenderHintingIntegrationTest.java index 0dc39d2c11d..e2431b0cf3d 100644 --- a/pdfbox/src/test/java/org/apache/pdfbox/rendering/RenderHintingIntegrationTest.java +++ b/pdfbox/src/test/java/org/apache/pdfbox/rendering/RenderHintingIntegrationTest.java @@ -20,8 +20,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.awt.image.BufferedImage; -import java.io.File; import java.io.IOException; +import java.io.InputStream; import org.apache.fontbox.ttf.TrueTypeFont; import org.apache.pdfbox.pdmodel.PDDocument; @@ -41,8 +41,12 @@ @Isolated // TrueTypeFont hinting is a global switch; other classes must not render while it is on class RenderHintingIntegrationTest { - private static final File FONT = - new File("src/test/resources/org/apache/pdfbox/ttf/LiberationSans-Regular.ttf"); + // PDTrueTypeFont.load() consumes and closes the stream, so each caller needs a fresh one + private static InputStream fontStream() + { + return RenderHintingIntegrationTest.class.getResourceAsStream( + "/org/apache/pdfbox/resources/ttf/LiberationSans-Regular.ttf"); + } @AfterEach void restoreHinting() @@ -78,7 +82,7 @@ private static byte[] buildPdf() throws IOException { PDPage page = new PDPage(new PDRectangle(160, 60)); doc.addPage(page); - PDTrueTypeFont font = PDTrueTypeFont.load(doc, FONT, WinAnsiEncoding.INSTANCE); + PDTrueTypeFont font = PDTrueTypeFont.load(doc, fontStream(), WinAnsiEncoding.INSTANCE); try (PDPageContentStream cs = new PDPageContentStream(doc, page)) { cs.beginText(); diff --git a/pdfbox/src/test/resources/org/apache/pdfbox/ttf/LiberationSans-Regular.ttf b/pdfbox/src/test/resources/org/apache/pdfbox/ttf/LiberationSans-Regular.ttf deleted file mode 100644 index e6339859d0b24bee79ae3f64e0071900170224ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 410712 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 U^4IX6nfTvc!IeM%9f$n?0IO~PegFUf From d716d9962b5841a01fb59eaf3701e61a4732594c Mon Sep 17 00:00:00 2001 From: Patrick Corless Date: Wed, 16 Sep 2026 22:52:43 -0600 Subject: [PATCH 7/8] PDFBOX-3293 move enabling hinting out to PDFRenderer instead of in TrueTypeFont and a system property. --- .../org/apache/fontbox/ttf/GlyphHinter.java | 5 +- .../org/apache/fontbox/ttf/TrueTypeFont.java | 61 +------- .../apache/fontbox/ttf/GoldenHintingTest.java | 15 -- .../fontbox/ttf/HintingConcurrencyTest.java | 14 -- .../fontbox/ttf/HintingIntegrationTest.java | 26 --- .../apache/fontbox/ttf/HintingSwitchTest.java | 148 ------------------ .../apache/pdfbox/printing/PDFPrintable.java | 25 +++ .../apache/pdfbox/rendering/PDFRenderer.java | 28 +++- .../apache/pdfbox/rendering/PageDrawer.java | 12 +- .../rendering/PageDrawerParameters.java | 16 +- .../font/PDTrueTypeFontHintingTest.java | 20 +-- .../pdmodel/font/PDType0FontHintingTest.java | 16 -- .../RenderHintingIntegrationTest.java | 23 +-- 13 files changed, 84 insertions(+), 325 deletions(-) delete mode 100644 fontbox/src/test/java/org/apache/fontbox/ttf/HintingSwitchTest.java diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphHinter.java b/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphHinter.java index a15959b82aa..82c8c6dc558 100644 --- a/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphHinter.java +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphHinter.java @@ -35,8 +35,7 @@ * Hinting is best-effort: anything malformed, unsupported, or not applicable (a composite glyph, a * glyph with no instructions, a ppem the {@code gasp} table excludes) falls back to {@code null}, and * the caller renders the raw outline. One bad glyph never disables hinting for the rest of the font. - * Hinting as a whole is switched on and off by {@link TrueTypeFont#isHintingEnabled()}; while it is - * off every glyph falls back to {@code null}. + * Whether to hint at all is the caller's decision; this class always grid-fits when asked. *

* The interpreter carries a great deal of mutable state - the storage area, the twilight zone, the * post-{@code prep} template, the active ppem - so every entry point here is {@code synchronized} and @@ -187,7 +186,7 @@ synchronized void traceGlyph(int gid, int ppem, java.io.PrintStream out, int tra /** Runs all gating, then grid-fits the glyph, returning the executed zone or null on fallback. */ private Hinted hint(int gid, int ppem) { - if (!TrueTypeFont.isHintingEnabled() || ppem <= 0) + if (ppem <= 0) { return null; } diff --git a/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java b/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java index b730122d808..07ff1a00130 100644 --- a/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java +++ b/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java @@ -58,61 +58,6 @@ public class TrueTypeFont implements FontBoxFont, Closeable private final List enabledGsubFeatures = new ArrayList<>(); private GlyphHinter hinter; - /** - * System property controlling whether TrueType bytecode hinting (grid-fitting) is applied to glyph - * outlines, e.g. {@code -Dorg.apache.fontbox.ttf.hinting=true}. Hinting is off by default, which - * preserves the raw outlines FontBox has always returned. The property is read once, on first use; - * call {@link #setHintingEnabled(boolean)} to change the setting after that. - */ - public static final String SYSPROP_HINTING = "org.apache.fontbox.ttf.hinting"; - - /** Resolved hinting setting; null until first read of {@link #SYSPROP_HINTING}. */ - private static volatile Boolean hintingEnabled; - - /** - * Returns whether TrueType bytecode hinting is enabled, resolving {@link #SYSPROP_HINTING} on the - * first call. This is the single switch for the feature: {@link #getHintedPath(int, int)} returns - * {@code null} while it is off, and callers that grid-fit at render time should consult it before - * asking for a hinted path. - * - * @return true if hinting is enabled - */ - public static boolean isHintingEnabled() - { - Boolean enabled = hintingEnabled; - if (enabled == null) - { - enabled = Boolean.parseBoolean(System.getProperty(SYSPROP_HINTING)); - hintingEnabled = enabled; - } - return enabled; - } - - /** - * Enables or disables TrueType bytecode hinting, overriding {@link #SYSPROP_HINTING}. This is a - * global setting; it takes effect for fonts already parsed. - *

- * Intended for start-up and for tests. Renderers consult {@link #isHintingEnabled()} per glyph, so - * flipping this while a page is being drawn produces a half-hinted page rather than an error. - * - * @param enabled true to grid-fit glyph outlines, false to return raw outlines - */ - public static void setHintingEnabled(boolean enabled) - { - hintingEnabled = enabled; - } - - /** - * Discards the resolved setting so the next {@link #isHintingEnabled()} reads - * {@link #SYSPROP_HINTING} again. Package-private, and here for the tests that cover the - * property-to-default resolution: the setting is resolved once per JVM, so without this a test - * cannot observe what a fresh JVM would have seen. - */ - static void resetHintingSetting() - { - hintingEnabled = null; - } - /** * Constructor. Clients should use the TTFParser to create a new TrueTypeFont object. * @@ -892,9 +837,9 @@ public float getWidth(String name) throws IOException /** * Returns the grid-fitted (hinted) path of the given glyph at the given ppem, in font units, or - * {@code null} if hinting does not apply (hinting disabled - see {@link #isHintingEnabled()} - no - * bytecode program, a composite or empty glyph, or a ppem excluded by the gasp table). The caller - * should fall back to the raw outline + * {@code null} if hinting does not apply (no bytecode program, a composite or empty glyph, or a + * ppem excluded by the gasp table). Whether to hint at all is the caller's decision, e.g. + * {@code PDFRenderer.setHintingEnabled(boolean)}; the caller should fall back to the raw outline * ({@link GlyphData#getPath()}) when this returns {@code null}. * * @param gid the glyph id diff --git a/fontbox/src/test/java/org/apache/fontbox/ttf/GoldenHintingTest.java b/fontbox/src/test/java/org/apache/fontbox/ttf/GoldenHintingTest.java index ae318b8ae1f..dce85c4ff6f 100644 --- a/fontbox/src/test/java/org/apache/fontbox/ttf/GoldenHintingTest.java +++ b/fontbox/src/test/java/org/apache/fontbox/ttf/GoldenHintingTest.java @@ -28,8 +28,6 @@ import java.util.List; import org.apache.pdfbox.io.RandomAccessReadBuffer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** @@ -55,19 +53,6 @@ */ class GoldenHintingTest { - // hinting is off by default, so the golden comparison has to turn the feature on first - @BeforeEach - void enableHinting() - { - TrueTypeFont.setHintingEnabled(true); - } - - @AfterEach - void restoreHinting() - { - TrueTypeFont.setHintingEnabled(false); - } - private static final int[] PPEMS = { 11, 13, 16, 24 }; /** diff --git a/fontbox/src/test/java/org/apache/fontbox/ttf/HintingConcurrencyTest.java b/fontbox/src/test/java/org/apache/fontbox/ttf/HintingConcurrencyTest.java index d3dc163b203..f861db0f3fa 100644 --- a/fontbox/src/test/java/org/apache/fontbox/ttf/HintingConcurrencyTest.java +++ b/fontbox/src/test/java/org/apache/fontbox/ttf/HintingConcurrencyTest.java @@ -37,8 +37,6 @@ import java.util.concurrent.TimeUnit; import org.apache.pdfbox.io.RandomAccessReadBuffer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** @@ -54,18 +52,6 @@ class HintingConcurrencyTest private static final int[] PPEMS = { 11, 13, 16, 24 }; private static final String GLYPHS = "HILEToxn"; - @BeforeEach - void enableHinting() - { - TrueTypeFont.setHintingEnabled(true); - } - - @AfterEach - void restoreHinting() - { - TrueTypeFont.setHintingEnabled(false); - } - private static TrueTypeFont parse() throws IOException { try (InputStream is = HintingConcurrencyTest.class diff --git a/fontbox/src/test/java/org/apache/fontbox/ttf/HintingIntegrationTest.java b/fontbox/src/test/java/org/apache/fontbox/ttf/HintingIntegrationTest.java index fd82ad73bb7..d843d8893a5 100644 --- a/fontbox/src/test/java/org/apache/fontbox/ttf/HintingIntegrationTest.java +++ b/fontbox/src/test/java/org/apache/fontbox/ttf/HintingIntegrationTest.java @@ -28,8 +28,6 @@ import java.util.Arrays; import org.apache.pdfbox.io.RandomAccessReadBuffer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** @@ -39,19 +37,6 @@ */ class HintingIntegrationTest { - // hinting is off by default, so every test here has to turn the feature on first - @BeforeEach - void enableHinting() - { - TrueTypeFont.setHintingEnabled(true); - } - - @AfterEach - void restoreHinting() - { - TrueTypeFont.setHintingEnabled(false); - } - private static TrueTypeFont parse(String resource) throws IOException { try (InputStream is = HintingIntegrationTest.class.getResourceAsStream(resource)) @@ -173,17 +158,6 @@ void testHintingIsIndependentOfPpemOrder() throws IOException "hinting at other ppems must not change the result at 16ppem"); } - @Test - void testEscapeHatchDisablesHinting() throws IOException - { - TrueTypeFont font = parse("/ttf/LiberationSans-Regular.ttf"); - int h = gid(font, 'H'); - TrueTypeFont.setHintingEnabled(false); - assertNull(font.getHintedPath(h, 16), "escape hatch must disable hinting"); - TrueTypeFont.setHintingEnabled(true); - assertNotNull(font.getHintedPath(h, 16), "hinting restored once the switch is back on"); - } - @Test void testFontWithoutBytecodeFallsBack() throws IOException { diff --git a/fontbox/src/test/java/org/apache/fontbox/ttf/HintingSwitchTest.java b/fontbox/src/test/java/org/apache/fontbox/ttf/HintingSwitchTest.java deleted file mode 100644 index 6cc23740a74..00000000000 --- a/fontbox/src/test/java/org/apache/fontbox/ttf/HintingSwitchTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.fontbox.ttf; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.IOException; -import java.io.InputStream; - -import org.apache.pdfbox.io.RandomAccessReadBuffer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -/** - * The switch itself: that hinting is off unless it is asked for, that - * {@link TrueTypeFont#SYSPROP_HINTING} is what asks for it, and that the setter overrides the property. - *

- * The setting is resolved once per JVM, so every test here clears the resolved value first - otherwise - * it would be observing whatever an earlier test class left behind rather than what a fresh JVM sees. - */ -class HintingSwitchTest -{ - private String previousProperty; - - @BeforeEach - void rememberProperty() - { - previousProperty = System.getProperty(TrueTypeFont.SYSPROP_HINTING); - } - - /** Puts the JVM back exactly as it was: the property restored and nothing resolved from it yet. */ - @AfterEach - void restoreProperty() - { - if (previousProperty == null) - { - System.clearProperty(TrueTypeFont.SYSPROP_HINTING); - } - else - { - System.setProperty(TrueTypeFont.SYSPROP_HINTING, previousProperty); - } - TrueTypeFont.resetHintingSetting(); - } - - private static void resolve(String propertyValue) - { - if (propertyValue == null) - { - System.clearProperty(TrueTypeFont.SYSPROP_HINTING); - } - else - { - System.setProperty(TrueTypeFont.SYSPROP_HINTING, propertyValue); - } - TrueTypeFont.resetHintingSetting(); - } - - private static TrueTypeFont parse() throws IOException - { - try (InputStream is = HintingSwitchTest.class - .getResourceAsStream("/ttf/LiberationSans-Regular.ttf")) - { - assertNotNull(is, "missing test font"); - return new TTFParser().parse(new RandomAccessReadBuffer(is)); - } - } - - private static int gid(TrueTypeFont font) throws IOException - { - return font.getUnicodeCmapLookup().getGlyphId('H'); - } - - /** - * The contract the whole feature rests on while it is opt-in: a JVM that was never told to hint - * renders exactly what it rendered before hinting existed. - */ - @Test - void testHintingIsOffWhenThePropertyIsNotSet() throws IOException - { - resolve(null); - assertFalse(TrueTypeFont.isHintingEnabled(), "hinting must be off by default"); - - TrueTypeFont font = parse(); - assertNull(font.getHintedPath(gid(font), 16), "an unconfigured JVM must not grid-fit"); - } - - @Test - void testPropertyTurnsHintingOn() throws IOException - { - resolve("true"); - assertTrue(TrueTypeFont.isHintingEnabled()); - - TrueTypeFont font = parse(); - assertNotNull(font.getHintedPath(gid(font), 16), "the property should enable grid-fitting"); - } - - @Test - void testPropertyIsCaseInsensitive() - { - resolve("TRUE"); - assertTrue(TrueTypeFont.isHintingEnabled(), "-D...=TRUE should work like =true"); - } - - @Test - void testAnythingOtherThanTrueLeavesHintingOff() - { - resolve("false"); - assertFalse(TrueTypeFont.isHintingEnabled()); - - resolve("yes"); - assertFalse(TrueTypeFont.isHintingEnabled(), "only \"true\" enables hinting"); - } - - /** The programmatic switch is documented to override the property; nothing pinned that. */ - @Test - void testSetterOverridesTheProperty() throws IOException - { - resolve("false"); - TrueTypeFont.setHintingEnabled(true); - assertTrue(TrueTypeFont.isHintingEnabled()); - - TrueTypeFont font = parse(); - assertNotNull(font.getHintedPath(gid(font), 16)); - - resolve("true"); - TrueTypeFont.setHintingEnabled(false); - assertFalse(TrueTypeFont.isHintingEnabled()); - } -} diff --git a/pdfbox/src/main/java/org/apache/pdfbox/printing/PDFPrintable.java b/pdfbox/src/main/java/org/apache/pdfbox/printing/PDFPrintable.java index b982099f3c7..38c0e73ff1f 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/printing/PDFPrintable.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/printing/PDFPrintable.java @@ -58,6 +58,7 @@ public final class PDFPrintable implements Printable private final float dpi; private final boolean center; private boolean subsamplingAllowed = false; + private boolean hintingEnabled = false; private RenderingHints renderingHints = null; /** @@ -176,6 +177,29 @@ public void setSubsamplingAllowed(boolean subsamplingAllowed) this.subsamplingAllowed = subsamplingAllowed; } + /** + * Value indicating whether the renderer grid-fits (hints) TrueType glyph outlines by running the + * font's bytecode instructions at the size the glyph is drawn at. Hinting is off by default. + * + * @return true if TrueType hinting is enabled, false otherwise. + */ + public boolean isHintingEnabled() + { + return hintingEnabled; + } + + /** + * Sets a value instructing the renderer whether to grid-fit (hint) TrueType glyph outlines by + * running the font's bytecode instructions at the size the glyph is drawn at. Only embedded + * TrueType outline fonts are affected; other fonts render as before. Hinting is off by default. + * + * @param hintingEnabled The new value indicating if TrueType hinting is enabled. + */ + public void setHintingEnabled(boolean hintingEnabled) + { + this.hintingEnabled = hintingEnabled; + } + /** * Get the rendering hints. * @@ -294,6 +318,7 @@ public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) // draw to graphics using PDFRender graphics2D.setBackground(Color.WHITE); renderer.setSubsamplingAllowed(subsamplingAllowed); + renderer.setHintingEnabled(hintingEnabled); renderer.setRenderingHints(renderingHints); renderer.renderPageToGraphics(pageIndex, graphics2D, (float) scale, (float) scale, RenderDestination.PRINT); diff --git a/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java b/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java index 27326cf9d7a..d34deac934e 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java @@ -62,6 +62,7 @@ public class PDFRenderer private AnnotationFilter annotationFilter = annotation -> true; private boolean subsamplingAllowed = false; + private boolean hintingEnabled = false; private RenderDestination defaultDestination; @@ -133,6 +134,29 @@ public void setSubsamplingAllowed(boolean subsamplingAllowed) this.subsamplingAllowed = subsamplingAllowed; } + /** + * Value indicating whether the renderer grid-fits (hints) TrueType glyph outlines by running the + * font's bytecode instructions at the size the glyph is drawn at. Hinting is off by default. + * + * @return true if TrueType hinting is enabled, false otherwise. + */ + public boolean isHintingEnabled() + { + return hintingEnabled; + } + + /** + * Sets a value instructing the renderer whether to grid-fit (hint) TrueType glyph outlines by + * running the font's bytecode instructions at the size the glyph is drawn at. Only embedded + * TrueType outline fonts are affected; other fonts render as before. Hinting is off by default. + * + * @param hintingEnabled The new value indicating if TrueType hinting is enabled. + */ + public void setHintingEnabled(boolean hintingEnabled) + { + this.hintingEnabled = hintingEnabled; + } + /** * @return the defaultDestination */ @@ -353,7 +377,7 @@ public BufferedImage renderImage(int pageIndex, float scale, ImageType imageType RenderingHints actualRenderingHints = renderingHints == null ? createDefaultRenderingHints(g) : renderingHints; PageDrawerParameters parameters - = new PageDrawerParameters(this, page, subsamplingAllowed, destination, + = new PageDrawerParameters(this, page, subsamplingAllowed, hintingEnabled, destination, actualRenderingHints, imageDownscalingOptimizationThreshold); PageDrawer drawer = createPageDrawer(parameters); drawer.drawPage(g, cropBox); @@ -469,7 +493,7 @@ public void renderPageToGraphics(int pageIndex, Graphics2D graphics, float scale RenderingHints actualRenderingHints = renderingHints == null ? createDefaultRenderingHints(graphics) : renderingHints; PageDrawerParameters parameters = - new PageDrawerParameters(this, page, subsamplingAllowed, destination, + new PageDrawerParameters(this, page, subsamplingAllowed, hintingEnabled, destination, actualRenderingHints, imageDownscalingOptimizationThreshold); PageDrawer drawer = createPageDrawer(parameters); drawer.drawPage(graphics, cropBox); diff --git a/pdfbox/src/main/java/org/apache/pdfbox/rendering/PageDrawer.java b/pdfbox/src/main/java/org/apache/pdfbox/rendering/PageDrawer.java index a99b83b115a..ed6c1a91d33 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/rendering/PageDrawer.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/rendering/PageDrawer.java @@ -62,7 +62,6 @@ import java.util.Map; import java.util.Set; -import org.apache.fontbox.ttf.TrueTypeFont; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import org.apache.pdfbox.contentstream.PDFGraphicsStreamEngine; @@ -137,6 +136,7 @@ public class PageDrawer extends PDFGraphicsStreamEngine private final PDFRenderer renderer; private final boolean subsamplingAllowed; + private final boolean hintingEnabled; // the graphics device to draw to, xform is the initial transform of the device (i.e. DPI) private Graphics2D graphics; @@ -195,6 +195,7 @@ public PageDrawer(PageDrawerParameters parameters) super(parameters.getPage()); this.renderer = parameters.getRenderer(); this.subsamplingAllowed = parameters.isSubsamplingAllowed(); + this.hintingEnabled = parameters.isHintingEnabled(); this.destination = parameters.getDestination(); this.renderingHints = parameters.getRenderingHints(); this.imageDownscalingOptimizationThreshold = @@ -511,12 +512,11 @@ protected void showFontGlyph(Matrix textRenderingMatrix, PDFont font, int code, glyphCaches.put(font, cache); } - // Grid-fitting is off by default; TrueTypeFont.SYSPROP_HINTING - // ("-Dorg.apache.fontbox.ttf.hinting=true") or TrueTypeFont.setHintingEnabled(true) turns it - // on. While it is off we never derive a ppem and take the plain, code-keyed cache path, so the - // feature costs nothing when disabled. + // Grid-fitting is off by default; PDFRenderer.setHintingEnabled(true) turns it on. While it + // is off we never derive a ppem and take the plain, code-keyed cache path, so the feature + // costs nothing when disabled. int ppem = 0; - if (TrueTypeFont.isHintingEnabled()) + if (hintingEnabled) { // hintingPpem expects the glyph-space-to-device transform, but 'at' only maps glyph space // to PDF user space (points) - the device scale lives in 'xform', which the Graphics2D diff --git a/pdfbox/src/main/java/org/apache/pdfbox/rendering/PageDrawerParameters.java b/pdfbox/src/main/java/org/apache/pdfbox/rendering/PageDrawerParameters.java index 3cf1c4edd83..9b0f66017ab 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/rendering/PageDrawerParameters.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/rendering/PageDrawerParameters.java @@ -33,6 +33,7 @@ public final class PageDrawerParameters private final PDFRenderer renderer; private final PDPage page; private final boolean subsamplingAllowed; + private final boolean hintingEnabled; private final RenderDestination destination; private final RenderingHints renderingHints; private final float imageDownscalingOptimizationThreshold; @@ -41,12 +42,13 @@ public final class PageDrawerParameters * Package-private constructor. */ PageDrawerParameters(PDFRenderer renderer, PDPage page, boolean subsamplingAllowed, - RenderDestination destination, RenderingHints renderingHints, - float imageDownscalingOptimizationThreshold) + boolean hintingEnabled, RenderDestination destination, + RenderingHints renderingHints, float imageDownscalingOptimizationThreshold) { this.renderer = renderer; this.page = page; this.subsamplingAllowed = subsamplingAllowed; + this.hintingEnabled = hintingEnabled; this.destination = destination; this.renderingHints = renderingHints; this.imageDownscalingOptimizationThreshold = imageDownscalingOptimizationThreshold; @@ -82,6 +84,16 @@ public boolean isSubsamplingAllowed() return subsamplingAllowed; } + /** + * Returns whether TrueType glyph outlines are grid-fitted (hinted). + * + * @return true if TrueType hinting is enabled + */ + public boolean isHintingEnabled() + { + return hintingEnabled; + } + /** * @return the destination */ diff --git a/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFontHintingTest.java b/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFontHintingTest.java index e0113f13aeb..26af3a2a7f6 100644 --- a/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFontHintingTest.java +++ b/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFontHintingTest.java @@ -28,41 +28,23 @@ import java.io.InputStream; import java.util.Arrays; -import org.apache.fontbox.ttf.TrueTypeFont; import org.apache.pdfbox.cos.COSDictionary; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.font.encoding.WinAnsiEncoding; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.parallel.Isolated; /** * Verifies the render-path hinting wiring at the font level: an embedded TrueType font returns a * grid-fitted normalized path that differs from the unhinted one, is in the same 1000/em space, and * respects the gasp gate. */ -@Isolated // TrueTypeFont hinting is a global switch; other classes must not render while it is on class PDTrueTypeFontHintingTest { - // hinting is off by default, so these tests have to turn the feature on first - @BeforeEach - void enableHinting() - { - TrueTypeFont.setHintingEnabled(true); - } - - @AfterEach - void restoreHinting() - { - TrueTypeFont.setHintingEnabled(false); - } - // PDTrueTypeFont.load() consumes and closes the stream, so each caller needs a fresh one private static InputStream fontStream() { - return TrueTypeFont.class.getResourceAsStream( + return PDTrueTypeFontHintingTest.class.getResourceAsStream( "/org/apache/pdfbox/resources/ttf/LiberationSans-Regular.ttf"); } diff --git a/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDType0FontHintingTest.java b/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDType0FontHintingTest.java index 5b34500d57f..fa38db9d32e 100644 --- a/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDType0FontHintingTest.java +++ b/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/PDType0FontHintingTest.java @@ -39,31 +39,15 @@ import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.io.RandomAccessReadBufferedFile; import org.apache.pdfbox.pdmodel.PDDocument; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.parallel.Isolated; /** * The CID half of the render-path hinting wiring. {@link PDCIDFontType2#getHintedNormalizedPath} is a * near-copy of the {@link PDTrueTypeFont} one but had no test of its own, and {@link PDType0Font} * forwards to it only when the descendant really is a CIDFontType2. */ -@Isolated // TrueTypeFont hinting is a global switch; other classes must not render while it is on class PDType0FontHintingTest { - @BeforeEach - void enableHinting() - { - TrueTypeFont.setHintingEnabled(true); - } - - @AfterEach - void restoreHinting() - { - TrueTypeFont.setHintingEnabled(false); - } - /** * Embeds the font whole (no subsetting) so the encoding is Identity and a character code is its * own glyph id, which keeps the test about hinting rather than about CID mapping. diff --git a/pdfbox/src/test/java/org/apache/pdfbox/rendering/RenderHintingIntegrationTest.java b/pdfbox/src/test/java/org/apache/pdfbox/rendering/RenderHintingIntegrationTest.java index e2431b0cf3d..5b706149efe 100644 --- a/pdfbox/src/test/java/org/apache/pdfbox/rendering/RenderHintingIntegrationTest.java +++ b/pdfbox/src/test/java/org/apache/pdfbox/rendering/RenderHintingIntegrationTest.java @@ -23,22 +23,18 @@ import java.io.IOException; import java.io.InputStream; -import org.apache.fontbox.ttf.TrueTypeFont; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.font.PDTrueTypeFont; import org.apache.pdfbox.pdmodel.font.encoding.WinAnsiEncoding; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.parallel.Isolated; /** * Proves that hinting is actually wired into the render path: when hinting is enabled, a page of * embedded TrueType text rasterizes to a different image than with it disabled. */ -@Isolated // TrueTypeFont hinting is a global switch; other classes must not render while it is on class RenderHintingIntegrationTest { // PDTrueTypeFont.load() consumes and closes the stream, so each caller needs a fresh one @@ -48,19 +44,12 @@ private static InputStream fontStream() "/org/apache/pdfbox/resources/ttf/LiberationSans-Regular.ttf"); } - @AfterEach - void restoreHinting() - { - TrueTypeFont.setHintingEnabled(false); - } - @Test void testHintingChangesRenderedPixels() throws IOException { byte[] pdf = buildPdf(); - BufferedImage off = render(pdf); - TrueTypeFont.setHintingEnabled(true); - BufferedImage on = render(pdf); + BufferedImage off = render(pdf, false); + BufferedImage on = render(pdf, true); assertEquals(off.getWidth(), on.getWidth()); assertEquals(off.getHeight(), on.getHeight()); @@ -73,7 +62,7 @@ void testHintingChangesRenderedPixels() throws IOException void testDisabledHintingIsDeterministic() throws IOException { byte[] pdf = buildPdf(); - assertEquals(0, countDifferences(render(pdf), render(pdf))); + assertEquals(0, countDifferences(render(pdf, false), render(pdf, false))); } private static byte[] buildPdf() throws IOException @@ -97,11 +86,13 @@ private static byte[] buildPdf() throws IOException } } - private static BufferedImage render(byte[] pdf) throws IOException + private static BufferedImage render(byte[] pdf, boolean hinting) throws IOException { try (PDDocument doc = org.apache.pdfbox.Loader.loadPDF(pdf)) { - return new PDFRenderer(doc).renderImageWithDPI(0, 96); + PDFRenderer renderer = new PDFRenderer(doc); + renderer.setHintingEnabled(hinting); + return renderer.renderImageWithDPI(0, 96); } } From 7c689668c221425cc271cad04bfb385837246cef Mon Sep 17 00:00:00 2001 From: Patrick Corless Date: Thu, 17 Sep 2026 20:48:12 -0600 Subject: [PATCH 8/8] PDFBOX-3293 hookup hinting in the debugger --- .../pdfbox/debugger/pagepane/PagePane.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/pagepane/PagePane.java b/debugger/src/main/java/org/apache/pdfbox/debugger/pagepane/PagePane.java index ddcbb5459b0..831a4c60dd9 100644 --- a/debugger/src/main/java/org/apache/pdfbox/debugger/pagepane/PagePane.java +++ b/debugger/src/main/java/org/apache/pdfbox/debugger/pagepane/PagePane.java @@ -95,7 +95,7 @@ /** * Display the page number and a page rendering. - * + * * @author Tilman Hausherr * @author John Hewson */ @@ -298,7 +298,7 @@ private void initUI() pageLabel.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 20)); pageLabel.setBorder(BorderFactory.createEmptyBorder(5, 0, 10, 0)); panel.add(pageLabel); - + label = new JLabel(); label.addMouseMotionListener(this); label.addMouseListener(this); @@ -395,7 +395,7 @@ public void ancestorAdded(AncestorEvent ancestorEvent) { zoomMenu.addMenuListeners(this); zoomMenu.setEnableMenu(true); - + rotationMenu = RotationMenu.getInstance(); rotationMenu.addMenuListeners(this); rotationMenu.setEnableMenu(true); @@ -412,7 +412,7 @@ public void ancestorAdded(AncestorEvent ancestorEvent) JMenu menuInstance = viewMenu.getMenu(); int itemCount = menuInstance.getItemCount(); - + for (int i = 0; i< itemCount; i++) { JMenuItem item = menuInstance.getItem(i); @@ -435,7 +435,7 @@ public void ancestorRemoved(AncestorEvent ancestorEvent) JMenu menuInstance = viewMenu.getMenu(); int itemCount = menuInstance.getItemCount(); - + for (int i = 0; i< itemCount; i++) { JMenuItem item = menuInstance.getItem(i); @@ -705,7 +705,7 @@ protected BufferedImage doInBackground() throws IOException PDFRenderer renderer = new PDFRenderer(document); renderer.setSubsamplingAllowed(ViewMenu.isAllowSubsampling()); - //renderer.setHintingEnabled(ViewMenu.isHintingEnabled()) + renderer.setHintingEnabled(ViewMenu.isHintingEnabled()); long t0 = System.nanoTime(); BufferedImage image = renderer.renderImage(pageIndex, scale, ImageTypeMenu.getImageType(), RenderDestinationMenu.getRenderDestination()); @@ -716,13 +716,13 @@ protected BufferedImage doInBackground() throws IOException statuslabel.setText(labelText); // debug overlays - DebugTextOverlay debugText = new DebugTextOverlay(document, pageIndex, scale, + DebugTextOverlay debugText = new DebugTextOverlay(document, pageIndex, scale, showTextStripper, showTextStripperBeads, showFontBBox, ViewMenu.isShowGlyphBounds()); Graphics2D g = image.createGraphics(); debugText.renderTo(g); g.dispose(); - + return ImageUtil.getRotatedImage(image, rotation); } @@ -733,12 +733,12 @@ protected void done() { BufferedImage image = get(); - // We cannot use "label.setIcon(new ImageIcon(get()))" here - // because of blurry upscaling in JDK9. Instead, the label is now created with + // We cannot use "label.setIcon(new ImageIcon(get()))" here + // because of blurry upscaling in JDK9. Instead, the label is now created with // a smaller size than the image to compensate that the // image is scaled up with some screen configurations (e.g. 125% on windows). // See PDFBOX-3665 for more sample code and discussion. - label.setSize((int) Math.ceil(image.getWidth() / defaultTransform.getScaleX()), + label.setSize((int) Math.ceil(image.getWidth() / defaultTransform.getScaleX()), (int) Math.ceil(image.getHeight() / defaultTransform.getScaleY())); label.setIcon(new HighResolutionImageIcon(image, label.getWidth(), label.getHeight())); label.setText(null);