git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1631605 13f79535-47bb-0310-9956-ffa450edef68pull/28/head
@@ -46,10 +46,11 @@ public interface Substitutable { | |||
* @param script a script identifier | |||
* @param language a language identifier | |||
* @param associations optional list to receive list of character associations | |||
* @param retainControls if true, then retain control characters and their glyph mappings, otherwise remove | |||
* @return output sequence (represented as a character sequence, where each character in the returned sequence | |||
* denotes "font characters", i.e., character codes that map directly (1-1) to their associated glyphs | |||
*/ | |||
CharSequence performSubstitution(CharSequence cs, String script, String language, List associations); | |||
CharSequence performSubstitution(CharSequence cs, String script, String language, List associations, boolean retainControls); | |||
/** | |||
* Reorder combining marks in character sequence so that they precede (within the sequence) the base |
@@ -417,10 +417,10 @@ public class Font implements Substitutable, Positionable { | |||
/** {@inheritDoc} */ | |||
public CharSequence performSubstitution(CharSequence cs, | |||
String script, String language, List associations) { | |||
String script, String language, List associations, boolean retainControls) { | |||
if (metric instanceof Substitutable) { | |||
Substitutable s = (Substitutable) metric; | |||
return s.performSubstitution(cs, script, language, associations); | |||
return s.performSubstitution(cs, script, language, associations, retainControls); | |||
} else { | |||
throw new UnsupportedOperationException(); | |||
} |
@@ -83,11 +83,12 @@ public class GlyphMapping { | |||
public static GlyphMapping doGlyphMapping(TextFragment text, int startIndex, int endIndex, | |||
Font font, MinOptMax letterSpaceIPD, MinOptMax[] letterSpaceAdjustArray, | |||
char precedingChar, char breakOpportunityChar, final boolean endsWithHyphen, int level, | |||
boolean dontOptimizeForIdentityMapping, boolean retainAssociations) { | |||
boolean dontOptimizeForIdentityMapping, boolean retainAssociations, boolean retainControls) { | |||
GlyphMapping mapping; | |||
if (font.performsSubstitution() || font.performsPositioning()) { | |||
mapping = processWordMapping(text, startIndex, endIndex, font, | |||
breakOpportunityChar, endsWithHyphen, level, dontOptimizeForIdentityMapping, retainAssociations); | |||
breakOpportunityChar, endsWithHyphen, level, | |||
dontOptimizeForIdentityMapping, retainAssociations, retainControls); | |||
} else { | |||
mapping = processWordNoMapping(text, startIndex, endIndex, font, | |||
letterSpaceIPD, letterSpaceAdjustArray, precedingChar, breakOpportunityChar, endsWithHyphen, level); | |||
@@ -98,7 +99,7 @@ public class GlyphMapping { | |||
private static GlyphMapping processWordMapping(TextFragment text, int startIndex, | |||
int endIndex, final Font font, final char breakOpportunityChar, | |||
final boolean endsWithHyphen, int level, | |||
boolean dontOptimizeForIdentityMapping, boolean retainAssociations) { | |||
boolean dontOptimizeForIdentityMapping, boolean retainAssociations, boolean retainControls) { | |||
int e = endIndex; // end index of word in FOText character buffer | |||
int nLS = 0; // # of letter spaces | |||
String script = text.getScript(); | |||
@@ -126,7 +127,7 @@ public class GlyphMapping { | |||
// 3. perform mapping of chars to glyphs ... to glyphs ... to chars, retaining | |||
// associations if requested. | |||
List associations = retainAssociations ? new java.util.ArrayList() : null; | |||
CharSequence mcs = font.performSubstitution(ics, script, language, associations); | |||
CharSequence mcs = font.performSubstitution(ics, script, language, associations, retainControls); | |||
// 4. compute glyph position adjustments on (substituted) characters. | |||
int[][] gpa = null; |
@@ -415,10 +415,12 @@ public class LazyFont extends Typeface implements FontDescriptor, Substitutable, | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public CharSequence performSubstitution(CharSequence cs, String script, String language, List associations) { | |||
public CharSequence performSubstitution(CharSequence cs, String script, String language, List associations, | |||
boolean retainControls) { | |||
load(true); | |||
if (realFontDescriptor instanceof Substitutable) { | |||
return ((Substitutable)realFontDescriptor).performSubstitution(cs, script, language, associations); | |||
return ((Substitutable)realFontDescriptor).performSubstitution(cs, | |||
script, language, associations, retainControls); | |||
} else { | |||
return cs; | |||
} |
@@ -38,6 +38,7 @@ import org.apache.fop.complexscripts.fonts.GlyphSubstitutionTable; | |||
import org.apache.fop.complexscripts.fonts.GlyphTable; | |||
import org.apache.fop.complexscripts.fonts.Positionable; | |||
import org.apache.fop.complexscripts.fonts.Substitutable; | |||
import org.apache.fop.complexscripts.util.CharAssociation; | |||
import org.apache.fop.complexscripts.util.CharNormalize; | |||
import org.apache.fop.complexscripts.util.GlyphSequence; | |||
import org.apache.fop.util.CharUtilities; | |||
@@ -490,7 +491,8 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl | |||
} | |||
/** {@inheritDoc} */ | |||
public CharSequence performSubstitution(CharSequence cs, String script, String language, List associations) { | |||
public CharSequence performSubstitution(CharSequence cs, String script, String language, List associations, | |||
boolean retainControls) { | |||
if (gsub != null) { | |||
CharSequence ncs = normalize(cs, associations); | |||
GlyphSequence igs = mapCharsToGlyphs(ncs, associations); | |||
@@ -499,6 +501,9 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl | |||
associations.clear(); | |||
associations.addAll(ogs.getAssociations()); | |||
} | |||
if (!retainControls) { | |||
ogs = elideControls(ogs); | |||
} | |||
CharSequence ocs = mapGlyphsToChars(ogs); | |||
return ocs; | |||
} else { | |||
@@ -695,6 +700,66 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl | |||
return sb; | |||
} | |||
private static GlyphSequence elideControls(GlyphSequence gs) { | |||
if (hasElidableControl(gs)) { | |||
int[] ca = gs.getCharacterArray(false); | |||
IntBuffer ngb = IntBuffer.allocate(gs.getGlyphCount()); | |||
List nal = new java.util.ArrayList(gs.getGlyphCount()); | |||
for (int i = 0, n = gs.getGlyphCount(); i < n; ++i) { | |||
CharAssociation a = gs.getAssociation(i); | |||
int s = a.getStart(); | |||
int e = a.getEnd(); | |||
while (s < e) { | |||
int ch = ca [ s ]; | |||
if (isElidableControl(ch)) { | |||
break; | |||
} else { | |||
++s; | |||
} | |||
} | |||
if (s == e) { | |||
ngb.put(gs.getGlyph(i)); | |||
nal.add(a); | |||
} | |||
} | |||
ngb.flip(); | |||
return new GlyphSequence(gs.getCharacters(), ngb, nal, gs.getPredications()); | |||
} else { | |||
return gs; | |||
} | |||
} | |||
private static boolean hasElidableControl(GlyphSequence gs) { | |||
int[] ca = gs.getCharacterArray(false); | |||
for (int i = 0, n = ca.length; i < n; ++i) { | |||
int ch = ca [ i ]; | |||
if (isElidableControl(ch)) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
private static boolean isElidableControl(int ch) { | |||
if (ch < 0x0020) { | |||
return true; | |||
} else if ((ch >= 0x80) && (ch < 0x00A0)) { | |||
return true; | |||
} else if ((ch >= 0x2000) && (ch <= 0x206F)) { | |||
if ((ch >= 0x200B) && (ch <= 0x200F)) { | |||
return true; | |||
} else if ((ch >= 0x2028) && (ch <= 0x202E)) { | |||
return true; | |||
} else if ((ch >= 0x2066) && (ch <= 0x206F)) { | |||
return true; | |||
} else { | |||
return ch == 0x2060; | |||
} | |||
} else { | |||
return false; | |||
} | |||
} | |||
@Override | |||
public boolean hasFeature(int tableType, String script, String language, String feature) { | |||
GlyphTable table; |
@@ -761,6 +761,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { | |||
char ch = 0; | |||
int level = -1; | |||
int prevLevel = -1; | |||
boolean retainControls = false; | |||
while (nextStart < foText.length()) { | |||
ch = foText.charAt(nextStart); | |||
level = foText.bidiLevelAt(nextStart); | |||
@@ -799,7 +800,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { | |||
|| ((prevLevel != -1) && (level != prevLevel))) { | |||
// this.foText.charAt(lastIndex) == CharUtilities.SOFT_HYPHEN | |||
prevMapping = processWord(alignment, sequence, prevMapping, ch, | |||
breakOpportunity, true, prevLevel); | |||
breakOpportunity, true, prevLevel, retainControls); | |||
} | |||
} else if (inWhitespace) { | |||
if (ch != CharUtilities.SPACE || breakOpportunity) { | |||
@@ -850,7 +851,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { | |||
// Process any last elements | |||
if (inWord) { | |||
processWord(alignment, sequence, prevMapping, ch, false, false, prevLevel); | |||
processWord(alignment, sequence, prevMapping, ch, false, false, prevLevel, retainControls); | |||
} else if (inWhitespace) { | |||
processWhitespace(alignment, sequence, !keepTogether, prevLevel); | |||
} else if (mapping != null) { | |||
@@ -918,7 +919,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { | |||
private GlyphMapping processWord(final int alignment, final KnuthSequence sequence, | |||
GlyphMapping prevMapping, final char ch, final boolean breakOpportunity, | |||
final boolean checkEndsWithHyphen, int level) { | |||
final boolean checkEndsWithHyphen, int level, boolean retainControls) { | |||
//Word boundary found, process widths and kerning | |||
int lastIndex = nextStart; | |||
@@ -934,7 +935,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { | |||
&& prevMapping.endIndex > 0 ? foText.charAt(prevMapping.endIndex - 1) : 0; | |||
GlyphMapping mapping = GlyphMapping.doGlyphMapping(foText, thisStart, lastIndex, font, | |||
letterSpaceIPD, letterSpaceAdjustArray, precedingChar, breakOpportunityChar, | |||
endsWithHyphen, level, false, false); | |||
endsWithHyphen, level, false, false, retainControls); | |||
prevMapping = mapping; | |||
addGlyphMapping(mapping); | |||
tempStart = nextStart; |
@@ -268,9 +268,10 @@ public class CustomFontMetricsMapper extends Typeface implements FontMetricsMapp | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public CharSequence performSubstitution(CharSequence cs, String script, String language, List associations) { | |||
public CharSequence performSubstitution(CharSequence cs, String script, String language, List associations, | |||
boolean retainControls) { | |||
if (typeface instanceof Substitutable) { | |||
return ((Substitutable) typeface).performSubstitution(cs, script, language, associations); | |||
return ((Substitutable) typeface).performSubstitution(cs, script, language, associations, retainControls); | |||
} else { | |||
return cs; | |||
} |
@@ -90,8 +90,10 @@ public class FOPGVTGlyphVector implements GVTGlyphVector { | |||
Font f = font.getFont(); | |||
MinOptMax letterSpaceIPD = MinOptMax.ZERO; | |||
MinOptMax[] letterSpaceAdjustments = new MinOptMax[text.getEndIndex() - text.getBeginIndex()]; | |||
boolean retainControls = false; | |||
GlyphMapping mapping = GlyphMapping.doGlyphMapping(text, text.getBeginIndex(), text.getEndIndex(), | |||
f, letterSpaceIPD, letterSpaceAdjustments, '\0', '\0', false, text.getBidiLevel(), true, true); | |||
f, letterSpaceIPD, letterSpaceAdjustments, '\0', '\0', | |||
false, text.getBidiLevel(), true, true, retainControls); | |||
CharacterIterator glyphAsCharIter = | |||
mapping.mapping != null ? new StringCharacterIterator(mapping.mapping) : text.getIterator(); | |||
this.glyphs = buildGlyphs(f, glyphAsCharIter); |