You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ti...@apache.org on 2024/04/30 08:53:21 UTC

svn commit: r1917432 - in /pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf: ./ gsub/ table/gsub/

Author: tilman
Date: Tue Apr 30 08:53:21 2024
New Revision: 1917432

URL: http://svn.apache.org/viewvc?rev=1917432&view=rev
Log:
PDFBOX-5808: add support for GSUB lookup type 3, by Fabrice Calafat

Added:
    pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/table/gsub/AlternateSetTable.java   (with props)
    pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/table/gsub/LookupTypeAlternateSubstitutionFormat1.java   (with props)
Modified:
    pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphSubstitutionTable.java
    pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java
    pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/gsub/GlyphSubstitutionDataExtractor.java

Modified: pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphSubstitutionTable.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphSubstitutionTable.java?rev=1917432&r1=1917431&r2=1917432&view=diff
==============================================================================
--- pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphSubstitutionTable.java (original)
+++ pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphSubstitutionTable.java Tue Apr 30 08:53:21 2024
@@ -27,7 +27,6 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.Predicate;
 import java.util.regex.Pattern;
 
 import org.apache.commons.logging.Log;
@@ -46,8 +45,10 @@ import org.apache.fontbox.ttf.table.comm
 import org.apache.fontbox.ttf.table.common.LookupTable;
 import org.apache.fontbox.ttf.table.common.RangeRecord;
 import org.apache.fontbox.ttf.table.common.ScriptTable;
+import org.apache.fontbox.ttf.table.gsub.AlternateSetTable;
 import org.apache.fontbox.ttf.table.gsub.LigatureSetTable;
 import org.apache.fontbox.ttf.table.gsub.LigatureTable;
+import org.apache.fontbox.ttf.table.gsub.LookupTypeAlternateSubstitutionFormat1;
 import org.apache.fontbox.ttf.table.gsub.LookupTypeLigatureSubstitutionSubstFormat1;
 import org.apache.fontbox.ttf.table.gsub.LookupTypeMultipleSubstitutionFormat1;
 import org.apache.fontbox.ttf.table.gsub.LookupTypeSingleSubstFormat1;
@@ -289,6 +290,10 @@ public class GlyphSubstitutionTable exte
                 // Multiple Substitution Subtable
                 // https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#lookuptype-2-multiple-substitution-subtable
                 return readMultipleSubstitutionSubtable(data, offset);
+            case 3:
+                // Alternate Substitution Subtable
+                // https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#lookuptype-3-alternate-substitution-subtable
+                return readAlternateSubstitutionSubtable(data, offset);
             case 4:
                 // Ligature Substitution Subtable
                 // https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#LS
@@ -345,6 +350,7 @@ public class GlyphSubstitutionTable exte
         {
         case 1:
         case 2:
+        case 3:
         case 4:
             for (int i = 0; i < subTableCount; i++)
             {
@@ -462,6 +468,49 @@ public class GlyphSubstitutionTable exte
         return new LookupTypeMultipleSubstitutionFormat1(substFormat, coverageTable, sequenceTables);
     }
 
+    private LookupSubTable readAlternateSubstitutionSubtable(TTFDataStream data, long offset) throws IOException
+    {
+        data.seek(offset);
+        int substFormat = data.readUnsignedShort();
+
+        if (substFormat != 1)
+        {
+            throw new IOException(
+                    "The expected SubstFormat for AlternateSubstitutionTable is 1");
+        }
+
+        int coverage = data.readUnsignedShort();
+        int altSetCount = data.readUnsignedShort();
+
+        int[] alternateOffsets = new int[altSetCount];
+
+        for (int i = 0; i < altSetCount; i++)
+        {
+            alternateOffsets[i] = data.readUnsignedShort();
+        }
+
+        CoverageTable coverageTable = readCoverageTable(data, offset + coverage);
+
+        if (altSetCount != coverageTable.getSize())
+        {
+            throw new IOException(
+                    "According to the OpenTypeFont specifications, the coverage count should be equal to the no. of AlternateSetTable");
+        }
+
+        AlternateSetTable[] alternateSetTables = new AlternateSetTable[altSetCount];
+
+        for (int i = 0; i < altSetCount; i++)
+        {
+            data.seek(offset + alternateOffsets[i]);
+            int glyphCount = data.readUnsignedShort();
+            int[] alternateGlyphIDs = data.readUnsignedShortArray(glyphCount);
+            alternateSetTables[i] = new AlternateSetTable(glyphCount, alternateGlyphIDs);
+        }
+
+        return new LookupTypeAlternateSubstitutionFormat1(substFormat, coverageTable,
+                alternateSetTables);
+    }
+
     private LookupSubTable readLigatureSubstitutionSubtable(TTFDataStream data, long offset)
             throws IOException
     {

Modified: pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java?rev=1917432&r1=1917431&r2=1917432&view=diff
==============================================================================
--- pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java (original)
+++ pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java Tue Apr 30 08:53:21 2024
@@ -47,6 +47,7 @@ public class TrueTypeFont implements Fon
     private float version;
     private int numberOfGlyphs = -1;
     private int unitsPerEm = -1;
+    private boolean enableGsub = true;
     protected final Map<String,TTFTable> tables = new HashMap<>();
     private final TTFDataStream data;
     private volatile Map<String, Integer> postScriptNames;
@@ -74,7 +75,7 @@ public class TrueTypeFont implements Fon
     /**
      * @return Returns the version.
      */
-    public float getVersion() 
+    public float getVersion()
     {
         return version;
     }
@@ -87,7 +88,24 @@ public class TrueTypeFont implements Fon
     {
         version = versionValue;
     }
-    
+
+    /**
+     * @return Returns true if the GSUB table can be used for this font
+     */
+    public boolean isEnableGsub()
+    {
+        return enableGsub;
+    }
+
+    /**
+     * Enable or disable the GSUB table for this font.
+     * GSUB table is enabled by default.
+     */
+    public void setEnableGsub(boolean enableGsub)
+    {
+        this.enableGsub = enableGsub;
+    }
+
     /**
      * Add a table definition. Package-private, used by TTFParser only.
      * 
@@ -638,13 +656,17 @@ public class TrueTypeFont implements Fon
     /**
      * Returns the GSubData of the GlyphSubstitutionTable if present.
      * 
-     * @return the GSubData of the GlyphSubstitutionTable or {@link GsubData#NO_DATA_FOUND} if either no GSUB data is
-     * available or its scripts are not supported
-     * 
+     * @return the GSubData of the GlyphSubstitutionTable or {@link GsubData#NO_DATA_FOUND} if no GSUB data is
+     * available, its scripts are not supported or it was disabled for that font
      * @throws IOException if the font data could not be read
      */
     public GsubData getGsubData() throws IOException
     {
+        if (!enableGsub)
+        {
+            return GsubData.NO_DATA_FOUND;
+        }
+
         GlyphSubstitutionTable table = getGsub();
         if (table == null)
         {

Modified: pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/gsub/GlyphSubstitutionDataExtractor.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/gsub/GlyphSubstitutionDataExtractor.java?rev=1917432&r1=1917431&r2=1917432&view=diff
==============================================================================
--- pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/gsub/GlyphSubstitutionDataExtractor.java (original)
+++ pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/gsub/GlyphSubstitutionDataExtractor.java Tue Apr 30 08:53:21 2024
@@ -36,8 +36,10 @@ import org.apache.fontbox.ttf.table.comm
 import org.apache.fontbox.ttf.table.common.LookupSubTable;
 import org.apache.fontbox.ttf.table.common.LookupTable;
 import org.apache.fontbox.ttf.table.common.ScriptTable;
+import org.apache.fontbox.ttf.table.gsub.AlternateSetTable;
 import org.apache.fontbox.ttf.table.gsub.LigatureSetTable;
 import org.apache.fontbox.ttf.table.gsub.LigatureTable;
+import org.apache.fontbox.ttf.table.gsub.LookupTypeAlternateSubstitutionFormat1;
 import org.apache.fontbox.ttf.table.gsub.LookupTypeLigatureSubstitutionSubstFormat1;
 import org.apache.fontbox.ttf.table.gsub.LookupTypeMultipleSubstitutionFormat1;
 import org.apache.fontbox.ttf.table.gsub.LookupTypeSingleSubstFormat1;
@@ -176,6 +178,11 @@ public class GlyphSubstitutionDataExtrac
                 extractDataFromLigatureSubstitutionSubstFormat1Table(glyphSubstitutionMap,
                         (LookupTypeLigatureSubstitutionSubstFormat1) lookupSubTable);
             }
+            else if (lookupSubTable instanceof LookupTypeAlternateSubstitutionFormat1)
+            {
+                extractDataFromAlternateSubstitutionSubstFormat1Table(glyphSubstitutionMap,
+                        (LookupTypeAlternateSubstitutionFormat1) lookupSubTable);
+            }
             else if (lookupSubTable instanceof LookupTypeSingleSubstFormat1)
             {
                 extractDataFromSingleSubstTableFormat1Table(glyphSubstitutionMap,
@@ -278,6 +285,47 @@ public class GlyphSubstitutionDataExtrac
         }
 
     }
+
+    /**
+     * Extracts data from the AlternateSubstitutionFormat1 (lookuptype) 3 table and puts it in the glyphSubstitutionMap
+     * This is added for Zola usage
+     *
+     * @param glyphSubstitutionMap         the map to store the substitution data
+     * @param alternateSubstitutionFormat1 the alternate substitution format 1 table
+     */
+    private void extractDataFromAlternateSubstitutionSubstFormat1Table(
+            Map<List<Integer>, Integer> glyphSubstitutionMap,
+            LookupTypeAlternateSubstitutionFormat1 alternateSubstitutionFormat1)
+    {
+
+        CoverageTable coverageTable = alternateSubstitutionFormat1.getCoverageTable();
+
+        if (coverageTable.getSize() != alternateSubstitutionFormat1.getAlternateSetTables().length)
+        {
+            LOG.warn("The coverage table size (" + coverageTable.getSize() +
+                    ") should be the same as the count of the atlternate set tables (" +
+                    alternateSubstitutionFormat1.getAlternateSetTables().length + ")");
+            return;
+        }
+
+        for (int i = 0; i < coverageTable.getSize(); i++)
+        {
+            int coverageGlyphId = coverageTable.getGlyphId(i);
+            AlternateSetTable sequenceTable = alternateSubstitutionFormat1.getAlternateSetTables()[i];
+
+            // Loop through the substitute glyphs and pick the first one that is not the same as the coverage glyph
+            for (int alternateGlyphId : sequenceTable.getAlternateGlyphIDs())
+            {
+                if (alternateGlyphId != coverageGlyphId)
+                {
+                    putNewSubstitutionEntry(glyphSubstitutionMap, alternateGlyphId,
+                            Collections.singletonList(coverageGlyphId));
+                    break;
+                }
+            }
+        }
+
+    }
 
     private void extractDataFromLigatureTable(Map<List<Integer>, Integer> glyphSubstitutionMap,
             LigatureTable ligatureTable)

Added: pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/table/gsub/AlternateSetTable.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/table/gsub/AlternateSetTable.java?rev=1917432&view=auto
==============================================================================
--- pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/table/gsub/AlternateSetTable.java (added)
+++ pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/table/gsub/AlternateSetTable.java Tue Apr 30 08:53:21 2024
@@ -0,0 +1,51 @@
+/*
+ * 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.table.gsub;
+
+import java.util.Arrays;
+
+/**
+ * LookupType 3: Alternate Substitution Subtable
+ * as described in OpenType spec: <a href="https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#31-alternate-substitution-format-1">...</a>
+ */
+public class AlternateSetTable
+{
+    private final int glyphCount;
+    private final int[] alternateGlyphIDs;
+
+    public AlternateSetTable(int glyphCount, int[] alternateGlyphIDs)
+    {
+        this.glyphCount = glyphCount;
+        this.alternateGlyphIDs = alternateGlyphIDs;
+    }
+
+    public int getGlyphCount()
+    {
+        return glyphCount;
+    }
+
+    public int[] getAlternateGlyphIDs()
+    {
+        return alternateGlyphIDs;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "AlternateSetTable{" + "glyphCount=" + glyphCount + ", alternateGlyphIDs=" + Arrays.toString(alternateGlyphIDs) + '}';
+    }
+}

Propchange: pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/table/gsub/AlternateSetTable.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/table/gsub/LookupTypeAlternateSubstitutionFormat1.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/table/gsub/LookupTypeAlternateSubstitutionFormat1.java?rev=1917432&view=auto
==============================================================================
--- pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/table/gsub/LookupTypeAlternateSubstitutionFormat1.java (added)
+++ pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/table/gsub/LookupTypeAlternateSubstitutionFormat1.java Tue Apr 30 08:53:21 2024
@@ -0,0 +1,43 @@
+/*
+ * 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.table.gsub;
+
+import org.apache.fontbox.ttf.table.common.CoverageTable;
+import org.apache.fontbox.ttf.table.common.LookupSubTable;
+
+/**
+ * Lookup Type 3: Alternate Substitution Subtable
+ * as described in OpenType spec: <a href="https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#31-alternate-substitution-format-1">...</a>
+ */
+public class LookupTypeAlternateSubstitutionFormat1 extends LookupSubTable {
+    private final AlternateSetTable[] alternateSetTables;
+
+    public LookupTypeAlternateSubstitutionFormat1(
+            int substFormat, CoverageTable coverageTable, AlternateSetTable[] alternateSetTables) {
+        super(substFormat, coverageTable);
+        this.alternateSetTables = alternateSetTables;
+    }
+
+    public AlternateSetTable[] getAlternateSetTables() {
+        return alternateSetTables;
+    }
+
+    @Override
+    public int doSubstitution(int gid, int coverageIndex) {
+        throw new UnsupportedOperationException("not applicable");
+    }
+}

Propchange: pdfbox/branches/3.0/fontbox/src/main/java/org/apache/fontbox/ttf/table/gsub/LookupTypeAlternateSubstitutionFormat1.java
------------------------------------------------------------------------------
    svn:eol-style = native