Skip to content

Commit

Permalink
PDFBOX-5648: support GSUB LookupType 2 by changing MapBackedGsubData …
Browse files Browse the repository at this point in the history
…so that its value is also a List

git-svn-id: https://svn.apache.org/repos/asf/pdfbox/trunk@1917545 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
THausherr committed May 7, 2024
1 parent 5033e86 commit 6b09c7c
Show file tree
Hide file tree
Showing 11 changed files with 81 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package org.apache.fontbox.ttf.gsub;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
Expand Down Expand Up @@ -97,7 +98,7 @@ private MapBackedGsubData buildMapBackedGsubData(FeatureListTable featureListTab
{
ScriptTable scriptTable = scriptTableDetails.getScriptTable();

Map<String, Map<List<Integer>, Integer>> gsubData = new LinkedHashMap<>();
Map<String, Map<List<Integer>, List<Integer>>> gsubData = new LinkedHashMap<>();
// the starting point is really the scriptTags
if (scriptTable.getDefaultLangSysTable() != null)
{
Expand Down Expand Up @@ -129,7 +130,7 @@ private ScriptTableDetails getSupportedLanguage(Map<String, ScriptTable> scriptL
return null;
}

private void populateGsubData(Map<String, Map<List<Integer>, Integer>> gsubData,
private void populateGsubData(Map<String, Map<List<Integer>, List<Integer>>> gsubData,
LangSysTable langSysTable, FeatureListTable featureListTable,
LookupListTable lookupListTable)
{
Expand All @@ -143,11 +144,12 @@ private void populateGsubData(Map<String, Map<List<Integer>, Integer>> gsubData,
}
}

private void populateGsubData(Map<String, Map<List<Integer>, Integer>> gsubData,
// Creates a Map<List<Integer>, Integer> from the lookup tables
private void populateGsubData(Map<String, Map<List<Integer>, List<Integer>>> gsubData,
FeatureRecord featureRecord, LookupListTable lookupListTable)
{
LookupTable[] lookups = lookupListTable.getLookups();
Map<List<Integer>, Integer> glyphSubstitutionMap = new LinkedHashMap<>();
Map<List<Integer>, List<Integer>> glyphSubstitutionMap = new LinkedHashMap<>();
for (int lookupIndex : featureRecord.getFeatureTable().getLookupListIndices())
{
if (lookupIndex < lookups.length)
Expand All @@ -163,7 +165,7 @@ private void populateGsubData(Map<String, Map<List<Integer>, Integer>> gsubData,
Collections.unmodifiableMap(glyphSubstitutionMap));
}

private void extractData(Map<List<Integer>, Integer> glyphSubstitutionMap,
private void extractData(Map<List<Integer>, List<Integer>> glyphSubstitutionMap,
LookupTable lookupTable)
{

Expand Down Expand Up @@ -204,21 +206,21 @@ else if (lookupSubTable instanceof LookupTypeMultipleSubstitutionFormat1)
}

private void extractDataFromSingleSubstTableFormat1Table(
Map<List<Integer>, Integer> glyphSubstitutionMap,
Map<List<Integer>, List<Integer>> glyphSubstitutionMap,
LookupTypeSingleSubstFormat1 singleSubstTableFormat1)
{
CoverageTable coverageTable = singleSubstTableFormat1.getCoverageTable();
for (int i = 0; i < coverageTable.getSize(); i++)
{
int coverageGlyphId = coverageTable.getGlyphId(i);
int substituteGlyphId = coverageGlyphId + singleSubstTableFormat1.getDeltaGlyphID();
putNewSubstitutionEntry(glyphSubstitutionMap, substituteGlyphId,
putNewSubstitutionEntry(glyphSubstitutionMap, Collections.singletonList(substituteGlyphId),
Collections.singletonList(coverageGlyphId));
}
}

private void extractDataFromSingleSubstTableFormat2Table(
Map<List<Integer>, Integer> glyphSubstitutionMap,
Map<List<Integer>, List<Integer>> glyphSubstitutionMap,
LookupTypeSingleSubstFormat2 singleSubstTableFormat2)
{

Expand All @@ -237,13 +239,13 @@ private void extractDataFromSingleSubstTableFormat2Table(
{
int coverageGlyphId = coverageTable.getGlyphId(i);
int substituteGlyphId = singleSubstTableFormat2.getSubstituteGlyphIDs()[i];
putNewSubstitutionEntry(glyphSubstitutionMap, substituteGlyphId,
putNewSubstitutionEntry(glyphSubstitutionMap, Collections.singletonList(substituteGlyphId),
Collections.singletonList(coverageGlyphId));
}
}

private void extractDataFromMultipleSubstitutionFormat1Table(
Map<List<Integer>, Integer> glyphSubstitutionMap,
Map<List<Integer>, List<Integer>> glyphSubstitutionMap,
LookupTypeMultipleSubstitutionFormat1 multipleSubstFormat1Subtable)
{
CoverageTable coverageTable = multipleSubstFormat1Subtable.getCoverageTable();
Expand All @@ -261,15 +263,20 @@ private void extractDataFromMultipleSubstitutionFormat1Table(
{
int coverageGlyphId = coverageTable.getGlyphId(i);
SequenceTable sequenceTable = multipleSubstFormat1Subtable.getSequenceTables()[i];

//TODO implement storing this data
// (not possible at this time because the map value isn't a list)
//TODO List.Of and Arrays.asList didn't work?!
List<Integer> list = new ArrayList<>();
for (int id : sequenceTable.getSubstituteGlyphIDs())
{
list.add(id);
}
putNewSubstitutionEntry(glyphSubstitutionMap,
list,
Collections.singletonList(coverageGlyphId));
}

}

private void extractDataFromLigatureSubstitutionSubstFormat1Table(
Map<List<Integer>, Integer> glyphSubstitutionMap,
Map<List<Integer>, List<Integer>> glyphSubstitutionMap,
LookupTypeLigatureSubstitutionSubstFormat1 ligatureSubstitutionTable)
{

Expand All @@ -292,7 +299,7 @@ private void extractDataFromLigatureSubstitutionSubstFormat1Table(
* @param alternateSubstitutionFormat1 the alternate substitution format 1 table
*/
private void extractDataFromAlternateSubstitutionSubstFormat1Table(
Map<List<Integer>, Integer> glyphSubstitutionMap,
Map<List<Integer>, List<Integer>> glyphSubstitutionMap,
LookupTypeAlternateSubstitutionFormat1 alternateSubstitutionFormat1)
{

Expand All @@ -316,7 +323,7 @@ private void extractDataFromAlternateSubstitutionSubstFormat1Table(
{
if (alternateGlyphId != coverageGlyphId)
{
putNewSubstitutionEntry(glyphSubstitutionMap, alternateGlyphId,
putNewSubstitutionEntry(glyphSubstitutionMap, Collections.singletonList(alternateGlyphId),
Collections.singletonList(coverageGlyphId));
break;
}
Expand All @@ -325,7 +332,7 @@ private void extractDataFromAlternateSubstitutionSubstFormat1Table(

}

private void extractDataFromLigatureTable(Map<List<Integer>, Integer> glyphSubstitutionMap,
private void extractDataFromLigatureTable(Map<List<Integer>, List<Integer>> glyphSubstitutionMap,
LigatureTable ligatureTable)
{
int[] componentGlyphIDs = ligatureTable.getComponentGlyphIDs();
Expand All @@ -337,20 +344,19 @@ private void extractDataFromLigatureTable(Map<List<Integer>, Integer> glyphSubst

LOG.debug("glyphsToBeSubstituted: {}", glyphsToBeSubstituted);

putNewSubstitutionEntry(glyphSubstitutionMap, ligatureTable.getLigatureGlyph(),
putNewSubstitutionEntry(glyphSubstitutionMap, Collections.singletonList(ligatureTable.getLigatureGlyph()),
glyphsToBeSubstituted);

}

private void putNewSubstitutionEntry(Map<List<Integer>, Integer> glyphSubstitutionMap,
int newGlyph, List<Integer> glyphsToBeSubstituted)
private void putNewSubstitutionEntry(Map<List<Integer>, List<Integer>> glyphSubstitutionMap,
List<Integer> newGlyphList, List<Integer> glyphsToBeSubstituted)
{
Integer oldValue = glyphSubstitutionMap.put(glyphsToBeSubstituted, newGlyph);
List<Integer> oldValues = glyphSubstitutionMap.put(glyphsToBeSubstituted, newGlyphList);

if (oldValue != null)
if (oldValues != null)
{
LOG.debug("For the newGlyph: {}, newValue: {} is trying to override the oldValue {}",
newGlyph, glyphsToBeSubstituted, oldValue);
newGlyphList, glyphsToBeSubstituted, oldValues);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,8 @@ private List<Integer> applyGsubFeature(ScriptFeature scriptFeature,
if (scriptFeature.canReplaceGlyphs(chunk))
{
// gsub system kicks in, you get the glyphId directly
Integer glyphId = scriptFeature.getReplacementForGlyphs(chunk);
gsubProcessedGlyphs.add(glyphId);
List<Integer> replacementForGlyphs = scriptFeature.getReplacementForGlyphs(chunk);
gsubProcessedGlyphs.addAll(replacementForGlyphs);
}
else
{
Expand Down Expand Up @@ -196,7 +196,7 @@ private List<Integer> getBeforeHalfGlyphIds()
ScriptFeature feature = gsubData.getFeature(INIT_FEATURE);
for (List<Integer> glyphCluster : feature.getAllGlyphIdsForSubstitution())
{
glyphIds.add(feature.getReplacementForGlyphs(glyphCluster));
glyphIds.addAll(feature.getReplacementForGlyphs(glyphCluster));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,8 @@ private List<Integer> applyGsubFeature(ScriptFeature scriptFeature, List<Integer
{
if (scriptFeature.canReplaceGlyphs(chunk))
{
Integer glyphId = scriptFeature.getReplacementForGlyphs(chunk);
gsubProcessedGlyphs.add(glyphId);
List<Integer> replacementForGlyphs = scriptFeature.getReplacementForGlyphs(chunk);
gsubProcessedGlyphs.addAll(replacementForGlyphs);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,8 @@ private List<Integer> applyGsubFeature(ScriptFeature scriptFeature, List<Integer
{
if (scriptFeature.canReplaceGlyphs(chunk))
{
Integer glyphId = scriptFeature.getReplacementForGlyphs(chunk);
gsubProcessedGlyphs.add(glyphId);
List<Integer> replacementForGlyphs = scriptFeature.getReplacementForGlyphs(chunk);
gsubProcessedGlyphs.addAll(replacementForGlyphs);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ private List<Integer> applyGsubFeature(ScriptFeature scriptFeature,
if (scriptFeature.canReplaceGlyphs(chunk))
{
// gsub system kicks in, you get the glyphId directly
int glyphId = scriptFeature.getReplacementForGlyphs(chunk);
gsubProcessedGlyphs.add(glyphId);
List<Integer> replacementForGlyphs = scriptFeature.getReplacementForGlyphs(chunk);
gsubProcessedGlyphs.addAll(replacementForGlyphs);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ public class MapBackedGsubData implements GsubData

private final Language language;
private final String activeScriptName;
private final Map<String, Map<List<Integer>, Integer>> glyphSubstitutionMap;
private final Map<String, Map<List<Integer>, List<Integer>>> glyphSubstitutionMap;

public MapBackedGsubData(Language language, String activeScriptName,
Map<String, Map<List<Integer>, Integer>> glyphSubstitutionMap)
Map<String, Map<List<Integer>, List<Integer>>> glyphSubstitutionMap)
{
this.language = language;
this.activeScriptName = activeScriptName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ public class MapBackedScriptFeature implements ScriptFeature
{

private final String name;
private final Map<List<Integer>, Integer> featureMap;
private final Map<List<Integer>, List<Integer>> featureMap;

public MapBackedScriptFeature(String name, Map<List<Integer>, Integer> featureMap)
public MapBackedScriptFeature(String name, Map<List<Integer>, List<Integer>> featureMap)
{
this.name = name;
this.featureMap = featureMap;
Expand All @@ -60,7 +60,7 @@ public boolean canReplaceGlyphs(List<Integer> glyphIds)
}

@Override
public Integer getReplacementForGlyphs(List<Integer> glyphIds)
public List<Integer> getReplacementForGlyphs(List<Integer> glyphIds)
{
if (!canReplaceGlyphs(glyphIds))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,35 @@
*/
public interface ScriptFeature
{

/**
* The name of this feature.
*
* @return
*/
String getName();

/**
* Returns a set of integer sequences that may match and thus can be replaced by
* something else.
*
* @return
*/
Set<List<Integer>> getAllGlyphIdsForSubstitution();

/**
* Returns true if the sequence of integers can be replaced by something else.
*
* @param glyphIds sequence of integers
* @return
*/
boolean canReplaceGlyphs(List<Integer> glyphIds);

Integer getReplacementForGlyphs(List<Integer> glyphIds);
/**
* Returns the replacement for the input sequence of integers.
*
* @param glyphIds sequence of integers
* @return
*/
List<Integer> getReplacementForGlyphs(List<Integer> glyphIds);

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -75,7 +76,7 @@ void testGetGsubData() throws IOException
for (String featureName : EXPECTED_FEATURE_NAMES)
{
System.out.println("******* Testing feature: " + featureName);
Map<List<Integer>, Integer> expectedGsubTableRawData = getExpectedGsubTableRawData(
Map<List<Integer>, List<Integer>> expectedGsubTableRawData = getExpectedGsubTableRawData(
String.format(templatePathToFile, featureName));
ScriptFeature scriptFeature = new MapBackedScriptFeature(featureName,
expectedGsubTableRawData);
Expand All @@ -84,10 +85,10 @@ void testGetGsubData() throws IOException

}

private Map<List<Integer>, Integer> getExpectedGsubTableRawData(String pathToResource)
private Map<List<Integer>, List<Integer>> getExpectedGsubTableRawData(String pathToResource)
throws IOException
{
Map<List<Integer>, Integer> gsubData = new HashMap<>();
Map<List<Integer>, List<Integer>> gsubData = new HashMap<>();

try (BufferedReader br = new BufferedReader(
new InputStreamReader(TestTTFParser.class.getResourceAsStream(pathToResource))))
Expand Down Expand Up @@ -124,7 +125,7 @@ private Map<List<Integer>, Integer> getExpectedGsubTableRawData(String pathToRes
}

Integer newGlyphId = Integer.valueOf(lineSplittedByKeyValue[1]);
gsubData.put(oldGlyphIds, newGlyphId);
gsubData.put(oldGlyphIds, Collections.singletonList(newGlyphId));
}
}
return gsubData;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package org.apache.fontbox.ttf.gsub;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -44,7 +45,7 @@ public void printCharacterToGlyph(GsubData gsubData, CmapLookup cmap)
{
System.err.println(
"Format:\n<Serial no.>.) <Space separated characters to be replaced> : RawUnicode: [<Space separated unicode representation of each character to be replaced in hexadecimal>] : <The compound character> : <The GlyphId with which these characters are replaced>");
Map<Integer, List<Integer>> rawGSubTableData = new HashMap<>();
Map<List<Integer>, List<Integer>> rawGSubTableData = new HashMap<>();

for (String featureName : gsubData.getSupportedFeatures())
{
Expand Down Expand Up @@ -74,15 +75,15 @@ public void printCharacterToGlyph(GsubData gsubData, CmapLookup cmap)

}

private String getUnicodeChar(Map<Integer, List<Integer>> rawGSubTableData, CmapLookup cmap,
private String getUnicodeChar(Map<List<Integer>, List<Integer>> rawGSubTableData, CmapLookup cmap,
Integer glyphId)
{
List<Integer> keyChars = cmap.getCharCodes(glyphId);

// its a compound glyph
if (keyChars == null)
{
List<Integer> constituentGlyphs = rawGSubTableData.get(glyphId);
List<Integer> constituentGlyphs = rawGSubTableData.get(Collections.singletonList(glyphId));

if (constituentGlyphs == null || constituentGlyphs.isEmpty())
{
Expand All @@ -107,7 +108,7 @@ private String getUnicodeChar(Map<Integer, List<Integer>> rawGSubTableData, Cmap

}

private String getUnicodeString(Map<Integer, List<Integer>> rawGSubTableData, CmapLookup cmap,
private String getUnicodeString(Map<List<Integer>, List<Integer>> rawGSubTableData, CmapLookup cmap,
List<Integer> glyphIDs)
{
StringBuilder sb = new StringBuilder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ private List<Integer> applyGsubFeature(ScriptFeature scriptFeature,
if (scriptFeature.canReplaceGlyphs(chunk))
{
// gsub system kicks in, you get the glyphId directly
int glyphId = scriptFeature.getReplacementForGlyphs(chunk);
gsubProcessedGlyphs.add(glyphId);
List<Integer> replacementForGlyphs = scriptFeature.getReplacementForGlyphs(chunk);
gsubProcessedGlyphs.addAll(replacementForGlyphs);
}
else
{
Expand Down

0 comments on commit 6b09c7c

Please sign in to comment.