You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xmlgraphics.apache.org by ss...@apache.org on 2025/07/21 07:12:16 UTC

(xmlgraphics-fop-pdf-images) branch main updated: FOP-3265: Remap glyphs for gid conflict

This is an automated email from the ASF dual-hosted git repository.

ssteiner pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/xmlgraphics-fop-pdf-images.git


The following commit(s) were added to refs/heads/main by this push:
     new c312e15  FOP-3265: Remap glyphs for gid conflict
c312e15 is described below

commit c312e15ce7a19f710e2ec13e8ace6d80ab33e7b7
Author: Simon Steiner <ss...@apache.org>
AuthorDate: Mon Jul 21 08:12:04 2025 +0100

    FOP-3265: Remap glyphs for gid conflict
---
 .../render/pdf/pdfbox/FOPPDFSingleByteFont.java    |  11 +--
 .../apache/fop/render/pdf/pdfbox/MergeTTFonts.java |  15 ++--
 .../pdfbox/FOPPDFSingleMultiByteFontTestCase.java  |  92 ++++++++++++---------
 .../apache/fop/render/pdf/pdfbox/ttsubset18.pdf    | Bin 0 -> 20790 bytes
 .../apache/fop/render/pdf/pdfbox/ttsubset19.pdf    | Bin 0 -> 15351 bytes
 .../apache/fop/render/pdf/pdfbox/ttsubset20.pdf    | Bin 0 -> 19957 bytes
 6 files changed, 69 insertions(+), 49 deletions(-)

diff --git a/src/java/org/apache/fop/render/pdf/pdfbox/FOPPDFSingleByteFont.java b/src/java/org/apache/fop/render/pdf/pdfbox/FOPPDFSingleByteFont.java
index b1e8946..60812c9 100644
--- a/src/java/org/apache/fop/render/pdf/pdfbox/FOPPDFSingleByteFont.java
+++ b/src/java/org/apache/fop/render/pdf/pdfbox/FOPPDFSingleByteFont.java
@@ -234,8 +234,7 @@ public class FOPPDFSingleByteFont extends SingleByteFont implements FOPPDFFont {
         int gid = entry.getValue();
         GlyphData glyphData = ttfont.getGlyph().getGlyph(gid);
         if (params.isRemapSingleByteFontEnabled() && gid != 0 && hasGlyph(glyphData) && !charMapGlobal.isEmpty()
-                && tempCmap.glyphIdToCharacterCode.containsValue(gid)
-                && !tempCmap.glyphIdToCharacterCode.containsKey(charCode)) {
+                && tempCmap.needsRemap(gid, charCode)) {
             //Move glyphs to different gid when 2 fonts use same gid with different char code
             if (oldToNewGIMapPerFont.containsKey(gid)) {
                 gid = oldToNewGIMapPerFont.get(gid);
@@ -246,9 +245,11 @@ public class FOPPDFSingleByteFont extends SingleByteFont implements FOPPDFFont {
                         || hasGlyph(ttfont.getGlyph().getGlyph(newGid))) {
                     newGid++;
                 }
-                oldToNewGIMap.put(gid, newGid);
-                oldToNewGIMapPerFont.put(gid, newGid);
-                gid = newGid;
+                if (newGid < ttfont.getNumberOfGlyphs()) {
+                    oldToNewGIMap.put(gid, newGid);
+                    oldToNewGIMapPerFont.put(gid, newGid);
+                    gid = newGid;
+                }
             }
         }
         if (gid != 0 && !tempCmap.glyphIdToCharacterCode.containsKey(charCode)
diff --git a/src/java/org/apache/fop/render/pdf/pdfbox/MergeTTFonts.java b/src/java/org/apache/fop/render/pdf/pdfbox/MergeTTFonts.java
index 95624bf..c2aa404 100644
--- a/src/java/org/apache/fop/render/pdf/pdfbox/MergeTTFonts.java
+++ b/src/java/org/apache/fop/render/pdf/pdfbox/MergeTTFonts.java
@@ -92,20 +92,20 @@ public class MergeTTFonts extends TTFSubSetFile implements MergeFonts {
                         (int)entry.getOffset() + glyphOffset,
                         glyphLength);
 
-                Glyph g = new Glyph(glyphData, mtxTab[origGlyphIndex],
+                Glyph glyph = new Glyph(glyphData, mtxTab[origGlyphIndex],
                         composedGlyphs.contains(origGlyphIndex),
                         compositeGlyphs.contains(origGlyphIndex), origGlyphIndex);
                 if (!cid && (origIndexesLen == 0 || (glyphLength > 0 && i > 0))) {
                     if (added.containsKey(i)) {
                         Glyph existing = added.get(i);
-                        if (existing.data.length == 0) {
-                            added.put(i, g);
+                        if (existing.data.length == 0 || existing.origGlyphIndex == 0) {
+                            added.put(i, glyph);
                         }
                     } else {
-                        added.put(i, g);
+                        added.put(i, glyph);
                     }
                 } else if (cid) {
-                    added.put(i + origIndexesLen, g);
+                    added.put(i + origIndexesLen, glyph);
                 }
             }
             if (!cid) {
@@ -586,5 +586,10 @@ public class MergeTTFonts extends TTFSubSetFile implements MergeFonts {
             }
             return glyphIdToCharacterCode;
         }
+
+        public boolean needsRemap(int gid, int charCode) {
+            return (glyphIdToCharacterCode.containsKey(charCode) && glyphIdToCharacterCode.get(charCode) != gid)
+                    || (glyphIdToCharacterCode.containsValue(gid) && !glyphIdToCharacterCode.containsKey(charCode));
+        }
     }
 }
diff --git a/test/java/org/apache/fop/render/pdf/pdfbox/FOPPDFSingleMultiByteFontTestCase.java b/test/java/org/apache/fop/render/pdf/pdfbox/FOPPDFSingleMultiByteFontTestCase.java
index b00636c..c9b6b47 100644
--- a/test/java/org/apache/fop/render/pdf/pdfbox/FOPPDFSingleMultiByteFontTestCase.java
+++ b/test/java/org/apache/fop/render/pdf/pdfbox/FOPPDFSingleMultiByteFontTestCase.java
@@ -72,6 +72,9 @@ import org.apache.fop.pdf.PDFMergeFontsParams;
 public class FOPPDFSingleMultiByteFontTestCase {
     private static final String TTSubset16 = "ttsubset16.pdf";
     private static final String TTSubset17 = "ttsubset17.pdf";
+    private static final String TTSubset18 = "ttsubset18.pdf";
+    private static final String TTSubset19 = "ttsubset19.pdf";
+    private static final String TTSubset20 = "ttsubset20.pdf";
     private PDFMergeFontsParams params = new PDFMergeFontsParams(true);
 
     private COSDictionary getFont(PDDocument doc, String internalname) {
@@ -332,15 +335,12 @@ public class FOPPDFSingleMultiByteFontTestCase {
     public void testNoEOFOnValidation() throws IOException {
         PDDocument doc = PDFBoxAdapterTestCase.load(FontMergeTestCase.TTSubset1);
         PDDocument doc2 = PDFBoxAdapterTestCase.load(FontMergeTestCase.TTSubset5);
-
         PDFBoxEventProducer eventProducer = mock(PDFBoxEventProducer.class);
         FOPPDFSingleByteFont sbFont = new FOPPDFSingleByteFont(getFont(doc, "R11"), "QOYQBZ+TimesNewRomanPSMT",
                 getEventBroadcaster(eventProducer), params);
         sbFont.addFont(getFont(doc2, "F1"));
-
         doc.close();
         doc2.close();
-
         sbFont.getInputStream();
         verifyNoEvents(eventProducer);
     }
@@ -365,45 +365,54 @@ public class FOPPDFSingleMultiByteFontTestCase {
 
     @Test
     public void testValidationSingleByteFont() throws IOException {
-        validateSingleByteFont(FontMergeTestCase.TTSubset3, FontMergeTestCase.TTSubset5,
-                "QOYQBZ+TimesNewRomanPSMT", "F1", true);
-        validateSingleByteFont(FontMergeTestCase.TTSubset14, FontMergeTestCase.TTSubset15,
-                "Montserrat-Regular", "TT1", true);
+        validateSingleByteFont("QOYQBZ+TimesNewRomanPSMT", "F1", true,
+                FontMergeTestCase.TTSubset3, FontMergeTestCase.TTSubset5);
+        validateSingleByteFont("Montserrat-Regular", "TT1", true,
+                FontMergeTestCase.TTSubset14, FontMergeTestCase.TTSubset15);
     }
 
     @Test
     public void testTTSubsetRemapGid() throws IOException {
-        InputStream is = validateSingleByteFont(TTSubset16, TTSubset17,
-                "Montserrat-Regular", "TT0", true);
-        TrueTypeFont mergedTTF = new TTFParser().parse(new RandomAccessReadBuffer(is));
+        TrueTypeFont mergedTTF = validateSingleByteFont("Montserrat-Regular", "TT0", true, TTSubset16, TTSubset17);
         List<Integer> gids = new ArrayList<>();
         for (int i = 0; i < mergedTTF.getNumberOfGlyphs(); i++) {
             if (mergedTTF.getGlyph().getGlyph(i).getNumberOfContours() != 0) {
                 gids.add(i);
             }
         }
-        Assert.assertEquals(124, gids.size());
-        Assert.assertTrue(gids.contains(242));
+        Assert.assertEquals(165, gids.size());
+        for (int gid = 242; gid < 260; gid++) {
+            Assert.assertTrue(String.valueOf(gid), gids.contains(gid));
+        }
+        for (int gid = 270; gid < 294; gid++) {
+            Assert.assertTrue(String.valueOf(gid), gids.contains(gid));
+        }
         Assert.assertEquals(3, mergedTTF.getGlyph().getGlyph(167).getNumberOfContours());
-        compareGlyphsUsingTTSubset16And17(mergedTTF);
+        Assert.assertEquals(Arrays.asList(8211, 37, 74), compareGlyphs(mergedTTF, TTSubset16, TTSubset17));
     }
 
-    private void compareGlyphsUsingTTSubset16And17(TrueTypeFont mergedTTF) throws IOException {
-        Map<Integer, GlyphData> glyphsFromInput1 = getGlyphs(TTSubset16, "TT0");
-        Assert.assertEquals(74, glyphsFromInput1.size());
-        for (Map.Entry<Integer, GlyphData> entry : glyphsFromInput1.entrySet()) {
-            compareGlyph(mergedTTF, entry.getKey(), entry.getValue());
-        }
-        Map<Integer, GlyphData> glyphsFromInput2 = getGlyphs(TTSubset17, "TT0");
-        Assert.assertEquals(54, glyphsFromInput2.size());
+    @Test
+    public void testTTSubsetRemapGid2() throws IOException {
+        TrueTypeFont mergedTTF = validateSingleByteFont("Montserrat", "TT0", true, TTSubset18, TTSubset19, TTSubset20);
+        Assert.assertEquals(1, mergedTTF.getGlyph().getGlyph(1608).getNumberOfContours());
+        Assert.assertEquals(Arrays.asList(8212, 55, 85), compareGlyphs(mergedTTF, TTSubset18, TTSubset19, TTSubset20));
+    }
+
+    private List<Integer> compareGlyphs(TrueTypeFont mergedTTF, String... files) throws IOException {
+        List<Integer> lastGlyphsChecked = new ArrayList<>();
         List<Integer> glyphsChecked = new ArrayList<>();
-        for (Map.Entry<Integer, GlyphData> entry : glyphsFromInput2.entrySet()) {
-            if (!glyphsFromInput1.containsKey(entry.getKey())) {
-                compareGlyph(mergedTTF, entry.getKey(), entry.getValue());
-                glyphsChecked.add(entry.getKey());
+        for (String file : files) {
+            lastGlyphsChecked.clear();
+            Map<Integer, GlyphData> glyphsFromInput = getGlyphs(file, "TT0");
+            for (Map.Entry<Integer, GlyphData> entry : glyphsFromInput.entrySet()) {
+                if (!glyphsChecked.contains(entry.getKey())) {
+                    compareGlyph(mergedTTF, entry.getKey(), entry.getValue());
+                    glyphsChecked.add(entry.getKey());
+                    lastGlyphsChecked.add(entry.getKey());
+                }
             }
         }
-        Assert.assertEquals(Arrays.asList(8211, 37, 74), glyphsChecked);
+        return lastGlyphsChecked;
     }
 
     private void compareGlyph(TrueTypeFont mergedTTF, int charCode, GlyphData glyphFromInput) throws IOException {
@@ -432,9 +441,7 @@ public class FOPPDFSingleMultiByteFontTestCase {
 
     @Test
     public void testTTSubsetNoRemapGid() throws IOException {
-        InputStream is = validateSingleByteFont(TTSubset16, TTSubset17,
-                "Montserrat", "TT0", false);
-        TrueTypeFont ttf = new TTFParser().parse(new RandomAccessReadBuffer(is));
+        TrueTypeFont ttf = validateSingleByteFont("Montserrat", "TT0", false, TTSubset16, TTSubset17);
         List<Integer> gids = new ArrayList<>();
         for (int i = 0; i < ttf.getNumberOfGlyphs(); i++) {
             if (ttf.getGlyph().getGlyph(i).getNumberOfContours() != 0) {
@@ -445,16 +452,23 @@ public class FOPPDFSingleMultiByteFontTestCase {
         Assert.assertFalse(gids.contains(242));
     }
 
-    private InputStream validateSingleByteFont(String firstFile, String secondFile, String fontName,
-                                               String internalName, boolean remapFont) throws IOException {
-        PDDocument doc = PDFBoxAdapterTestCase.load(firstFile);
-        PDDocument doc2 = PDFBoxAdapterTestCase.load(secondFile);
-        FOPPDFSingleByteFont sbFont = new FOPPDFSingleByteFont(getFont(doc, internalName), fontName,
-                new DefaultEventBroadcaster(), new PDFMergeFontsParams(remapFont));
-        sbFont.addFont(getFont(doc2, internalName));
-        doc.close();
-        doc2.close();
-        return sbFont.getInputStream();
+    private TrueTypeFont validateSingleByteFont(String fontName, String internalName, boolean remap, String... files)
+        throws IOException {
+        FOPPDFSingleByteFont sbFont = null;
+        PDFBoxEventProducer eventProducer = mock(PDFBoxEventProducer.class);
+        for (String file : files) {
+            PDDocument doc = PDFBoxAdapterTestCase.load(file);
+            if (sbFont == null) {
+                sbFont = new FOPPDFSingleByteFont(getFont(doc, internalName), fontName,
+                        getEventBroadcaster(eventProducer), new PDFMergeFontsParams(remap));
+            } else {
+                sbFont.addFont(getFont(doc, internalName));
+            }
+            doc.close();
+        }
+        InputStream is = sbFont.getInputStream();
+        verifyNoEvents(eventProducer);
+        return new TTFParser().parse(new RandomAccessReadBuffer(is));
     }
 
     private EventBroadcaster getEventBroadcaster(PDFBoxEventProducer eventProducer) {
diff --git a/test/resources/org/apache/fop/render/pdf/pdfbox/ttsubset18.pdf b/test/resources/org/apache/fop/render/pdf/pdfbox/ttsubset18.pdf
new file mode 100755
index 0000000..d98ff71
Binary files /dev/null and b/test/resources/org/apache/fop/render/pdf/pdfbox/ttsubset18.pdf differ
diff --git a/test/resources/org/apache/fop/render/pdf/pdfbox/ttsubset19.pdf b/test/resources/org/apache/fop/render/pdf/pdfbox/ttsubset19.pdf
new file mode 100755
index 0000000..dcbb8e1
Binary files /dev/null and b/test/resources/org/apache/fop/render/pdf/pdfbox/ttsubset19.pdf differ
diff --git a/test/resources/org/apache/fop/render/pdf/pdfbox/ttsubset20.pdf b/test/resources/org/apache/fop/render/pdf/pdfbox/ttsubset20.pdf
new file mode 100755
index 0000000..0a49b55
Binary files /dev/null and b/test/resources/org/apache/fop/render/pdf/pdfbox/ttsubset20.pdf differ


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: commits-help@xmlgraphics.apache.org