1040 lines · 35.4 KB · Java
RawDownload
      //    }      //}       return getCharinfo()[v * numvals + advx] != 0;  }   private final float[] getCharinfo() {    if (charinfo == null) {      charinfo = createCharinfo();    }    return charinfo;  } /** This takes the glyph info record obtained from the glyph vector and converts it into a similar record* adjusted to represent character data instead.  For economy we don't use glyph info records in this processing.** Here are some constraints:* - there can be more glyphs than characters (glyph insertion, perhaps based on normalization, has taken place)* - there can not be fewer glyphs than characters (0xffff glyphs are inserted for characters ligaturized away)* - each glyph maps to a single character, when multiple glyphs exist for a character they all map to it, but*   no two characters map to the same glyph* - multiple glyphs mapping to the same character need not be in sequence (thai, tamil have split characters)* - glyphs may be arbitrarily reordered (Indic reorders glyphs)* - all glyphs share the same bidi level* - all glyphs share the same horizontal (or vertical) baseline* - combining marks visually follow their base character in the glyph array-- i.e. in an rtl gv they are*   to the left of their base character-- and have zero advance.** The output maps this to character positions, and therefore caret positions, via the following assumptions:* - zero-advance glyphs do not contribute to the advance of their character (i.e. position is ignored), conversely*   if a glyph is to contribute to the advance of its character it must have a non-zero (float) advance* - no carets can appear between a zero width character and its preceeding character, where 'preceeding' is*   defined logically.* - no carets can appear within a split character* - no carets can appear within a local reordering (i.e. Indic reordering, or non-adjacent split characters)* - all characters lie on the same baseline, and it is either horizontal or vertical* - the charinfo is in uniform ltr or rtl order (visual order), since local reorderings and split characters are removed** The algorithm works in the following way:* 1) we scan the glyphs ltr or rtl based on the bidi run direction* 2) we can work in place, since we always consume a glyph for each char we write*    a) if the line is ltr, we start writing at position 0 until we finish, there may be leftver space*    b) if the line is rtl and 1-1, we start writing at position numChars/glyphs - 1 until we finish at 0*    c) otherwise if we don't finish at 0, we have to copy the data down* 3) we consume clusters in the following way:*    a) the first element is always consumed*    b) subsequent elements are consumed if:*       i) their advance is zero*       ii) their character index <= the character index of any character seen in this cluster*       iii) the minimum character index seen in this cluster isn't adjacent to the previous cluster*    c) character data is written as follows for horizontal lines (x/y and w/h are exchanged on vertical lines)*       i) the x position is the position of the leftmost glyph whose advance is not zero*       ii)the y position is the baseline*       iii) the x advance is the distance to the maximum x + adv of all glyphs whose advance is not zero*       iv) the y advance is the baseline*       v) vis x,y,w,h tightly encloses the vis x,y,w,h of all the glyphs with nonzero w and h* 4) we can make some simple optimizations if we know some things:*    a) if the mapping is 1-1, unidirectional, and there are no zero-adv glyphs, we just return the glyphinfo*    b) if the mapping is 1-1, unidirectional, we just adjust the remaining glyphs to originate at right/left of the base*    c) if the mapping is 1-1, we compute the base position and advance as we go, then go back to adjust the remaining glyphs*    d) otherwise we keep separate track of the write position as we do (c) since no glyph in the cluster may be in the*    position we are writing.*    e) most clusters are simply the single base glyph in the same position as its character, so we try to avoid*    copying its data unnecessarily.* 5) the glyph vector ought to provide access to these 'global' attributes to enable these optimizations.  A single*    int with flags set is probably ok, we could also provide accessors for each attribute.  This doesn't map to*    the GlyphMetrics flags very well, so I won't attempt to keep them similar.  It might be useful to add those*    in addition to these.*    int FLAG_HAS_ZERO_ADVANCE_GLYPHS = 1; // set if there are zero-advance glyphs*    int FLAG_HAS_NONUNIFORM_ORDER = 2; // set if some glyphs are rearranged out of character visual order*    int FLAG_HAS_SPLIT_CHARACTERS = 4; // set if multiple glyphs per character*    int getDescriptionFlags(); // return an int containing the above flags*    boolean hasZeroAdvanceGlyphs();*    boolean hasNonuniformOrder();*    boolean hasSplitCharacters();*    The optimized cases in (4) correspond to values 0, 1, 3, and 7 returned by getDescriptionFlags().*/  protected float[] createCharinfo() {    StandardGlyphVector gv = getGV();    float[] glyphinfo = null;    try {        glyphinfo = gv.getGlyphInfo();    }    catch (Exception e) {        System.out.println(source);    }     /*    if ((gv.getDescriptionFlags() & 0x7) == 0) {        return glyphinfo;    }    */     int numGlyphs = gv.getNumGlyphs();    int[] indices = gv.getGlyphCharIndices(0, numGlyphs, null);     boolean DEBUG = false;    if (DEBUG) {      System.err.println("number of glyphs: " + numGlyphs);      for (int i = 0; i < numGlyphs; ++i) {        System.err.println("g: " + i +            ", x: " + glyphinfo[i*numvals+posx] +            ", a: " + glyphinfo[i*numvals+advx] +            ", n: " + indices[i]);      }    }     int minIndex = indices[0];  // smallest index seen this cluster    int maxIndex = minIndex;    // largest index seen this cluster    int nextMin = 0;            // expected smallest index for this cluster    int cp = 0;                 // character position    int cx = 0;                 // character index (logical)    int gp = 0;                 // glyph position    int gx = 0;                 // glyph index (visual)    int gxlimit = numGlyphs;    // limit of gx, when we reach this we're done    int pdelta = numvals;       // delta for incrementing positions    int xdelta = 1;             // delta for incrementing indices     boolean ltr = (source.getLayoutFlags() & 0x1) == 0;    if (!ltr) {        minIndex = indices[numGlyphs - 1];        maxIndex = minIndex;        nextMin  = 0; // still logical        cp = glyphinfo.length - numvals;        cx = 0; // still logical        gp = glyphinfo.length - numvals;        gx = numGlyphs - 1;        gxlimit = -1;        pdelta = -numvals;        xdelta = -1;    }     /*    // to support vertical, use 'ixxxx' indices and swap horiz and vertical components    if (source.isVertical()) {        iposx = posy;        iposy = posx;        iadvx = advy;        iadvy = advx;        ivisx = visy;        ivisy = visx;        ivish = visw;        ivisw = vish;    } else {        // use standard values    }    */     // use intermediates to reduce array access when we need to    float cposl = 0, cposr = 0, cvisl = 0, cvist = 0, cvisr = 0, cvisb = 0;    float baseline = 0;     // record if we have to copy data even when no cluster    boolean mustCopy = false;     while (gx != gxlimit) {        // start of new cluster        boolean haveCopy = false;        int clusterExtraGlyphs = 0;         minIndex = indices[gx];        maxIndex = minIndex;         // advance to next glyph        gx += xdelta;        gp += pdelta;  /*        while (gx != gxlimit && (glyphinfo[gp + advx] == 0 ||                           minIndex != nextMin || indices[gx] <= maxIndex)) {  */        while (gx != gxlimit &&               ((glyphinfo[gp + advx] == 0) ||               (minIndex != nextMin) ||               (indices[gx] <= maxIndex) ||               (maxIndex - minIndex > clusterExtraGlyphs))) {            // initialize base data first time through, using base glyph            if (!haveCopy) {                int gps = gp - pdelta;                 cposl = glyphinfo[gps + posx];                cposr = cposl + glyphinfo[gps + advx];                cvisl = glyphinfo[gps + visx];                cvist = glyphinfo[gps + visy];                cvisr = cvisl + glyphinfo[gps + visw];                cvisb = cvist + glyphinfo[gps + vish];                 haveCopy = true;            }             // have an extra glyph in this cluster            ++clusterExtraGlyphs;             // adjust advance only if new glyph has non-zero advance            float radvx = glyphinfo[gp + advx];            if (radvx != 0) {                float rposx = glyphinfo[gp + posx];                cposl = Math.min(cposl, rposx);                cposr = Math.max(cposr, rposx + radvx);            }             // adjust visible bounds only if new glyph has non-empty bounds            float rvisw = glyphinfo[gp + visw];            if (rvisw != 0) {                float rvisx = glyphinfo[gp + visx];                float rvisy = glyphinfo[gp + visy];                cvisl = Math.min(cvisl, rvisx);                cvist = Math.min(cvist, rvisy);                cvisr = Math.max(cvisr, rvisx + rvisw);                cvisb = Math.max(cvisb, rvisy + glyphinfo[gp + vish]);            }             // adjust min, max index            minIndex = Math.min(minIndex, indices[gx]);            maxIndex = Math.max(maxIndex, indices[gx]);             // get ready to examine next glyph            gx += xdelta;            gp += pdelta;        }        // done with cluster, gx and gp are set for next glyph         if (DEBUG) {            System.out.println("minIndex = " + minIndex + ", maxIndex = " + maxIndex);        }         nextMin = maxIndex + 1;         // do common character adjustments        glyphinfo[cp + posy] = baseline;        glyphinfo[cp + advy] = 0;         if (haveCopy) {            // save adjustments to the base character            glyphinfo[cp + posx] = cposl;            glyphinfo[cp + advx] = cposr - cposl;            glyphinfo[cp + visx] = cvisl;            glyphinfo[cp + visy] = cvist;            glyphinfo[cp + visw] = cvisr - cvisl;            glyphinfo[cp + vish] = cvisb - cvist;             // compare number of chars read with number of glyphs read.            // if more glyphs than chars, set mustCopy to true, as we'll always have            // to copy the data from here on out.            if (maxIndex - minIndex < clusterExtraGlyphs) {                mustCopy = true;            }             // Fix the characters that follow the base character.            // New values are all the same.  Note we fix the number of characters            // we saw, not the number of glyphs we saw.            if (minIndex < maxIndex) {                if (!ltr) {                    // if rtl, characters to left of base, else to right.  reuse cposr.                    cposr = cposl;                }                cvisr -= cvisl; // reuse, convert to deltas.                cvisb -= cvist;                 int iMinIndex = minIndex, icp = cp / 8;                 while (minIndex < maxIndex) {                    ++minIndex;                    cx += xdelta;                    cp += pdelta;                     if (cp < 0 || cp >= glyphinfo.length) {                        if (DEBUG) System.out.println("minIndex = " + iMinIndex + ", maxIndex = " + maxIndex + ", cp = " + icp);                    }                     glyphinfo[cp + posx] = cposr;                    glyphinfo[cp + posy] = baseline;                    glyphinfo[cp + advx] = 0;                    glyphinfo[cp + advy] = 0;                    glyphinfo[cp + visx] = cvisl;                    glyphinfo[cp + visy] = cvist;                    glyphinfo[cp + visw] = cvisr;                    glyphinfo[cp + vish] = cvisb;                }            }             // no longer using this copy            haveCopy = false;        } else if (mustCopy) {            // out of synch, so we have to copy all the time now            int gpr = gp - pdelta;             glyphinfo[cp + posx] = glyphinfo[gpr + posx];            glyphinfo[cp + advx] = glyphinfo[gpr + advx];            glyphinfo[cp + visx] = glyphinfo[gpr + visx];            glyphinfo[cp + visy] = glyphinfo[gpr + visy];            glyphinfo[cp + visw] = glyphinfo[gpr + visw];            glyphinfo[cp + vish] = glyphinfo[gpr + vish];        }        // else glyphinfo is already at the correct character position, and is unchanged, so just leave it         // reset for new cluster        cp += pdelta;        cx += xdelta;    }     if (mustCopy && !ltr) {        // data written to wrong end of array, need to shift down         cp -= pdelta; // undo last increment, get start of valid character data in array        System.arraycopy(glyphinfo, cp, glyphinfo, 0, glyphinfo.length - cp);    }     if (DEBUG) {      char[] chars = source.getChars();      int start = source.getStart();      int length = source.getLength();      System.out.println("char info for " + length + " characters");      for(int i = 0; i < length * numvals;) {        System.out.println(" ch: " + Integer.toHexString(chars[start + v2l(i / numvals)]) +                           " x: " + glyphinfo[i++] +                           " y: " + glyphinfo[i++] +                           " xa: " + glyphinfo[i++] +                           " ya: " + glyphinfo[i++] +                           " l: " + glyphinfo[i++] +                           " t: " + glyphinfo[i++] +                           " w: " + glyphinfo[i++] +                           " h: " + glyphinfo[i++]);      }    }     return glyphinfo;  }   /**   * Map logical character index to visual character index.   * <p>   * This ignores hindi reordering.  @see createCharinfo   */  protected int l2v(int index) {    return (source.getLayoutFlags() & 0x1) == 0 ? index : source.getLength() - 1 - index;  }   /**   * Map visual character index to logical character index.   * <p>   * This ignores hindi reordering.  @see createCharinfo   */  protected int v2l(int index) {    return (source.getLayoutFlags() & 0x1) == 0 ? index : source.getLength() - 1 - index;  }   public TextLineComponent getSubset(int start, int limit, int dir) {    return new ExtendedTextSourceLabel(source.getSubSource(start, limit-start, dir), decorator);  }   public String toString() {    if (true) {        return source.toString(source.WITHOUT_CONTEXT);    }    StringBuffer buf = new StringBuffer();    buf.append(super.toString());    buf.append("[source:");    buf.append(source.toString(source.WITHOUT_CONTEXT));    buf.append(", lb:");    buf.append(lb);    buf.append(", ab:");    buf.append(ab);    buf.append(", vb:");    buf.append(vb);    buf.append(", gv:");    buf.append(gv);    buf.append(", ci: ");    if (charinfo == null) {      buf.append("null");    } else {      buf.append(charinfo[0]);      for (int i = 1; i < charinfo.length;) {        buf.append(i % numvals == 0 ? "; " : ", ");        buf.append(charinfo[i]);      }    }    buf.append("]");     return buf.toString();  }   //public static ExtendedTextLabel create(TextSource source) {  //  return new ExtendedTextSourceLabel(source);  //}   public int getNumJustificationInfos() {    return getGV().getNumGlyphs();  }    public void getJustificationInfos(GlyphJustificationInfo[] infos, int infoStart, int charStart, int charLimit) {    // This simple implementation only uses spaces for justification.    // Since regular characters aren't justified, we don't need to deal with    // special infos for combining marks or ligature substitution glyphs.    // added character justification for kanjii only 2/22/98     StandardGlyphVector gv = getGV();     float[] charinfo = getCharinfo();     float size = gv.getFont().getSize2D();     GlyphJustificationInfo nullInfo =      new GlyphJustificationInfo(0,                                 false, GlyphJustificationInfo.PRIORITY_NONE, 0, 0,                                 false, GlyphJustificationInfo.PRIORITY_NONE, 0, 0);     GlyphJustificationInfo spaceInfo =      new GlyphJustificationInfo(size,                                 true, GlyphJustificationInfo.PRIORITY_WHITESPACE, 0, size,                                 true, GlyphJustificationInfo.PRIORITY_WHITESPACE, 0, size / 4f);     GlyphJustificationInfo kanjiInfo =      new GlyphJustificationInfo(size,                                 true, GlyphJustificationInfo.PRIORITY_INTERCHAR, size, size,                                 false, GlyphJustificationInfo.PRIORITY_NONE, 0, 0);     char[] chars = source.getChars();    int offset = source.getStart();     // assume data is 1-1 and either all rtl or all ltr, for now     int numGlyphs = gv.getNumGlyphs();    int minGlyph = 0;    int maxGlyph = numGlyphs;    boolean ltr = (source.getLayoutFlags() & 0x1) == 0;    if (charStart != 0 || charLimit != source.getLength()) {      if (ltr) {        minGlyph = charStart;        maxGlyph = charLimit;      } else {        minGlyph = numGlyphs - charLimit;        maxGlyph = numGlyphs - charStart;      }    }     for (int i = 0; i < numGlyphs; ++i) {      GlyphJustificationInfo info = null;      if (i >= minGlyph && i < maxGlyph) {        if (charinfo[i * numvals + advx] == 0) { // combining marks don't justify          info = nullInfo;        } else {          int ci = v2l(i); // 1-1 assumption again          char c = chars[offset + ci];          if (Character.isWhitespace(c)) {            info = spaceInfo;            // CJK, Hangul, CJK Compatibility areas          } else if (c >= 0x4e00 &&                     (c < 0xa000) ||                     (c >= 0xac00 && c < 0xd7b0) ||                     (c >= 0xf900 && c < 0xfb00)) {            info = kanjiInfo;          } else {            info = nullInfo;          }        }      }      infos[infoStart + i] = info;    }  }   public TextLineComponent applyJustificationDeltas(float[] deltas, int deltaStart, boolean[] flags) {     // when we justify, we need to adjust the charinfo since spaces    // change their advances.  preserve the existing charinfo.     float[] newCharinfo = (float[])getCharinfo().clone();     // we only push spaces, so never need to rejustify    flags[0] = false;     // preserve the existing gv.     StandardGlyphVector newgv = (StandardGlyphVector)getGV().clone();    float[] newPositions = newgv.getGlyphPositions(null);    int numGlyphs = newgv.getNumGlyphs();     /*    System.out.println("oldgv: " + getGV() + ", newgv: " + newgv);    System.out.println("newpositions: " + newPositions);    for (int i = 0; i < newPositions.length; i += 2) {      System.out.println("[" + (i/2) + "] " + newPositions[i] + ", " + newPositions[i+1]);    }     System.out.println("deltas: " + deltas + " start: " + deltaStart);    for (int i = deltaStart; i < deltaStart + numGlyphs; i += 2) {      System.out.println("[" + (i/2) + "] " + deltas[i] + ", " + deltas[i+1]);    }    */     char[] chars = source.getChars();    int offset = source.getStart();     // accumulate the deltas to adjust positions and advances.    // handle whitespace by modifying advance,    // handle everything else by modifying position before and after     float deltaPos = 0;

Lines 501–1000 of 1040. Previous lines