Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/font/ExtendedTextSourceLabel.java
38829 views
/*1* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24/*25*26* (C) Copyright IBM Corp. 1998-2003 - All Rights Reserved27*/2829package sun.font;3031import java.awt.Font;32import java.awt.Graphics2D;33import java.awt.Rectangle;34import java.awt.Shape;3536import java.awt.font.FontRenderContext;37import java.awt.font.GlyphJustificationInfo;38import java.awt.font.GlyphMetrics;39import java.awt.font.LineMetrics;40import java.awt.font.TextAttribute;4142import java.awt.geom.AffineTransform;43import java.awt.geom.Point2D;44import java.awt.geom.Rectangle2D;4546import java.util.Map;4748/**49* Default implementation of ExtendedTextLabel.50*/5152// {jbr} I made this class package-private to keep the53// Decoration.Label API package-private.5455/* public */56class ExtendedTextSourceLabel extends ExtendedTextLabel implements Decoration.Label {5758TextSource source;59private Decoration decorator;6061// caches62private Font font;63private AffineTransform baseTX;64private CoreMetrics cm;6566Rectangle2D lb;67Rectangle2D ab;68Rectangle2D vb;69Rectangle2D ib;70StandardGlyphVector gv;71float[] charinfo;7273/**74* Create from a TextSource.75*/76public ExtendedTextSourceLabel(TextSource source, Decoration decorator) {77this.source = source;78this.decorator = decorator;79finishInit();80}8182/**83* Create from a TextSource, optionally using cached data from oldLabel starting at the offset.84* If present oldLabel must have been created from a run of text that includes the text used in85* the new label. Start in source corresponds to logical character offset in oldLabel.86*/87public ExtendedTextSourceLabel(TextSource source, ExtendedTextSourceLabel oldLabel, int offset) {88// currently no optimization.89this.source = source;90this.decorator = oldLabel.decorator;91finishInit();92}9394private void finishInit() {95font = source.getFont();9697Map<TextAttribute, ?> atts = font.getAttributes();98baseTX = AttributeValues.getBaselineTransform(atts);99if (baseTX == null){100cm = source.getCoreMetrics();101} else {102AffineTransform charTX = AttributeValues.getCharTransform(atts);103if (charTX == null) {104charTX = new AffineTransform();105}106font = font.deriveFont(charTX);107108LineMetrics lm = font.getLineMetrics(source.getChars(), source.getStart(),109source.getStart() + source.getLength(), source.getFRC());110cm = CoreMetrics.get(lm);111}112}113114115// TextLabel API116117public Rectangle2D getLogicalBounds() {118return getLogicalBounds(0, 0);119}120121public Rectangle2D getLogicalBounds(float x, float y) {122if (lb == null) {123lb = createLogicalBounds();124}125return new Rectangle2D.Float((float)(lb.getX() + x),126(float)(lb.getY() + y),127(float)lb.getWidth(),128(float)lb.getHeight());129}130131public float getAdvance() {132if (lb == null) {133lb = createLogicalBounds();134}135return (float)lb.getWidth();136}137138public Rectangle2D getVisualBounds(float x, float y) {139if (vb == null) {140vb = decorator.getVisualBounds(this);141}142return new Rectangle2D.Float((float)(vb.getX() + x),143(float)(vb.getY() + y),144(float)vb.getWidth(),145(float)vb.getHeight());146}147148public Rectangle2D getAlignBounds(float x, float y) {149if (ab == null) {150ab = createAlignBounds();151}152return new Rectangle2D.Float((float)(ab.getX() + x),153(float)(ab.getY() + y),154(float)ab.getWidth(),155(float)ab.getHeight());156157}158159public Rectangle2D getItalicBounds(float x, float y) {160if (ib == null) {161ib = createItalicBounds();162}163return new Rectangle2D.Float((float)(ib.getX() + x),164(float)(ib.getY() + y),165(float)ib.getWidth(),166(float)ib.getHeight());167168}169170public Rectangle getPixelBounds(FontRenderContext frc, float x, float y) {171return getGV().getPixelBounds(frc, x, y);172}173174public boolean isSimple() {175return decorator == Decoration.getPlainDecoration() &&176baseTX == null;177}178179public AffineTransform getBaselineTransform() {180return baseTX; // passing internal object, caller must not modify!181}182183public Shape handleGetOutline(float x, float y) {184return getGV().getOutline(x, y);185}186187public Shape getOutline(float x, float y) {188return decorator.getOutline(this, x, y);189}190191public void handleDraw(Graphics2D g, float x, float y) {192g.drawGlyphVector(getGV(), x, y);193}194195public void draw(Graphics2D g, float x, float y) {196decorator.drawTextAndDecorations(this, g, x, y);197}198199/**200* The logical bounds extends from the origin of the glyphvector to the201* position at which a following glyphvector's origin should be placed.202* We always assume glyph vectors are rendered from left to right, so203* the origin is always to the left.204* <p> On a left-to-right run, combining marks and 'ligatured away'205* characters are to the right of their base characters. The charinfo206* array will record the character positions for these 'missing' characters207* as being at the origin+advance of the base glyph, with zero advance.208* (This is not necessarily the same as the glyph position, for example,209* an umlaut glyph may have a position to the left of this point, it depends210* on whether the font was designed so that such glyphs overhang to the left211* of their origin, or whether it presumes some kind of kerning to position212* the glyphs). Anyway, the left of the bounds is the origin of the first213* logical (leftmost) character, and the right is the origin + advance of the214* last logical (rightmost) character.215* <p> On a right-to-left run, these special characters are to the left216* of their base characters. Again, since 'glyph position' has been abstracted217* away, we can use the origin of the leftmost character, and the origin +218* advance of the rightmost character.219* <p> On a mixed run (hindi) we can't rely on the first logical character220* being the leftmost character. However we can again rely on the leftmost221* character origin and the rightmost character + advance.222*/223protected Rectangle2D createLogicalBounds() {224return getGV().getLogicalBounds();225}226227public Rectangle2D handleGetVisualBounds() {228return getGV().getVisualBounds();229}230231/**232* Like createLogicalBounds except ignore leading and logically trailing white space.233* this assumes logically trailing whitespace is also visually trailing.234* Whitespace is anything that has a zero visual width, regardless of its advance.235* <p> We make the same simplifying assumptions as in createLogicalBounds, namely236* that we can rely on the charinfo to shield us from any glyph positioning oddities237* in the font that place the glyph for a character at other than the pos + advance238* of the character to its left. So we no longer need to skip chars with zero239* advance, as their bounds (right and left) are already correct.240*/241protected Rectangle2D createAlignBounds() {242float[] info = getCharinfo();243244float al = 0f;245float at = -cm.ascent;246float aw = 0f;247float ah = cm.ascent + cm.descent;248249if (charinfo == null || charinfo.length == 0) {250return new Rectangle2D.Float(al, at, aw, ah);251}252253boolean lineIsLTR = (source.getLayoutFlags() & 0x8) == 0;254int rn = info.length - numvals;255if (lineIsLTR) {256while (rn > 0 && info[rn+visw] == 0) {257rn -= numvals;258}259}260261if (rn >= 0) {262int ln = 0;263while (ln < rn && ((info[ln+advx] == 0) || (!lineIsLTR && info[ln+visw] == 0))) {264ln += numvals;265}266267al = Math.max(0f, info[ln+posx]);268aw = info[rn+posx] + info[rn+advx] - al;269}270271/*272boolean lineIsLTR = source.lineIsLTR();273int rn = info.length - numvals;274while (rn > 0 && ((info[rn+advx] == 0) || (lineIsLTR && info[rn+visw] == 0))) {275rn -= numvals;276}277278if (rn >= 0) {279int ln = 0;280while (ln < rn && ((info[ln+advx] == 0) || (!lineIsLTR && info[ln+visw] == 0))) {281ln += numvals;282}283284al = Math.max(0f, info[ln+posx]);285aw = info[rn+posx] + info[rn+advx] - al;286}287*/288289return new Rectangle2D.Float(al, at, aw, ah);290}291292public Rectangle2D createItalicBounds() {293float ia = cm.italicAngle;294295Rectangle2D lb = getLogicalBounds();296float l = (float)lb.getMinX();297float t = -cm.ascent;298float r = (float)lb.getMaxX();299float b = cm.descent;300if (ia != 0) {301if (ia > 0) {302l -= ia * (b - cm.ssOffset);303r -= ia * (t - cm.ssOffset);304} else {305l -= ia * (t - cm.ssOffset);306r -= ia * (b - cm.ssOffset);307}308}309return new Rectangle2D.Float(l, t, r - l, b - t);310}311312private final StandardGlyphVector getGV() {313if (gv == null) {314gv = createGV();315}316317return gv;318}319320protected StandardGlyphVector createGV() {321FontRenderContext frc = source.getFRC();322int flags = source.getLayoutFlags();323char[] context = source.getChars();324int start = source.getStart();325int length = source.getLength();326327GlyphLayout gl = GlyphLayout.get(null); // !!! no custom layout engines328gv = gl.layout(font, frc, context, start, length, flags, null); // ??? use textsource329GlyphLayout.done(gl);330331return gv;332}333334// ExtendedTextLabel API335336private static final int posx = 0,337posy = 1,338advx = 2,339advy = 3,340visx = 4,341visy = 5,342visw = 6,343vish = 7;344private static final int numvals = 8;345346public int getNumCharacters() {347return source.getLength();348}349350public CoreMetrics getCoreMetrics() {351return cm;352}353354public float getCharX(int index) {355validate(index);356float[] charinfo = getCharinfo();357int idx = l2v(index) * numvals + posx;358if (charinfo == null || idx >= charinfo.length) {359return 0f;360} else {361return charinfo[idx];362}363}364365public float getCharY(int index) {366validate(index);367float[] charinfo = getCharinfo();368int idx = l2v(index) * numvals + posy;369if (charinfo == null || idx >= charinfo.length) {370return 0f;371} else {372return charinfo[idx];373}374}375376public float getCharAdvance(int index) {377validate(index);378float[] charinfo = getCharinfo();379int idx = l2v(index) * numvals + advx;380if (charinfo == null || idx >= charinfo.length) {381return 0f;382} else {383return charinfo[idx];384}385}386387public Rectangle2D handleGetCharVisualBounds(int index) {388validate(index);389float[] charinfo = getCharinfo();390index = l2v(index) * numvals;391if (charinfo == null || (index+vish) >= charinfo.length) {392return new Rectangle2D.Float();393}394return new Rectangle2D.Float(395charinfo[index + visx],396charinfo[index + visy],397charinfo[index + visw],398charinfo[index + vish]);399}400401public Rectangle2D getCharVisualBounds(int index, float x, float y) {402403Rectangle2D bounds = decorator.getCharVisualBounds(this, index);404if (x != 0 || y != 0) {405bounds.setRect(bounds.getX()+x,406bounds.getY()+y,407bounds.getWidth(),408bounds.getHeight());409}410return bounds;411}412413private void validate(int index) {414if (index < 0) {415throw new IllegalArgumentException("index " + index + " < 0");416} else if (index >= source.getLength()) {417throw new IllegalArgumentException("index " + index + " < " + source.getLength());418}419}420421/*422public int hitTestChar(float x, float y) {423// !!! return index of char hit, for swing424// result is negative for trailing-edge hits425// no italics so no problem at margins.426// for now, ignore y since we assume horizontal text427428// find non-combining char origin to right of x429float[] charinfo = getCharinfo();430431int n = 0;432int e = source.getLength();433while (n < e && charinfo[n + advx] != 0 && charinfo[n + posx] > x) {434n += numvals;435}436float rightx = n < e ? charinfo[n+posx] : charinfo[e - numvals + posx] + charinfo[e - numvals + advx];437438// find non-combining char to left of that char439n -= numvals;440while (n >= 0 && charinfo[n+advx] == 0) {441n -= numvals;442}443float leftx = n >= 0 ? charinfo[n+posx] : 0;444float lefta = n >= 0 ? charinfo[n+advx] : 0;445446n /= numvals;447448boolean left = true;449if (x < leftx + lefta / 2f) {450// left of prev char451} else if (x < (leftx + lefta + rightx) / 2f) {452// right of prev char453left = false;454} else {455// left of follow char456n += 1;457}458459if ((source.getLayoutFlags() & 0x1) != 0) {460n = getNumCharacters() - 1 - n;461left = !left;462}463464return left ? n : -n;465}466*/467468public int logicalToVisual(int logicalIndex) {469validate(logicalIndex);470return l2v(logicalIndex);471}472473public int visualToLogical(int visualIndex) {474validate(visualIndex);475return v2l(visualIndex);476}477478public int getLineBreakIndex(int start, float width) {479float[] charinfo = getCharinfo();480int length = source.getLength();481--start;482while (width >= 0 && ++start < length) {483int cidx = l2v(start) * numvals + advx;484if (cidx >= charinfo.length) {485break; // layout bailed for some reason486}487float adv = charinfo[cidx];488width -= adv;489}490491return start;492}493494public float getAdvanceBetween(int start, int limit) {495float a = 0f;496497float[] charinfo = getCharinfo();498--start;499while (++start < limit) {500int cidx = l2v(start) * numvals + advx;501if (cidx >= charinfo.length) {502break; // layout bailed for some reason503}504a += charinfo[cidx];505}506507return a;508}509510public boolean caretAtOffsetIsValid(int offset) {511// REMIND: improve this implementation512513// Ligature formation can either be done in logical order,514// with the ligature glyph logically preceding the null515// chars; or in visual order, with the ligature glyph to516// the left of the null chars. This method's implementation517// must reflect which strategy is used.518519if (offset == 0 || offset == source.getLength()) {520return true;521}522char c = source.getChars()[source.getStart() + offset];523if (c == '\t' || c == '\n' || c == '\r') { // hack524return true;525}526int v = l2v(offset);527528// If ligatures are always to the left, do this stuff:529//if (!(source.getLayoutFlags() & 0x1) == 0) {530// v += 1;531// if (v == source.getLength()) {532// return true;533// }534//}535536int idx = v * numvals + advx;537float[] charinfo = getCharinfo();538if (charinfo == null || idx >= charinfo.length) {539return false;540} else {541return charinfo[idx] != 0;542}543}544545private final float[] getCharinfo() {546if (charinfo == null) {547charinfo = createCharinfo();548}549return charinfo;550}551552/*553* This takes the glyph info record obtained from the glyph vector and converts it into a similar record554* adjusted to represent character data instead. For economy we don't use glyph info records in this processing.555*556* Here are some constraints:557* - there can be more glyphs than characters (glyph insertion, perhaps based on normalization, has taken place)558* - there can not be fewer glyphs than characters (0xffff glyphs are inserted for characters ligaturized away)559* - each glyph maps to a single character, when multiple glyphs exist for a character they all map to it, but560* no two characters map to the same glyph561* - multiple glyphs mapping to the same character need not be in sequence (thai, tamil have split characters)562* - glyphs may be arbitrarily reordered (Indic reorders glyphs)563* - all glyphs share the same bidi level564* - all glyphs share the same horizontal (or vertical) baseline565* - combining marks visually follow their base character in the glyph array-- i.e. in an rtl gv they are566* to the left of their base character-- and have zero advance.567*568* The output maps this to character positions, and therefore caret positions, via the following assumptions:569* - zero-advance glyphs do not contribute to the advance of their character (i.e. position is ignored), conversely570* if a glyph is to contribute to the advance of its character it must have a non-zero (float) advance571* - no carets can appear between a zero width character and its preceding character, where 'preceding' is572* defined logically.573* - no carets can appear within a split character574* - no carets can appear within a local reordering (i.e. Indic reordering, or non-adjacent split characters)575* - all characters lie on the same baseline, and it is either horizontal or vertical576* - the charinfo is in uniform ltr or rtl order (visual order), since local reorderings and split characters are removed577*578* The algorithm works in the following way:579* 1) we scan the glyphs ltr or rtl based on the bidi run direction580* 2) we can work in place, since we always consume a glyph for each char we write581* a) if the line is ltr, we start writing at position 0 until we finish, there may be leftver space582* b) if the line is rtl and 1-1, we start writing at position numChars/glyphs - 1 until we finish at 0583* c) otherwise if we don't finish at 0, we have to copy the data down584* 3) we consume clusters in the following way:585* a) the first element is always consumed586* b) subsequent elements are consumed if:587* i) their advance is zero588* ii) their character index <= the character index of any character seen in this cluster589* iii) the minimum character index seen in this cluster isn't adjacent to the previous cluster590* c) character data is written as follows for horizontal lines (x/y and w/h are exchanged on vertical lines)591* i) the x position is the position of the leftmost glyph whose advance is not zero592* ii)the y position is the baseline593* iii) the x advance is the distance to the maximum x + adv of all glyphs whose advance is not zero594* iv) the y advance is the baseline595* v) vis x,y,w,h tightly encloses the vis x,y,w,h of all the glyphs with nonzero w and h596* 4) we can make some simple optimizations if we know some things:597* a) if the mapping is 1-1, unidirectional, and there are no zero-adv glyphs, we just return the glyphinfo598* b) if the mapping is 1-1, unidirectional, we just adjust the remaining glyphs to originate at right/left of the base599* c) if the mapping is 1-1, we compute the base position and advance as we go, then go back to adjust the remaining glyphs600* d) otherwise we keep separate track of the write position as we do (c) since no glyph in the cluster may be in the601* position we are writing.602* e) most clusters are simply the single base glyph in the same position as its character, so we try to avoid603* copying its data unnecessarily.604* 5) the glyph vector ought to provide access to these 'global' attributes to enable these optimizations. A single605* int with flags set is probably ok, we could also provide accessors for each attribute. This doesn't map to606* the GlyphMetrics flags very well, so I won't attempt to keep them similar. It might be useful to add those607* in addition to these.608* int FLAG_HAS_ZERO_ADVANCE_GLYPHS = 1; // set if there are zero-advance glyphs609* int FLAG_HAS_NONUNIFORM_ORDER = 2; // set if some glyphs are rearranged out of character visual order610* int FLAG_HAS_SPLIT_CHARACTERS = 4; // set if multiple glyphs per character611* int getDescriptionFlags(); // return an int containing the above flags612* boolean hasZeroAdvanceGlyphs();613* boolean hasNonuniformOrder();614* boolean hasSplitCharacters();615* The optimized cases in (4) correspond to values 0, 1, 3, and 7 returned by getDescriptionFlags().616*/617protected float[] createCharinfo() {618StandardGlyphVector gv = getGV();619float[] glyphinfo = null;620try {621glyphinfo = gv.getGlyphInfo();622}623catch (Exception e) {624System.out.println(source);625}626627/*628if ((gv.getDescriptionFlags() & 0x7) == 0) {629return glyphinfo;630}631*/632633int numGlyphs = gv.getNumGlyphs();634if (numGlyphs == 0) {635return glyphinfo;636}637int[] indices = gv.getGlyphCharIndices(0, numGlyphs, null);638639boolean DEBUG = false;640if (DEBUG) {641System.err.println("number of glyphs: " + numGlyphs);642for (int i = 0; i < numGlyphs; ++i) {643System.err.println("g: " + i +644", x: " + glyphinfo[i*numvals+posx] +645", a: " + glyphinfo[i*numvals+advx] +646", n: " + indices[i]);647}648}649650int minIndex = indices[0]; // smallest index seen this cluster651int maxIndex = minIndex; // largest index seen this cluster652int nextMin = 0; // expected smallest index for this cluster653int cp = 0; // character position654int cx = 0; // character index (logical)655int gp = 0; // glyph position656int gx = 0; // glyph index (visual)657int gxlimit = numGlyphs; // limit of gx, when we reach this we're done658int pdelta = numvals; // delta for incrementing positions659int xdelta = 1; // delta for incrementing indices660661boolean ltr = (source.getLayoutFlags() & 0x1) == 0;662if (!ltr) {663minIndex = indices[numGlyphs - 1];664maxIndex = minIndex;665nextMin = 0; // still logical666cp = glyphinfo.length - numvals;667cx = 0; // still logical668gp = glyphinfo.length - numvals;669gx = numGlyphs - 1;670gxlimit = -1;671pdelta = -numvals;672xdelta = -1;673}674675/*676// to support vertical, use 'ixxxx' indices and swap horiz and vertical components677if (source.isVertical()) {678iposx = posy;679iposy = posx;680iadvx = advy;681iadvy = advx;682ivisx = visy;683ivisy = visx;684ivish = visw;685ivisw = vish;686} else {687// use standard values688}689*/690691// use intermediates to reduce array access when we need to692float cposl = 0, cposr = 0, cvisl = 0, cvist = 0, cvisr = 0, cvisb = 0;693float baseline = 0;694695// record if we have to copy data even when no cluster696boolean mustCopy = false;697698while (gx != gxlimit) {699// start of new cluster700boolean haveCopy = false;701int clusterExtraGlyphs = 0;702703minIndex = indices[gx];704maxIndex = minIndex;705706// advance to next glyph707gx += xdelta;708gp += pdelta;709710/*711while (gx != gxlimit && (glyphinfo[gp + advx] == 0 ||712minIndex != nextMin || indices[gx] <= maxIndex)) {713*/714while (gx != gxlimit &&715((glyphinfo[gp + advx] == 0) ||716(minIndex != nextMin) ||717(indices[gx] <= maxIndex) ||718(maxIndex - minIndex > clusterExtraGlyphs))) {719// initialize base data first time through, using base glyph720if (!haveCopy) {721int gps = gp - pdelta;722723cposl = glyphinfo[gps + posx];724cposr = cposl + glyphinfo[gps + advx];725cvisl = glyphinfo[gps + visx];726cvist = glyphinfo[gps + visy];727cvisr = cvisl + glyphinfo[gps + visw];728cvisb = cvist + glyphinfo[gps + vish];729730haveCopy = true;731}732733// have an extra glyph in this cluster734++clusterExtraGlyphs;735736// adjust advance only if new glyph has non-zero advance737float radvx = glyphinfo[gp + advx];738if (radvx != 0) {739float rposx = glyphinfo[gp + posx];740cposl = Math.min(cposl, rposx);741cposr = Math.max(cposr, rposx + radvx);742}743744// adjust visible bounds only if new glyph has non-empty bounds745float rvisw = glyphinfo[gp + visw];746if (rvisw != 0) {747float rvisx = glyphinfo[gp + visx];748float rvisy = glyphinfo[gp + visy];749cvisl = Math.min(cvisl, rvisx);750cvist = Math.min(cvist, rvisy);751cvisr = Math.max(cvisr, rvisx + rvisw);752cvisb = Math.max(cvisb, rvisy + glyphinfo[gp + vish]);753}754755// adjust min, max index756minIndex = Math.min(minIndex, indices[gx]);757maxIndex = Math.max(maxIndex, indices[gx]);758759// get ready to examine next glyph760gx += xdelta;761gp += pdelta;762}763// done with cluster, gx and gp are set for next glyph764765if (DEBUG) {766System.out.println("minIndex = " + minIndex + ", maxIndex = " + maxIndex);767}768769nextMin = maxIndex + 1;770771// do common character adjustments772glyphinfo[cp + posy] = baseline;773glyphinfo[cp + advy] = 0;774775if (haveCopy) {776// save adjustments to the base character777glyphinfo[cp + posx] = cposl;778glyphinfo[cp + advx] = cposr - cposl;779glyphinfo[cp + visx] = cvisl;780glyphinfo[cp + visy] = cvist;781glyphinfo[cp + visw] = cvisr - cvisl;782glyphinfo[cp + vish] = cvisb - cvist;783784// compare number of chars read with number of glyphs read.785// if more glyphs than chars, set mustCopy to true, as we'll always have786// to copy the data from here on out.787if (maxIndex - minIndex < clusterExtraGlyphs) {788mustCopy = true;789}790791// Fix the characters that follow the base character.792// New values are all the same. Note we fix the number of characters793// we saw, not the number of glyphs we saw.794if (minIndex < maxIndex) {795if (!ltr) {796// if rtl, characters to left of base, else to right. reuse cposr.797cposr = cposl;798}799cvisr -= cvisl; // reuse, convert to deltas.800cvisb -= cvist;801802int iMinIndex = minIndex, icp = cp / 8;803804while (minIndex < maxIndex) {805++minIndex;806cx += xdelta;807cp += pdelta;808809if (cp < 0 || cp >= glyphinfo.length) {810if (DEBUG) System.out.println("minIndex = " + iMinIndex + ", maxIndex = " + maxIndex + ", cp = " + icp);811}812813glyphinfo[cp + posx] = cposr;814glyphinfo[cp + posy] = baseline;815glyphinfo[cp + advx] = 0;816glyphinfo[cp + advy] = 0;817glyphinfo[cp + visx] = cvisl;818glyphinfo[cp + visy] = cvist;819glyphinfo[cp + visw] = cvisr;820glyphinfo[cp + vish] = cvisb;821}822}823824// no longer using this copy825haveCopy = false;826} else if (mustCopy) {827// out of synch, so we have to copy all the time now828int gpr = gp - pdelta;829830glyphinfo[cp + posx] = glyphinfo[gpr + posx];831glyphinfo[cp + advx] = glyphinfo[gpr + advx];832glyphinfo[cp + visx] = glyphinfo[gpr + visx];833glyphinfo[cp + visy] = glyphinfo[gpr + visy];834glyphinfo[cp + visw] = glyphinfo[gpr + visw];835glyphinfo[cp + vish] = glyphinfo[gpr + vish];836}837// else glyphinfo is already at the correct character position, and is unchanged, so just leave it838839// reset for new cluster840cp += pdelta;841cx += xdelta;842}843844if (mustCopy && !ltr) {845// data written to wrong end of array, need to shift down846847cp -= pdelta; // undo last increment, get start of valid character data in array848System.arraycopy(glyphinfo, cp, glyphinfo, 0, glyphinfo.length - cp);849}850851if (DEBUG) {852char[] chars = source.getChars();853int start = source.getStart();854int length = source.getLength();855System.out.println("char info for " + length + " characters");856for(int i = 0; i < length * numvals;) {857System.out.println(" ch: " + Integer.toHexString(chars[start + v2l(i / numvals)]) +858" x: " + glyphinfo[i++] +859" y: " + glyphinfo[i++] +860" xa: " + glyphinfo[i++] +861" ya: " + glyphinfo[i++] +862" l: " + glyphinfo[i++] +863" t: " + glyphinfo[i++] +864" w: " + glyphinfo[i++] +865" h: " + glyphinfo[i++]);866}867}868869return glyphinfo;870}871872/**873* Map logical character index to visual character index.874* <p>875* This ignores hindi reordering. @see createCharinfo876*/877protected int l2v(int index) {878return (source.getLayoutFlags() & 0x1) == 0 ? index : source.getLength() - 1 - index;879}880881/**882* Map visual character index to logical character index.883* <p>884* This ignores hindi reordering. @see createCharinfo885*/886protected int v2l(int index) {887return (source.getLayoutFlags() & 0x1) == 0 ? index : source.getLength() - 1 - index;888}889890public TextLineComponent getSubset(int start, int limit, int dir) {891return new ExtendedTextSourceLabel(source.getSubSource(start, limit-start, dir), decorator);892}893894public String toString() {895if (true) {896return source.toString(source.WITHOUT_CONTEXT);897}898StringBuffer buf = new StringBuffer();899buf.append(super.toString());900buf.append("[source:");901buf.append(source.toString(source.WITHOUT_CONTEXT));902buf.append(", lb:");903buf.append(lb);904buf.append(", ab:");905buf.append(ab);906buf.append(", vb:");907buf.append(vb);908buf.append(", gv:");909buf.append(gv);910buf.append(", ci: ");911if (charinfo == null) {912buf.append("null");913} else {914buf.append(charinfo[0]);915for (int i = 1; i < charinfo.length;) {916buf.append(i % numvals == 0 ? "; " : ", ");917buf.append(charinfo[i]);918}919}920buf.append("]");921922return buf.toString();923}924925//public static ExtendedTextLabel create(TextSource source) {926// return new ExtendedTextSourceLabel(source);927//}928929public int getNumJustificationInfos() {930return getGV().getNumGlyphs();931}932933934public void getJustificationInfos(GlyphJustificationInfo[] infos, int infoStart, int charStart, int charLimit) {935// This simple implementation only uses spaces for justification.936// Since regular characters aren't justified, we don't need to deal with937// special infos for combining marks or ligature substitution glyphs.938// added character justification for kanjii only 2/22/98939940StandardGlyphVector gv = getGV();941942float[] charinfo = getCharinfo();943944float size = gv.getFont().getSize2D();945946GlyphJustificationInfo nullInfo =947new GlyphJustificationInfo(0,948false, GlyphJustificationInfo.PRIORITY_NONE, 0, 0,949false, GlyphJustificationInfo.PRIORITY_NONE, 0, 0);950951GlyphJustificationInfo spaceInfo =952new GlyphJustificationInfo(size,953true, GlyphJustificationInfo.PRIORITY_WHITESPACE, 0, size,954true, GlyphJustificationInfo.PRIORITY_WHITESPACE, 0, size / 4f);955956GlyphJustificationInfo kanjiInfo =957new GlyphJustificationInfo(size,958true, GlyphJustificationInfo.PRIORITY_INTERCHAR, size, size,959false, GlyphJustificationInfo.PRIORITY_NONE, 0, 0);960961char[] chars = source.getChars();962int offset = source.getStart();963964// assume data is 1-1 and either all rtl or all ltr, for now965966int numGlyphs = gv.getNumGlyphs();967int minGlyph = 0;968int maxGlyph = numGlyphs;969boolean ltr = (source.getLayoutFlags() & 0x1) == 0;970if (charStart != 0 || charLimit != source.getLength()) {971if (ltr) {972minGlyph = charStart;973maxGlyph = charLimit;974} else {975minGlyph = numGlyphs - charLimit;976maxGlyph = numGlyphs - charStart;977}978}979980for (int i = 0; i < numGlyphs; ++i) {981GlyphJustificationInfo info = null;982if (i >= minGlyph && i < maxGlyph) {983if (charinfo[i * numvals + advx] == 0) { // combining marks don't justify984info = nullInfo;985} else {986int ci = v2l(i); // 1-1 assumption again987char c = chars[offset + ci];988if (Character.isWhitespace(c)) {989info = spaceInfo;990// CJK, Hangul, CJK Compatibility areas991} else if (c >= 0x4e00 &&992(c < 0xa000) ||993(c >= 0xac00 && c < 0xd7b0) ||994(c >= 0xf900 && c < 0xfb00)) {995info = kanjiInfo;996} else {997info = nullInfo;998}999}1000}1001infos[infoStart + i] = info;1002}1003}10041005public TextLineComponent applyJustificationDeltas(float[] deltas, int deltaStart, boolean[] flags) {10061007// when we justify, we need to adjust the charinfo since spaces1008// change their advances. preserve the existing charinfo.10091010float[] newCharinfo = (float[])getCharinfo().clone();10111012// we only push spaces, so never need to rejustify1013flags[0] = false;10141015// preserve the existing gv.10161017StandardGlyphVector newgv = (StandardGlyphVector)getGV().clone();1018float[] newPositions = newgv.getGlyphPositions(null);1019int numGlyphs = newgv.getNumGlyphs();10201021/*1022System.out.println("oldgv: " + getGV() + ", newgv: " + newgv);1023System.out.println("newpositions: " + newPositions);1024for (int i = 0; i < newPositions.length; i += 2) {1025System.out.println("[" + (i/2) + "] " + newPositions[i] + ", " + newPositions[i+1]);1026}10271028System.out.println("deltas: " + deltas + " start: " + deltaStart);1029for (int i = deltaStart; i < deltaStart + numGlyphs; i += 2) {1030System.out.println("[" + (i/2) + "] " + deltas[i] + ", " + deltas[i+1]);1031}1032*/10331034char[] chars = source.getChars();1035int offset = source.getStart();10361037// accumulate the deltas to adjust positions and advances.1038// handle whitespace by modifying advance,1039// handle everything else by modifying position before and after10401041float deltaPos = 0;1042for (int i = 0; i < numGlyphs; ++i) {1043if (Character.isWhitespace(chars[offset + v2l(i)])) {1044newPositions[i*2] += deltaPos;10451046float deltaAdv = deltas[deltaStart + i*2] + deltas[deltaStart + i*2 + 1];10471048newCharinfo[i * numvals + posx] += deltaPos;1049newCharinfo[i * numvals + visx] += deltaPos;1050newCharinfo[i * numvals + advx] += deltaAdv;10511052deltaPos += deltaAdv;1053} else {1054deltaPos += deltas[deltaStart + i*2];10551056newPositions[i*2] += deltaPos;1057newCharinfo[i * numvals + posx] += deltaPos;1058newCharinfo[i * numvals + visx] += deltaPos;10591060deltaPos += deltas[deltaStart + i*2 + 1];1061}1062}1063newPositions[numGlyphs * 2] += deltaPos;10641065newgv.setGlyphPositions(newPositions);10661067/*1068newPositions = newgv.getGlyphPositions(null);1069System.out.println(">> newpositions: " + newPositions);1070for (int i = 0; i < newPositions.length; i += 2) {1071System.out.println("[" + (i/2) + "] " + newPositions[i] + ", " + newPositions[i+1]);1072}1073*/10741075ExtendedTextSourceLabel result = new ExtendedTextSourceLabel(source, decorator);1076result.gv = newgv;1077result.charinfo = newCharinfo;10781079return result;1080}1081}108210831084