![JAR search and dependency download from the Maven repository](/logo.png)
com.adobe.fontengine.inlineformatting.infontformatting.IndicFormatter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
/*
* File: IndicFormatter.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 java.util.ArrayList;
import com.adobe.agl.util.ULocale;
import com.adobe.fontengine.CharUtil;
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.OpenTypeFont;
import com.adobe.fontengine.font.opentype.Tag;
import com.adobe.fontengine.inlineformatting.AttributedRun;
import com.adobe.fontengine.inlineformatting.ElementAttribute;
/** In-font formatter for OpenType layout on Indic orthographies. */
abstract class IndicFormatter extends GenericFormatter {
/** Enumeration of the possible shapes.
*/
final public static class Shape {
private String name;
private Shape (String name) {
this.name = name;
}
public String toString () {
return name;
}
public static final Shape any = new Shape ("any");
public static final Shape live = new Shape ("live");
public static final Shape dead = new Shape ("dead");
public static final Shape half = new Shape ("half");
public static final Shape explicitVirama = new Shape ("explicitVirama");
public static final Shape rephCons = new Shape ("rephCons");
public static final Shape rephVowel = new Shape ("rephVowel");
public static final Shape vattu = new Shape ("vattu");
public static final Shape subjoins = new Shape ("subjoins");
public static final Shape postjoins = new Shape ("postjoins");
}
/** Attribute to carry the Indic shape.
* Its value must be a Shape.
*/
private static final ElementAttribute indicShape
= new ElementAttribute ("indicShape");
/** Enumeration of the possible positions.
*/
final public static class Position {
private String name;
private Position (String name) {
this.name = name;
}
public String toString () {
return name;
}
public static final Position any = new Position ("any");
public static final Position left = new Position ("left");
public static final Position topMatra = new Position ("topMatra");
public static final Position topOther = new Position ("topOther");
public static final Position bottom = new Position ("bottom");
public static final Position rightMatra = new Position ("rightMatra");
public static final Position rightOther = new Position ("rightOther");
}
abstract protected int nukta ();
abstract protected int virama ();
abstract protected int splitVowelsAndNormalize (AttributedRun run, int start, int limit);
abstract protected Position getPosition (int usv);
abstract protected boolean isConsonant (int usv);
abstract protected boolean hasNukta (int usv);
abstract protected int removeNukta (int usv);
abstract protected boolean isMark (int usv);
abstract protected boolean isIndependentVowel (int usv);
abstract protected Shape rephLike (int usv);
abstract protected boolean subjoins (int usv);
abstract protected boolean postjoins (int usv);
abstract protected boolean postjoinsIndependentVowels (int usv);
final class AksaraPart {
public int usv;
public int nuktaCount;
public Shape shape;
public int nbGlyphs () {
return 1 + nuktaCount + (shape != Shape.live ? 1 : 0);
}
public int addGlyph (int[] glyphIds, Shape[] glyphForms, int glyphCount,
int ch, Shape shape, OpenTypeFont font)
throws InvalidFontException, UnsupportedFontException {
glyphIds [glyphCount] = font.getGlyphForChar (ch);
glyphForms [glyphCount] = shape;
return glyphCount + 1;
}
public int toGlyphs (int[] glyphIds, Shape[] glyphForms, int glyphCount,
OpenTypeFont font)
throws InvalidFontException, UnsupportedFontException {
glyphCount = addGlyph (glyphIds, glyphForms, glyphCount,
usv, shape, font);
for (int i = 0; i < nuktaCount; i++) {
glyphCount = addGlyph (glyphIds, glyphForms, glyphCount,
nukta (), Shape.any, font); }
if (shape != Shape.live) {
glyphCount = addGlyph (glyphIds, glyphForms, glyphCount,
virama (), shape, font); }
return glyphCount;
}
}
final class Aksara {
public int start;
public int firstMark;
public int limit;
public ArrayList /**/ parts;
public Aksara () {
reset ();
}
public void reset () {
parts = new ArrayList ();
}
public int nbGlyphs () {
int nbGlyphs = limit - firstMark;
for (int i = 0; i < parts.size (); i++) {
nbGlyphs += ((AksaraPart) parts.get (i)).nbGlyphs (); }
return nbGlyphs;
}
public int addGlyph (int[] glyphIds, Shape[] glyphForms, int glyphCount,
int ch, Shape shape, OpenTypeFont font)
throws InvalidFontException, UnsupportedFontException {
glyphIds [glyphCount] = font.getGlyphForChar (ch);
glyphForms [glyphCount] = shape;
return glyphCount + 1;
}
public int toGlyphs (AttributedRun run, OpenTypeFont font)
throws InvalidFontException, UnsupportedFontException {
int [] glyphIds = new int [nbGlyphs ()];
Shape [] glyphForms = new Shape [glyphIds.length];
int glyphCount = 0;
for (int i = firstMark; i < limit; i++) {
int usv = run.elementAt (i);
if (getPosition (usv) == Position.left) {
glyphCount = addGlyph (glyphIds, glyphForms, glyphCount,
usv, Shape.any, font); }}
AksaraPart firstPart = null, lastPart = null;
for (int i = 0; i < parts.size (); i++) {
AksaraPart part = ((AksaraPart) parts.get (i));
if (firstPart == null) {
firstPart = part; }
lastPart = part;
if (part.shape != Shape.rephCons && part.shape != Shape.rephVowel && part.shape != Shape.postjoins) {
glyphCount = part.toGlyphs (glyphIds, glyphForms, glyphCount, font); }}
for (int i = firstMark; i < limit; i++) {
int usv = run.elementAt (i);
if (getPosition (usv) == Position.bottom) {
glyphCount = addGlyph (glyphIds, glyphForms, glyphCount,
usv, Shape.any, font); }}
for (int i = firstMark; i < limit; i++) {
int usv = run.elementAt (i);
if (getPosition (usv) == Position.topMatra) {
glyphCount = addGlyph (glyphIds, glyphForms, glyphCount,
usv, Shape.any, font); }}
if (firstPart.shape == Shape.rephCons) {
glyphCount = firstPart.toGlyphs (glyphIds, glyphForms, glyphCount, font); }
if (lastPart.shape == Shape.postjoins) {
glyphCount = lastPart.toGlyphs (glyphIds, glyphForms, glyphCount, font); }
for (int i = firstMark; i < limit; i++) {
int usv = run.elementAt (i);
if (getPosition (usv) == Position.rightMatra) {
glyphCount = addGlyph (glyphIds, glyphForms, glyphCount,
usv, Shape.any, font); }}
if (firstPart.shape == Shape.rephVowel) {
glyphCount = firstPart.toGlyphs (glyphIds, glyphForms, glyphCount, font); }
for (int i = firstMark; i < limit; i++) {
int usv = run.elementAt (i);
if (getPosition (usv) == Position.topOther) {
glyphCount = addGlyph (glyphIds, glyphForms, glyphCount,
usv, Shape.any, font); }}
for (int i = firstMark; i < limit; i++) {
int usv = run.elementAt (i);
if (getPosition (usv) == Position.rightOther) {
glyphCount = addGlyph (glyphIds, glyphForms, glyphCount,
usv, Shape.any, font); }}
int [] positions = new int [limit - start];
for (int i = 0; i < positions.length; i++) {
positions [i] = start + i; }
run.replace (positions, glyphIds);
run.setElementStyle (start, start + glyphIds.length,
ElementAttribute.isGlyph, java.lang.Boolean.TRUE);
for (int i = 0; i < glyphIds.length; i++) {
run.setElementStyle (start + i, indicShape, glyphForms [i]); }
return start + glyphIds.length;
}
}
private int processAksaraMarks (AttributedRun run, int start, int limit,
Aksara aksara) {
aksara.firstMark = start;
while (start < limit && isMark (run.elementAt (start))) {
start++; }
aksara.limit = start;
return start;
}
private int processAksaras (AttributedRun run, int start, int limit,
OpenTypeFont font)
throws InvalidFontException, UnsupportedFontException {
Aksara aksara = new Aksara ();
while (start < limit) {
aksara.reset ();
aksara.start = start;
if (isConsonant (run.elementAt (start))) {
while (start < limit && isConsonant (run.elementAt (start))) {
AksaraPart part = new AksaraPart ();
part.shape = Shape.live;
if (hasNukta (run.elementAt (start))) {
part.nuktaCount = 1;
part.usv = removeNukta (run.elementAt (start)); }
else {
part.nuktaCount = 0;
part.usv = run.elementAt (start); }
start++;
while (start < limit && nukta () == run.elementAt (start)) {
part.nuktaCount++;
start++;}
if (start < limit && virama () == run.elementAt (start)) {
part.shape = Shape.dead;
start++;
if (start < limit && 0x200D /*ZWJ*/ == run.elementAt (start)) {
part.shape = Shape.half;
start++; }
else if (start < limit && 0x200C /*ZWNJ*/ == run.elementAt (start)) {
part.shape = Shape.explicitVirama;
start++; }}
aksara.parts.add (part);
if (part.shape == Shape.live) {
break; }}
if (aksara.parts.size () > 1) {
AksaraPart firstPart = (AksaraPart) aksara.parts.get (0);
Shape rephShape = rephLike (firstPart.usv);
if (rephShape != Shape.any
&& firstPart.shape == Shape.dead) {
firstPart.shape = rephShape; }}
for (int i = 1; i < aksara.parts.size (); i++) {
AksaraPart part = (AksaraPart) aksara.parts.get (i);
AksaraPart prevPart = (AksaraPart) aksara.parts.get (i-1);
if (subjoins (part.usv) && (part.shape == Shape.dead || part.shape == Shape.live) && (prevPart.shape == Shape.dead)) {
if (part.shape == Shape.live) {
prevPart.shape = Shape.live; }
part.shape = Shape.subjoins; }}
if (aksara.parts.size () > 1) {
AksaraPart part = (AksaraPart) aksara.parts.get (aksara.parts.size () - 1);
AksaraPart prevPart = (AksaraPart) aksara.parts.get (aksara.parts.size () - 2);
if (postjoins (part.usv) && (part.shape == Shape.dead || part.shape == Shape.live) && (prevPart.shape == Shape.dead)) {
if (part.shape == Shape.live) {
prevPart.shape = Shape.live; }
part.shape = Shape.postjoins; }}
start = processAksaraMarks (run, start, limit, aksara); }
else if (isIndependentVowel (run.elementAt (start))) {
AksaraPart part = new AksaraPart ();
part.shape = Shape.live;
part.usv = run.elementAt (start);
part.nuktaCount = 0;
aksara.parts.add (part);
start++;
while ( start + 1 < limit
&& virama () == run.elementAt (start)
&& postjoinsIndependentVowels (run.elementAt (start + 1))) {
part = new AksaraPart ();
part.shape = Shape.postjoins;
part.usv = run.elementAt (start + 1);
part.nuktaCount = 0;
aksara.parts.add (part);
start += 2; }
start = processAksaraMarks (run, start, limit, aksara); }
else {
AksaraPart part = new AksaraPart ();
part.shape = Shape.live;
part.usv = run.elementAt (start);
part.nuktaCount = 0;
aksara.parts.add (part);
start++;
aksara.firstMark = start;
aksara.limit = start; }
int newStart = aksara.toGlyphs (run, font);
limit += newStart - start;
start = newStart; }
return limit;
}
protected int canRenderWithFont (OpenTypeFont font, AttributedRun run,
int start, int limit, boolean notdefOk)
throws InvalidFontException, UnsupportedFontException {
int usv = run.elementAt (start);
if (CharUtil.isControl (usv)) {
return 1; }
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 = font.getGlyphForChar (usv);
return (gid == 0) ? 0 : start + 1; }
{ int graphemeLimit = start + 1;
while ( graphemeLimit < limit
&& run.getElementStyle (graphemeLimit, ElementAttribute.isGlyph) == Boolean.FALSE
&& 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 = font.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 = font.getGlyphForChar (usvc);
if (gid != 0) {
return graphemeLimit - start; }}}
if (notdefOk) {
return graphemeLimit - start; }
else {
return 0; }}
}
protected boolean canFormatOT() {
return true;
}
protected int formatOT (OpenTypeFont otFont, AttributedRun run, int start, int limit, boolean shouldKern)
throws InvalidFontException, UnsupportedFontException {
// 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
int scriptTag = getOTScriptTag ((Integer) run.getElementStyle (start, InFontFormatter.scriptAttribute));
int langTag = getOTLanguageTag ((ULocale) run.getElementStyle (start, ElementAttribute.locale));
limit = splitVowelsAndNormalize (run, start, limit);
// Map characters to glyphs; simply skip existing glyphs
limit = processAksaras (run, start, limit, otFont);
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, everywhere, otFont.gdef); // nukt
limit = otFont.gsub.applyLookups (gsubLookups [1], run, start, limit, akhnSelector, otFont.gdef); // akhn
limit = otFont.gsub.applyLookups (gsubLookups [2], run, start, limit, rphfSelector, otFont.gdef); // rphf
limit = otFont.gsub.applyLookups (gsubLookups [3], run, start, limit, blwfSelector, otFont.gdef); // blwf
limit = otFont.gsub.applyLookups (gsubLookups [11], run, start, limit, pstfSelector, otFont.gdef); // pstf
limit = otFont.gsub.applyLookups (gsubLookups [4], run, start, limit, halfSelector, otFont.gdef); // half
limit = otFont.gsub.applyLookups (gsubLookups [5], run, start, limit, everywhere, otFont.gdef); // vatu
limit = otFont.gsub.applyLookups (gsubLookups [6], run, start, limit, everywhere, otFont.gdef); // pres
limit = otFont.gsub.applyLookups (gsubLookups [7], run, start, limit, everywhere, otFont.gdef); // abvs
limit = otFont.gsub.applyLookups (gsubLookups [8], run, start, limit, everywhere, otFont.gdef); // blws
limit = otFont.gsub.applyLookups (gsubLookups [9], run, start, limit, everywhere, otFont.gdef); // psts
limit = otFont.gsub.applyLookups (gsubLookups [10], run, start, limit, everywhere, otFont.gdef); } // haln
// Position the glyphs
posFromAdvanceWidth (run, otFont, start, limit);
if (otFont.gpos != null) {
int[][] gposLookups = LookupsCache.resolveFeatureTag (otFont.gpos, scriptTag, langTag, gposFeatures);
limit = otFont.gpos.applyLookups (gposLookups [0], run, start, limit, everywhere, otFont.gdef); // abvm
limit = otFont.gpos.applyLookups (gposLookups [1], run, start, limit, everywhere, otFont.gdef); // blwm
limit = otFont.gpos.applyLookups (gposLookups [2], run, start, limit, everywhere, otFont.gdef); } // dist
return limit;
}
private static final OTSelector everywhere = new OTSelector ();
private static final OTSelector akhnSelector = new OTSelector () {
public boolean isApplied (AttributedRun run, int position) {
Object v = run.getElementStyle (position, indicShape);
return ( v != Shape.explicitVirama
&& v != Shape.half);
}
};
private static final OTSelector rphfSelector = new OTSelector () {
public boolean isApplied (AttributedRun run, int position) {
Object v = run.getElementStyle (position, indicShape);
return ( v == Shape.rephCons
|| v == Shape.rephVowel);
}
};
private static final OTSelector blwfSelector = new OTSelectorOnElementAttribute (indicShape, Shape.subjoins, true);
private static final OTSelector pstfSelector = new OTSelectorOnElementAttribute (indicShape, Shape.postjoins, true);
private static final OTSelector halfSelector = new OTSelector () {
public boolean isApplied (AttributedRun run, int position) {
Object v = run.getElementStyle (position, indicShape);
return v != Shape.explicitVirama;
}
};
static final int[] gsubFeatures = {
Tag.feature_nukt,
Tag.feature_akhn,
Tag.feature_rphf,
Tag.feature_blwf,
Tag.feature_half,
Tag.feature_vatu,
Tag.feature_pres,
Tag.feature_abvs,
Tag.feature_blws,
Tag.feature_psts,
Tag.feature_haln,
Tag.feature_pstf};
static final int[] gposFeatures = {
Tag.feature_abvm,
Tag.feature_blwm,
Tag.feature_dist};
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy