git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1631605 13f79535-47bb-0310-9956-ffa450edef68pull/28/head
* @param script a script identifier | * @param script a script identifier | ||||
* @param language a language identifier | * @param language a language identifier | ||||
* @param associations optional list to receive list of character associations | * @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 | * @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 | * 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 | * Reorder combining marks in character sequence so that they precede (within the sequence) the base |
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public CharSequence performSubstitution(CharSequence cs, | public CharSequence performSubstitution(CharSequence cs, | ||||
String script, String language, List associations) { | |||||
String script, String language, List associations, boolean retainControls) { | |||||
if (metric instanceof Substitutable) { | if (metric instanceof Substitutable) { | ||||
Substitutable s = (Substitutable) metric; | Substitutable s = (Substitutable) metric; | ||||
return s.performSubstitution(cs, script, language, associations); | |||||
return s.performSubstitution(cs, script, language, associations, retainControls); | |||||
} else { | } else { | ||||
throw new UnsupportedOperationException(); | throw new UnsupportedOperationException(); | ||||
} | } |
public static GlyphMapping doGlyphMapping(TextFragment text, int startIndex, int endIndex, | public static GlyphMapping doGlyphMapping(TextFragment text, int startIndex, int endIndex, | ||||
Font font, MinOptMax letterSpaceIPD, MinOptMax[] letterSpaceAdjustArray, | Font font, MinOptMax letterSpaceIPD, MinOptMax[] letterSpaceAdjustArray, | ||||
char precedingChar, char breakOpportunityChar, final boolean endsWithHyphen, int level, | char precedingChar, char breakOpportunityChar, final boolean endsWithHyphen, int level, | ||||
boolean dontOptimizeForIdentityMapping, boolean retainAssociations) { | |||||
boolean dontOptimizeForIdentityMapping, boolean retainAssociations, boolean retainControls) { | |||||
GlyphMapping mapping; | GlyphMapping mapping; | ||||
if (font.performsSubstitution() || font.performsPositioning()) { | if (font.performsSubstitution() || font.performsPositioning()) { | ||||
mapping = processWordMapping(text, startIndex, endIndex, font, | mapping = processWordMapping(text, startIndex, endIndex, font, | ||||
breakOpportunityChar, endsWithHyphen, level, dontOptimizeForIdentityMapping, retainAssociations); | |||||
breakOpportunityChar, endsWithHyphen, level, | |||||
dontOptimizeForIdentityMapping, retainAssociations, retainControls); | |||||
} else { | } else { | ||||
mapping = processWordNoMapping(text, startIndex, endIndex, font, | mapping = processWordNoMapping(text, startIndex, endIndex, font, | ||||
letterSpaceIPD, letterSpaceAdjustArray, precedingChar, breakOpportunityChar, endsWithHyphen, level); | letterSpaceIPD, letterSpaceAdjustArray, precedingChar, breakOpportunityChar, endsWithHyphen, level); | ||||
private static GlyphMapping processWordMapping(TextFragment text, int startIndex, | private static GlyphMapping processWordMapping(TextFragment text, int startIndex, | ||||
int endIndex, final Font font, final char breakOpportunityChar, | int endIndex, final Font font, final char breakOpportunityChar, | ||||
final boolean endsWithHyphen, int level, | 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 e = endIndex; // end index of word in FOText character buffer | ||||
int nLS = 0; // # of letter spaces | int nLS = 0; // # of letter spaces | ||||
String script = text.getScript(); | String script = text.getScript(); | ||||
// 3. perform mapping of chars to glyphs ... to glyphs ... to chars, retaining | // 3. perform mapping of chars to glyphs ... to glyphs ... to chars, retaining | ||||
// associations if requested. | // associations if requested. | ||||
List associations = retainAssociations ? new java.util.ArrayList() : null; | 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. | // 4. compute glyph position adjustments on (substituted) characters. | ||||
int[][] gpa = null; | int[][] gpa = null; |
/** | /** | ||||
* {@inheritDoc} | * {@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); | load(true); | ||||
if (realFontDescriptor instanceof Substitutable) { | if (realFontDescriptor instanceof Substitutable) { | ||||
return ((Substitutable)realFontDescriptor).performSubstitution(cs, script, language, associations); | |||||
return ((Substitutable)realFontDescriptor).performSubstitution(cs, | |||||
script, language, associations, retainControls); | |||||
} else { | } else { | ||||
return cs; | return cs; | ||||
} | } |
import org.apache.fop.complexscripts.fonts.GlyphTable; | import org.apache.fop.complexscripts.fonts.GlyphTable; | ||||
import org.apache.fop.complexscripts.fonts.Positionable; | import org.apache.fop.complexscripts.fonts.Positionable; | ||||
import org.apache.fop.complexscripts.fonts.Substitutable; | 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.CharNormalize; | ||||
import org.apache.fop.complexscripts.util.GlyphSequence; | import org.apache.fop.complexscripts.util.GlyphSequence; | ||||
import org.apache.fop.util.CharUtilities; | import org.apache.fop.util.CharUtilities; | ||||
} | } | ||||
/** {@inheritDoc} */ | /** {@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) { | if (gsub != null) { | ||||
CharSequence ncs = normalize(cs, associations); | CharSequence ncs = normalize(cs, associations); | ||||
GlyphSequence igs = mapCharsToGlyphs(ncs, associations); | GlyphSequence igs = mapCharsToGlyphs(ncs, associations); | ||||
associations.clear(); | associations.clear(); | ||||
associations.addAll(ogs.getAssociations()); | associations.addAll(ogs.getAssociations()); | ||||
} | } | ||||
if (!retainControls) { | |||||
ogs = elideControls(ogs); | |||||
} | |||||
CharSequence ocs = mapGlyphsToChars(ogs); | CharSequence ocs = mapGlyphsToChars(ogs); | ||||
return ocs; | return ocs; | ||||
} else { | } else { | ||||
return sb; | 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 | @Override | ||||
public boolean hasFeature(int tableType, String script, String language, String feature) { | public boolean hasFeature(int tableType, String script, String language, String feature) { | ||||
GlyphTable table; | GlyphTable table; |
char ch = 0; | char ch = 0; | ||||
int level = -1; | int level = -1; | ||||
int prevLevel = -1; | int prevLevel = -1; | ||||
boolean retainControls = false; | |||||
while (nextStart < foText.length()) { | while (nextStart < foText.length()) { | ||||
ch = foText.charAt(nextStart); | ch = foText.charAt(nextStart); | ||||
level = foText.bidiLevelAt(nextStart); | level = foText.bidiLevelAt(nextStart); | ||||
|| ((prevLevel != -1) && (level != prevLevel))) { | || ((prevLevel != -1) && (level != prevLevel))) { | ||||
// this.foText.charAt(lastIndex) == CharUtilities.SOFT_HYPHEN | // this.foText.charAt(lastIndex) == CharUtilities.SOFT_HYPHEN | ||||
prevMapping = processWord(alignment, sequence, prevMapping, ch, | prevMapping = processWord(alignment, sequence, prevMapping, ch, | ||||
breakOpportunity, true, prevLevel); | |||||
breakOpportunity, true, prevLevel, retainControls); | |||||
} | } | ||||
} else if (inWhitespace) { | } else if (inWhitespace) { | ||||
if (ch != CharUtilities.SPACE || breakOpportunity) { | if (ch != CharUtilities.SPACE || breakOpportunity) { | ||||
// Process any last elements | // Process any last elements | ||||
if (inWord) { | if (inWord) { | ||||
processWord(alignment, sequence, prevMapping, ch, false, false, prevLevel); | |||||
processWord(alignment, sequence, prevMapping, ch, false, false, prevLevel, retainControls); | |||||
} else if (inWhitespace) { | } else if (inWhitespace) { | ||||
processWhitespace(alignment, sequence, !keepTogether, prevLevel); | processWhitespace(alignment, sequence, !keepTogether, prevLevel); | ||||
} else if (mapping != null) { | } else if (mapping != null) { | ||||
private GlyphMapping processWord(final int alignment, final KnuthSequence sequence, | private GlyphMapping processWord(final int alignment, final KnuthSequence sequence, | ||||
GlyphMapping prevMapping, final char ch, final boolean breakOpportunity, | 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 | //Word boundary found, process widths and kerning | ||||
int lastIndex = nextStart; | int lastIndex = nextStart; | ||||
&& prevMapping.endIndex > 0 ? foText.charAt(prevMapping.endIndex - 1) : 0; | && prevMapping.endIndex > 0 ? foText.charAt(prevMapping.endIndex - 1) : 0; | ||||
GlyphMapping mapping = GlyphMapping.doGlyphMapping(foText, thisStart, lastIndex, font, | GlyphMapping mapping = GlyphMapping.doGlyphMapping(foText, thisStart, lastIndex, font, | ||||
letterSpaceIPD, letterSpaceAdjustArray, precedingChar, breakOpportunityChar, | letterSpaceIPD, letterSpaceAdjustArray, precedingChar, breakOpportunityChar, | ||||
endsWithHyphen, level, false, false); | |||||
endsWithHyphen, level, false, false, retainControls); | |||||
prevMapping = mapping; | prevMapping = mapping; | ||||
addGlyphMapping(mapping); | addGlyphMapping(mapping); | ||||
tempStart = nextStart; | tempStart = nextStart; |
/** | /** | ||||
* {@inheritDoc} | * {@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) { | if (typeface instanceof Substitutable) { | ||||
return ((Substitutable) typeface).performSubstitution(cs, script, language, associations); | |||||
return ((Substitutable) typeface).performSubstitution(cs, script, language, associations, retainControls); | |||||
} else { | } else { | ||||
return cs; | return cs; | ||||
} | } |
Font f = font.getFont(); | Font f = font.getFont(); | ||||
MinOptMax letterSpaceIPD = MinOptMax.ZERO; | MinOptMax letterSpaceIPD = MinOptMax.ZERO; | ||||
MinOptMax[] letterSpaceAdjustments = new MinOptMax[text.getEndIndex() - text.getBeginIndex()]; | MinOptMax[] letterSpaceAdjustments = new MinOptMax[text.getEndIndex() - text.getBeginIndex()]; | ||||
boolean retainControls = false; | |||||
GlyphMapping mapping = GlyphMapping.doGlyphMapping(text, text.getBeginIndex(), text.getEndIndex(), | 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 = | CharacterIterator glyphAsCharIter = | ||||
mapping.mapping != null ? new StringCharacterIterator(mapping.mapping) : text.getIterator(); | mapping.mapping != null ? new StringCharacterIterator(mapping.mapping) : text.getIterator(); | ||||
this.glyphs = buildGlyphs(f, glyphAsCharIter); | this.glyphs = buildGlyphs(f, glyphAsCharIter); |