
com.adobe.fontengine.inlineformatting.infontformatting.ArabicFormatter Maven / Gradle / Ivy
/*
*
* File: ArabicFormatter.java
*
*
* ADOBE CONFIDENTIAL
* ___________________
*
* Copyright 2004-2005 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains the property of
* Adobe Systems Incorporated and its suppliers, if any. The intellectual
* and technical concepts contained herein are proprietary to Adobe Systems
* Incorporated and its suppliers and may be covered by U.S. and Foreign
* Patents, patents in process, and are protected by trade secret or
* copyright law. Dissemination of this information or reproduction of this
* material is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
*
*/
package com.adobe.fontengine.inlineformatting.infontformatting;
import com.adobe.agl.lang.UCharacter;
import com.adobe.agl.lang.UProperty;
import com.adobe.agl.util.ULocale;
import com.adobe.fontengine.CharUtil;
import com.adobe.fontengine.font.Font;
import com.adobe.fontengine.font.FontImpl;
import com.adobe.fontengine.font.FontLoadingException;
import com.adobe.fontengine.font.InvalidFontException;
import com.adobe.fontengine.font.UnsupportedFontException;
import com.adobe.fontengine.font.opentype.OTSelector;
import com.adobe.fontengine.font.opentype.OTSelectorOnElementAttribute;
import com.adobe.fontengine.font.opentype.OTSelectors;
import com.adobe.fontengine.font.opentype.OpenTypeFont;
import com.adobe.fontengine.font.opentype.Tag;
import com.adobe.fontengine.inlineformatting.AttributedRun;
import com.adobe.fontengine.inlineformatting.ElementAttribute;
import com.adobe.fontengine.inlineformatting.InterElementAttribute;
import com.adobe.fontengine.inlineformatting.LigatureLevel;
final class ArabicFormatter extends BaseFormatter {
//------------------------------------------------------------ first pass ---
/** Enumeration of the possible joining shapes.
*/
private static class JoiningShapes {
private String name;
private JoiningShapes (String name) {
this.name = name;
}
public String toString () {
return name;
}
public static final JoiningShapes init = new JoiningShapes ("init");
public static final JoiningShapes isol = new JoiningShapes ("isol");
public static final JoiningShapes medi = new JoiningShapes ("medi");
public static final JoiningShapes fina = new JoiningShapes ("fina");
}
/** Attribute to carry the joining shape.
* Its value must be a JoiningShape.
*/
private static final ElementAttribute joiningShape
= new ElementAttribute ("joiningShape");
/** Set the joiningShape attribute on a run.
* The joiningShape attribute is the actual shape that non transparent
* characters must take. This code assumes that the subrun being processed
* is bracketed by NON_JOINING characters.
* The elements between start and limit must be at the same directionality
* level.
*/
protected void setJoiningShape (AttributedRun run, int start, int limit) {
int previousIndex = Integer.MIN_VALUE;
boolean previousJoinsItsRight = false;
int previousType = UCharacter.JoiningType.NON_JOINING;
for (int i = start; i < limit + 1; i++) {
int thisType;
if (i == limit) {
thisType = UCharacter.JoiningType.NON_JOINING; }
else if (run.getElementStyle (i, ElementAttribute.isGlyph) != Boolean.TRUE) {
thisType = UCharacter.getIntPropertyValue (run.elementAt (i), UProperty.JOINING_TYPE); }
else {
thisType = ((Integer) run.getElementStyle (i, ElementAttribute.joiningType)).intValue (); }
if (thisType == UCharacter.JoiningType.TRANSPARENT) {
continue; }
JoiningShapes previousShape;
if ( ( thisType == UCharacter.JoiningType.JOIN_CAUSING
|| thisType == UCharacter.JoiningType.DUAL_JOINING
|| thisType == UCharacter.JoiningType.RIGHT_JOINING)
&& ( previousType == UCharacter.JoiningType.JOIN_CAUSING
|| previousType == UCharacter.JoiningType.DUAL_JOINING
|| previousType == UCharacter.JoiningType.LEFT_JOINING)) {
previousShape = previousJoinsItsRight ? JoiningShapes.medi : JoiningShapes.init;
previousJoinsItsRight = true; }
else {
previousShape = previousJoinsItsRight ? JoiningShapes.fina : JoiningShapes.isol;
previousJoinsItsRight = false; }
if (previousIndex != Integer.MIN_VALUE
&& previousType != UCharacter.JoiningType.JOIN_CAUSING
&& previousType != UCharacter.JoiningType.NON_JOINING) {
run.setElementStyle (previousIndex, joiningShape, previousShape); }
previousIndex = i;
previousType = thisType; }
}
public int firstPass (AttributedRun run, int start, int limit) {
// setJoiningShape needs to see elements at the same bidi level.
while (start < limit) {
int bidiRunLimit = run.getSubrunLimit (start, limit, ElementAttribute.bidiLevel);
setJoiningShape (run, start, bidiRunLimit);
start = bidiRunLimit; }
return super.firstPass (run, start, limit);
}
//----------------------------------------------------- canRenderWithFont ---
// public int canRenderWithFont (Font font, AttributedRun run, int start, int limit, boolean notdefOK)
// throws InvalidFontException, UnsupportedFontException {
// FontData fontData = ((FontImpl) font).getFontData();
//
// if (fontData instanceof OpenTypeFont) {
// return super.canRenderWithFont (font, run, start, limit, notdefOK); }
//
// return 0;
// }
//------------------------------------------------------- OpenType layout ---
protected int setGlyphs (AttributedRun run, int start, int limit)
throws InvalidFontException, UnsupportedFontException, FontLoadingException {
Font font = (Font) run.getElementStyle (start, ElementAttribute.font);
OpenTypeFont otFont = (OpenTypeFont) ((FontImpl) font).getFontData();
while (start < limit) {
if (run. getElementStyle (start, ElementAttribute.isGlyph) == Boolean.TRUE) {
start++;
continue; }
int usv = run.elementAt (start);
if (usv == 0x200D /* ZWJ */ || usv == 0x200C /* ZWNJ */) {
run.remove (start);
limit--;
run.setInterElementStyleBefore (start,
InterElementAttribute.ligatureLevel,
LigatureLevel.NONE);
continue; }
if (CharUtil.isControl (usv)) {
run.remove (start);
limit--;
continue; }
if (((Integer)run.getElementStyle (start, ElementAttribute.bidiLevel)).intValue () % 2 == 1) {
usv = UCharacter.getMirror (usv); }
if ( ! CharUtil.isBase (usv)
|| start + 1 == limit
|| run.getElementStyle (start + 1, ElementAttribute.isGlyph) == Boolean.TRUE
|| ! CharUtil.isCombining (run.elementAt (start + 1))) {
// common case: a single character
int gid = otFont.getGlyphForChar (usv);
run.replace (start, gid);
run.setElementStyle (start, ElementAttribute.isGlyph, Boolean.TRUE);
start++;
continue; }
{ int graphemeLimit = start + 1;
while ( graphemeLimit < limit
&& run.getElementStyle (graphemeLimit, ElementAttribute.isGlyph) != Boolean.TRUE
&& CharUtil.isCombining (run.elementAt (graphemeLimit))) {
graphemeLimit++; }
boolean directMappingHasNotdef = false;
int[] usvs = new int [graphemeLimit - start];
int[] gids = new int [graphemeLimit - start];
for (int i = start; i < graphemeLimit; i++) {
usvs [i - start] = run.elementAt (i);
int gid = otFont.getGlyphForChar (usvs [i - start]);
if (gid == 0) {
directMappingHasNotdef = true; }
gids [i - start] = gid; }
if (directMappingHasNotdef) {
int usvc = CharUtil.compose (usvs, 0, graphemeLimit - start);
if (usvc != -1) {
int gid = otFont.getGlyphForChar (usvc);
if (gid != 0) {
run.replace (start, graphemeLimit, gid);
run.setElementStyle (start, ElementAttribute.isGlyph, Boolean.TRUE);
limit -= (graphemeLimit - start - 1);
start++;
continue; }}}
for (int i = start; i < graphemeLimit; i++) {
run.replace (i, gids [i - start]);
run.setElementStyle (start, ElementAttribute.isGlyph, Boolean.TRUE); }
start = graphemeLimit; }}
return limit;
}
//-------------------------------------------------------------- formatOT ---
protected boolean canFormatOT() {
return true;
}
protected int formatOT (OpenTypeFont otFont, AttributedRun run, int start, int limit, boolean shouldKern)
throws InvalidFontException, UnsupportedFontException, FontLoadingException {
// PRE: r.font [first, last] = one font
// r.direction [first, last] = one direction
// charRun is not defective, i.e. does not start in the
// middle of a default grapheme cluster
Integer bidiLevel = (Integer) run.getElementStyle (start, ElementAttribute.bidiLevel);
int scriptTag = getOTScriptTag ((Integer) run.getElementStyle (start, InFontFormatter.scriptAttribute));
int langTag = getOTLanguageTag ((ULocale) run.getElementStyle (start, ElementAttribute.locale));
// Map characters to glyphs; simply skip existing glyphs
limit = setGlyphs (run, start, limit);
if (otFont.gsub != null) {
// Shape the glyphs
int[][] gsubLookups = LookupsCache.resolveFeatureTag (otFont.gsub, scriptTag, langTag, gsubFeatures);
limit = otFont.gsub.applyLookups (gsubLookups [0], run, start, limit, OTSelectors.everywhere, otFont.gdef); // ccmp
limit = otFont.gsub.applyLookups (gsubLookups [1], run, start, limit, OTSelectors.everywhere, otFont.gdef); // locl
limit = otFont.gsub.applyLookups (gsubLookups [2], run, start, limit, initSelector, otFont.gdef); // init
limit = otFont.gsub.applyLookups (gsubLookups [3], run, start, limit, mediSelector, otFont.gdef); // medi
limit = otFont.gsub.applyLookups (gsubLookups [4], run, start, limit, finaSelector, otFont.gdef); // fina
limit = otFont.gsub.applyLookups (gsubLookups [5], run, start, limit, isolSelector, otFont.gdef); // isol
if (bidiLevel.intValue () % 2 == 1) {
limit = otFont.gsub.applyLookups (gsubLookups [6], run, start, limit, OTSelectors.everywhere, otFont.gdef); } // rtla
limit = otFont.gsub.applyLookups (gsubLookups [7], run, start, limit, OTSelectors.minimumLigatures, otFont.gdef); // rlig
limit = otFont.gsub.applyLookups (gsubLookups [8], run, start, limit, OTSelectors.commonLigatures, otFont.gdef); } // liga
// Position the glyphs
posFromAdvanceWidth (run, otFont, start, limit);
if (otFont.gpos != null) {
int[][] gposLookups = LookupsCache.resolveFeatureTag (otFont.gpos, scriptTag, langTag, gposFeatures);
if (shouldKern)
{
if (gposLookups[0].length != 0)
{
limit = otFont.gpos.applyLookups (gposLookups [0], run, start, limit, OTSelectors.everywhere, otFont.gdef); // kern
} else {
applyKernTable(otFont, run, start, limit);
}
}
limit = otFont.gpos.applyLookups (gposLookups [1], run, start, limit, OTSelectors.everywhere, otFont.gdef); // mark
limit = otFont.gpos.applyLookups (gposLookups [2], run, start, limit, OTSelectors.everywhere, otFont.gdef); } // mkmk
else {
applyKernTable(otFont, run, start, limit);
}
return limit;
}
private static final OTSelector initSelector = new OTSelectorOnElementAttribute (ArabicFormatter.joiningShape, ArabicFormatter.JoiningShapes.init, true);
private static final OTSelector mediSelector = new OTSelectorOnElementAttribute (ArabicFormatter.joiningShape, ArabicFormatter.JoiningShapes.medi, true);
private static final OTSelector finaSelector = new OTSelectorOnElementAttribute (ArabicFormatter.joiningShape, ArabicFormatter.JoiningShapes.fina, true);
private static final OTSelector isolSelector = new OTSelectorOnElementAttribute (ArabicFormatter.joiningShape, ArabicFormatter.JoiningShapes.isol, true);
private static final int[] gsubFeatures = {
Tag.feature_ccmp, //0
Tag.feature_locl, //1
Tag.feature_init, //2
Tag.feature_medi, //3
Tag.feature_fina, //4
Tag.feature_isol, //5
Tag.feature_rtla, //6
Tag.feature_rlig, //7
Tag.feature_liga}; //8
private static final int[] gposFeatures = {
Tag.feature_kern,
Tag.feature_mark,
Tag.feature_mkmk};
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy