001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
003: *
004: * This code is free software; you can redistribute it and/or modify it
005: * under the terms of the GNU General Public License version 2 only, as
006: * published by the Free Software Foundation. Sun designates this
007: * particular file as subject to the "Classpath" exception as provided
008: * by Sun in the LICENSE file that accompanied this code.
009: *
010: * This code is distributed in the hope that it will be useful, but WITHOUT
011: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
012: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
013: * version 2 for more details (a copy is included in the LICENSE file that
014: * accompanied this code).
015: *
016: * You should have received a copy of the GNU General Public License version
017: * 2 along with this work; if not, write to the Free Software Foundation,
018: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
019: *
020: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
021: * CA 95054 USA or visit www.sun.com if you need additional information or
022: * have any questions.
023: *
024: */
025:
026: /*
027: * @(#)FontRunIterator.java 1.2 03/11/26
028: *
029: * (C) Copyright IBM Corp. 2003 - All Rights Reserved
030: */
031:
032: package sun.font;
033:
034: /**
035: * Iterates over runs of fonts in a CompositeFont, optionally taking script runs into account.
036: */
037: public final class FontRunIterator {
038: CompositeFont font;
039: char[] text;
040: int start;
041: int limit;
042:
043: CompositeGlyphMapper mapper; // handy cache
044:
045: int slot = -1;
046: int pos;
047:
048: public void init(CompositeFont font, char[] text, int start,
049: int limit) {
050: if (font == null || text == null || start < 0 || limit < start
051: || limit > text.length) {
052: throw new IllegalArgumentException();
053: }
054:
055: this .font = font;
056: this .text = text;
057: this .start = start;
058: this .limit = limit;
059:
060: this .mapper = (CompositeGlyphMapper) font.getMapper();
061: this .slot = -1;
062: this .pos = start;
063: }
064:
065: public PhysicalFont getFont() {
066: return slot == -1 ? null : font.getSlotFont(slot);
067: }
068:
069: public int getGlyphMask() {
070: return slot << 24;
071: }
072:
073: public int getPos() {
074: return pos;
075: }
076:
077: /*
078: * characters that are in the 'common' script become part of the
079: * surrounding script run. we want to fetch these from the same font
080: * used to get surrounding characters, where possible. but we don't
081: * want to force non-common characters to come from other than their
082: * standard font.
083: *
084: * what we really want to do is this:
085: * 1) fetch a code point from the text.
086: * 2) get its 'native' script code
087: * 3) determine its 'resolved' script code
088: * 4) if its native script is COMMON, and its resolved script is the same as the previous
089: * code point's, then see if the previous font supports this code point. if so, use it.
090: * 5) otherwise resolve the font as usual
091: * 6) break the run when either the physical font or the resolved script changes.
092: *
093: * problems: we optimize latin-1 and cjk text assuming a fixed
094: * width for each character. since latin-1 digits and punctuation
095: * are common, following this algorithm they will change to match
096: * the fonts used for the preceeding text, and potentially change metrics.
097: *
098: * this also seems to have the potential for changing arbitrary runs of text, e.g.
099: * any number of digits and spaces can change depending on the preceeding (or following!)
100: * non-COMMON character's font assignment. this is not good.
101: *
102: * since the goal is to enable layout to be performed using as few physical fonts as
103: * possible, and the primary cause of switching fonts is to handle spaces, perhaps
104: * we should just special-case spaces and assign them from the current font, whatever
105: * it may be.
106: *
107: * One could also argue that the job of the composite font is to assign physical fonts
108: * to text runs, however it wishes. we don't necessarily have to provide script info
109: * to let it do this. it can determine based on whatever. so having a special 'next'
110: * function that takes script (and limit) is redundant. It can fetch the script again
111: * if need be.
112: *
113: * both this and the script iterator are turning char sequences into code point
114: * sequences. maybe it would be better to feed a single code point into each iterator-- push
115: * the data instead of pull it?
116: */
117:
118: public boolean next(int script, int lim) {
119: if (pos == lim) {
120: return false;
121: }
122:
123: int ch = nextCodePoint(lim);
124: int sl = mapper.charToGlyph(ch) & CompositeGlyphMapper.SLOTMASK;
125: slot = sl >>> 24;
126: while ((ch = nextCodePoint(lim)) != DONE
127: && (mapper.charToGlyph(ch) & CompositeGlyphMapper.SLOTMASK) == sl)
128: ;
129: pushback(ch);
130:
131: return true;
132: }
133:
134: public boolean next() {
135: return next(Script.COMMON, limit);
136: }
137:
138: static final int SURROGATE_START = 0x10000;
139: static final int LEAD_START = 0xd800;
140: static final int LEAD_LIMIT = 0xdc00;
141: static final int TAIL_START = 0xdc00;
142: static final int TAIL_LIMIT = 0xe000;
143: static final int LEAD_SURROGATE_SHIFT = 10;
144: static final int SURROGATE_OFFSET = SURROGATE_START
145: - (LEAD_START << LEAD_SURROGATE_SHIFT) - TAIL_START;
146:
147: static final int DONE = -1;
148:
149: final int nextCodePoint() {
150: return nextCodePoint(limit);
151: }
152:
153: final int nextCodePoint(int lim) {
154: if (pos >= lim) {
155: return DONE;
156: }
157: int ch = text[pos++];
158: if (ch >= LEAD_START && ch < LEAD_LIMIT && pos < lim) {
159: int nch = text[pos];
160: if (nch >= TAIL_START && nch < TAIL_LIMIT) {
161: ++pos;
162: ch = (ch << LEAD_SURROGATE_SHIFT) + nch
163: + SURROGATE_OFFSET;
164: }
165: }
166: return ch;
167: }
168:
169: final void pushback(int ch) {
170: if (ch >= 0) {
171: if (ch >= 0x10000) {
172: pos -= 2;
173: } else {
174: pos -= 1;
175: }
176: }
177: }
178: }
|