001: /*
002: * $Id: LazyText.java,v 1.7 2002/07/17 05:13:37 skavish Exp $
003: *
004: * ==========================================================================
005: *
006: * The JGenerator Software License, Version 1.0
007: *
008: * Copyright (c) 2000 Dmitry Skavish (skavish@usa.net). All rights reserved.
009: *
010: * Redistribution and use in source and binary forms, with or without
011: * modification, are permitted provided that the following conditions are met:
012: *
013: * 1. Redistributions of source code must retain the above copyright
014: * notice, this list of conditions and the following disclaimer.
015: *
016: * 2. Redistributions in binary form must reproduce the above copyright
017: * notice, this list of conditions and the following disclaimer in
018: * the documentation and/or other materials provided with the
019: * distribution.
020: *
021: * 3. The end-user documentation included with the redistribution, if
022: * any, must include the following acknowlegement:
023: * "This product includes software developed by Dmitry Skavish
024: * (skavish@usa.net, http://www.flashgap.com/)."
025: * Alternately, this acknowlegement may appear in the software itself,
026: * if and wherever such third-party acknowlegements normally appear.
027: *
028: * 4. The name "The JGenerator" must not be used to endorse or promote
029: * products derived from this software without prior written permission.
030: * For written permission, please contact skavish@usa.net.
031: *
032: * 5. Products derived from this software may not be called "The JGenerator"
033: * nor may "The JGenerator" appear in their names without prior written
034: * permission of Dmitry Skavish.
035: *
036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
039: * DISCLAIMED. IN NO EVENT SHALL DMITRY SKAVISH OR THE OTHER
040: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: * SUCH DAMAGE.
048: *
049: */
050:
051: package org.openlaszlo.iv.flash.api.text;
052:
053: import java.io.*;
054: import java.util.*;
055: import java.awt.geom.*;
056:
057: import org.openlaszlo.iv.flash.parser.*;
058: import org.openlaszlo.iv.flash.util.*;
059: import org.openlaszlo.iv.flash.api.*;
060: import org.openlaszlo.iv.flash.context.*;
061:
062: /**
063: * This class represents DefineText tag.
064: * <P>
065: * Basically it is alredy laid out text.
066: *
067: * @author Dmitry Skavish
068: * @see Text
069: */
070: public final class LazyText extends FlashDef implements TextBlock {
071:
072: public static final int HAS_FONT = 0x08;
073: public static final int HAS_COLOR = 0x04;
074: public static final int HAS_YOFFSET = 0x02;
075: public static final int HAS_XOFFSET = 0x01;
076:
077: public boolean withAlpha; // true if color with alpha
078: public Rectangle2D bounds; // bounds of the text
079: public AffineTransform matrix; // matrix of the text
080: public IVVector records = new IVVector(); // vector of TextRecord's and TextStyleChangeRecord's
081: private Text text; // reconstructed text
082:
083: public LazyText() {
084: }
085:
086: public LazyText(boolean withAlpha) {
087: this .withAlpha = withAlpha;
088: }
089:
090: public Rectangle2D getBounds() {
091: if (text != null)
092: return text.getBounds();
093: return bounds;
094: }
095:
096: public void setBounds(Rectangle2D bounds) {
097: this .bounds = bounds;
098: }
099:
100: /**
101: * Returns vector of all text records of this text.
102: *
103: * @return vector of {@link TextRecord} or/and {@link TextStyleChangeRecord}
104: */
105: public IVVector getAllTextRecords1() {
106: if (text != null)
107: return text.getAllTextRecords();
108: return records;
109: }
110:
111: /**
112: * Sets new vector of text records for this text.
113: *
114: * @param records new vector of {@link TextItem} or/and {@link TextStyleChangeRecord}
115: */
116: public void setAllTextRecords(IVVector records) {
117: this .records = records;
118: }
119:
120: /**
121: * Adds new text record to this text
122: *
123: * @param record new text record to be added
124: */
125: public void addTextRecord(TextRecord record) {
126: records.addElement(record);
127: }
128:
129: /**
130: * Adds new text style change record to this text
131: *
132: * @param record new text style change record to be added
133: */
134: public void addTextStyleChangeRecord(TextStyleChangeRecord record) {
135: records.addElement(record);
136: }
137:
138: public void setMatrix(AffineTransform matrix) {
139: this .matrix = matrix;
140: }
141:
142: public AffineTransform getMatrix() {
143: if (text != null)
144: return text.getMatrix();
145: return matrix;
146: }
147:
148: public int getTag() {
149: if (withAlpha)
150: return Tag.DEFINETEXT2;
151: return Tag.DEFINETEXT;
152: }
153:
154: public static LazyText parse(Parser p, boolean withAlpha) {
155: LazyText text = new LazyText(withAlpha);
156: text._parse(p);
157: return text;
158: }
159:
160: public void _parse(Parser p) {
161: // get id
162: setID(p.getUWord());
163: // get bounds and matrix
164: bounds = p.getRect();
165: matrix = p.getMatrix();
166:
167: // get nBits
168: int nGlyphBits = p.getUByte();
169: int nAdvanceBits = p.getUByte();
170:
171: boolean is_swt = p.getFile().isTemplate();
172: boolean is_const = true;
173: //System.out.println("LazyText.parse: nGlyphBits="+nGlyphBits+", nAdvanceBits="+nAdvanceBits);
174:
175: // get textrecords
176: Font curFont = null;
177: boolean isData = false;
178: for (;;) {
179: int flags = p.getUByte();
180: if (flags == 0) {
181: break;
182: }
183: //System.out.println("LazyText.parse: flags="+Util.b2h(flags));
184: //if( (flags&0x80) != 0 ) {
185: if (isData || (flags & 0x80) == 0) {
186: TextRecord tr = new TextRecord(flags); // flags contains number of glyphs
187: records.addElement(tr);
188: // glyph record
189: p.initBits();
190: for (int i = 0; i < flags; i++) {
191: //System.out.println("LazyText.parse: i="+i);
192: int idx = p.getBits(nGlyphBits);
193: int adv = p.getSBits(nAdvanceBits);
194: char ch = (char) curFont.codeTable[idx]; // codetable has to be already setup
195: //System.out.println("LazyText.parse: idx="+idx+", adv="+adv+", ch="+ch);
196: tr.add(ch, idx, adv);
197: }
198: if (!is_swt && is_const) {
199: for (int i = 0; i < flags; i++) {
200: if (tr.getChar(i) == '{'
201: && (i + 1 >= flags || tr.getChar(i + 1) != '{')) {
202: is_const = false;
203: break;
204: }
205: }
206: }
207: //tr.printContent(System.out, "trrecord: ");
208: } else {
209: // control record
210: TextStyleChangeRecord ts = new TextStyleChangeRecord();
211: records.addElement(ts);
212: if ((flags & HAS_FONT) != 0) {
213: // get font definition
214: curFont = ((FontDef) p.getDef(p.getUWord()))
215: .getFont();
216: ts.setFont(curFont);
217: }
218: if ((flags & HAS_COLOR) != 0) {
219: // get color
220: ts.setColor(Color.parse(p, withAlpha));
221: }
222: if ((flags & HAS_XOFFSET) != 0) {
223: ts.setX(p.getWord());
224: }
225: if ((flags & HAS_YOFFSET) != 0) {
226: ts.setY(p.getWord());
227: }
228: if ((flags & HAS_FONT) != 0) {
229: ts.setHeight(p.getUWord());
230: }
231: //ts.printContent(System.out, "StyleChange: ");
232: }
233: isData = !isData;
234: }
235: setConstant(is_const);
236: }
237:
238: public void write(FlashOutput fob) {
239: if (text != null) {
240: text.write(fob, this );
241: } else {
242: int start = fob.getPos(); // save for tag
243: fob.skip(6); // 6 - long tag
244: fob.writeDefID(this );
245: fob.write(bounds);
246: fob.write(matrix);
247:
248: // update indexes from font and calculate maximum index and advance value
249: int maxIdx = Integer.MIN_VALUE;
250: int maxAdv = Integer.MIN_VALUE;
251: Font lastFont = null;
252: for (int i = 0; i < records.size(); i++) {
253: Object o = records.elementAt(i);
254: if (o instanceof TextStyleChangeRecord) {
255: Font f = ((TextStyleChangeRecord) o).getFont();
256: if (f != null)
257: lastFont = f;
258: } else {
259: TextRecord tr = (TextRecord) o;
260: int idx = tr.getMaxIndex();
261: int adv = tr.getMaxAdvance();
262: if (idx > maxIdx)
263: maxIdx = idx;
264: if (adv > maxAdv)
265: maxAdv = adv;
266: }
267:
268: }
269: int nGlyphBits = Util.getMinBitsU(maxIdx);
270: int nAdvanceBits = Util.getMinBitsS(maxAdv);
271:
272: fob.writeByte(nGlyphBits);
273: fob.writeByte(nAdvanceBits);
274:
275: fob.setUserData(new int[] { nGlyphBits, nAdvanceBits });
276: records.write(fob);
277:
278: fob.writeByte(0);
279:
280: int size = fob.getPos() - start - 6; // 6 - long tag
281: fob.writeLongTagAt(getTag(), size, start);
282: }
283: }
284:
285: public void printContent(PrintStream out, String indent) {
286: out.println(indent + "LazyText: id=" + getID());
287: out.println(indent + " " + bounds);
288: out.println(indent + " " + matrix);
289: records.printContent(out, indent + " ");
290: }
291:
292: // ------------------------------------------------------- //
293: // TextBlock implementation //
294: /**
295: * Layouts this text.
296: * <p>
297: * Does nothing, because the text is already laid out.
298: */
299: public void layout() {
300: if (text != null)
301: text.layout();
302: }
303:
304: /**
305: * Returns vector of {@link TextRecord}s from this text
306: * of specified font.
307: *
308: * @param font font of text records to be returned
309: * @return text records of specified font
310: */
311: public IVVector getTextRecords(Font font) {
312: if (text != null)
313: return text.getTextRecords(font);
314: IVVector trs = new IVVector();
315: Font lastFont = null;
316: for (int k = 0; k < records.size(); k++) {
317: Object o = records.elementAt(k);
318: if (o instanceof TextStyleChangeRecord) {
319: Font f = ((TextStyleChangeRecord) o).getFont();
320: if (f != null)
321: lastFont = f;
322: } else {
323: if (lastFont == font)
324: trs.addElement(o);
325: }
326: }
327: return trs;
328: }
329:
330: /**
331: * Updates records' font.
332: * <P>
333: * Changes one specified font into another in all records.
334: * In text records also updates indexes.
335: *
336: * @param old_font old font
337: * @param new_font new font
338: */
339: public void changeFont(Font old_font, Font new_font) {
340: if (text != null) {
341: text.changeFont(old_font, new_font);
342: } else {
343: FontDef.changeRecordsFont(records, old_font, new_font);
344: }
345: }
346:
347: // ------------------------------------------------------- //
348:
349: public void collectDeps(DepsCollector dc) {
350: if (text != null) {
351: text.collectDeps(dc);
352: } else {
353: for (int i = 0; i < records.size(); i++) {
354: FlashItem t = (FlashItem) records.elementAt(i);
355: if (t instanceof TextStyleChangeRecord) {
356: TextStyleChangeRecord ts = (TextStyleChangeRecord) t;
357: if (ts.getFont() != null)
358: dc.addDep(ts.getFont());
359: }
360: }
361: }
362: }
363:
364: public void collectFonts(FontsCollector fc) {
365: if (text != null) {
366: text.collectFonts(fc);
367: } else {
368: for (int i = 0; i < records.size(); i++) {
369: FlashItem t = (FlashItem) records.elementAt(i);
370: if (t instanceof TextStyleChangeRecord) {
371: TextStyleChangeRecord ts = (TextStyleChangeRecord) t;
372: if (ts.getFont() != null)
373: fc.addFont(ts.getFont(), this );
374: }
375: }
376: }
377: }
378:
379: /**
380: * if x == 0 && prevX was at the end then append
381: * if x == 0 && prevX was NOT at the end then new item
382: * if x != 0 then check if rest of x != 0 then if they are equal it's 'center'
383: * @return
384: */
385: public Text createText() {
386: Font font = null;
387: Color color = null;
388: int lastX = -1, lastY = 0;
389: int height = 0;
390: int last_rem = 0; // remainder in last line
391: char end_ch = '\0'; // last char in line
392:
393: int max_ascent = 0;
394: int max_descent = 0;
395: int first_not_updated = 0;
396:
397: boolean newItem = false;
398: IVVector items = new IVVector();
399: TextItem curItem = null;
400: for (int i = 0; i < records.size();) {
401: Object o = records.elementAt(i++);
402: if (!(o instanceof TextStyleChangeRecord))
403: return null;
404: TextStyleChangeRecord tsr = (TextStyleChangeRecord) o;
405: o = records.elementAt(i++);
406: if (!(o instanceof TextRecord))
407: return null;
408: TextRecord tr = (TextRecord) o;
409: String str = new String(tr.getText(), 0, tr.getSize());
410:
411: //System.out.println("New record: '"+str+"'");
412: // calculate first word size
413: int first_word_size = 0;
414: int k = 0;
415: int max_adv = 0;
416: for (; k < tr.getSize(); k++)
417: if (tr.getChar(k) != ' ')
418: break;
419: for (; k < tr.getSize(); k++) {
420: if (tr.getChar(k) == ' ')
421: break;
422: int adv = tr.getAdvance(k);
423: if (adv > max_adv)
424: max_adv = adv;
425: first_word_size += adv;
426: }
427: first_word_size += max_adv - 1;
428: //System.out.println(" first word size="+first_word_size);
429:
430: newItem = false;
431: // get new font props
432: if (tsr.getFont() != null) {
433: font = tsr.getFont();
434: height = tsr.getHeight();
435: newItem = true;
436: }
437:
438: if (tsr.getColor() != null) {
439: color = tsr.getColor();
440: if (!(color instanceof AlphaColor)) {
441: color = new AlphaColor(color.getRGB());
442: ((AlphaColor) color).setAlpha(255);
443: }
444: newItem = true;
445: }
446: //System.out.println(" newItem="+newItem);
447:
448: if (newItem) {
449: if (curItem != null)
450: items.addElement(curItem);
451: curItem = new TextItem("", font, height, color);
452: }
453:
454: // check whether x and y have been changed
455: int newX = tsr.getX();
456: int newY = tsr.getY();
457: boolean x_changed = newX == Integer.MAX_VALUE ? false
458: : newX != lastX;
459: boolean y_changed = newY == Integer.MAX_VALUE ? false
460: : newY != lastY;
461: //System.out.println(" x_changed="+x_changed+", newX="+newX);
462: //System.out.println(" y_changed="+y_changed+", newY="+newY);
463:
464: // calculate linesp
465: /*if( y_changed && i >= 2 ) {
466: int delta = newY-lastY; // ascent+descent+linesp
467: int linesp = delta-max_ascent-max_descent;
468: for(; first_not_updated<items.size(); first_not_updated++ ) {
469: TextItem item = (TextItem) items.elementAt(first_not_updated);
470: item.linesp = linesp;
471: }
472: max_ascent = 0;
473: max_descent = 0;
474: }
475:
476: if( tsr.getFont() != null ) {
477: int ascent = (font.ascent * height) / 1024;
478: //int descent = (font.descent * height) / 1024 + curItem.linesp;
479: int descent = (font.descent * height) / 1024;
480: if( ascent > max_ascent ) max_ascent = ascent;
481: if( descent > max_descent ) max_descent = descent;
482:
483: } */
484:
485: if (x_changed && y_changed) {
486: // next line
487: //if( newX == 0 ) {
488: // normal new line
489: if (end_ch == ' ' && last_rem < first_word_size) {
490: // add this text to current item
491: curItem.text += str;
492: } else {
493: // it's a newline
494: curItem.text += "\n" + str;
495: }
496: //} else {
497: // aligned new line: most likely to right
498: //}
499: } else if (y_changed) {
500: // x was not changed, usually it's a first record
501: // if it's a first record do not add newline
502: if (i == 2)
503: curItem.text += str;
504: else {
505: curItem.text += "\n" + str;
506: }
507: } else if (x_changed) {
508: // y was not change, very strange -> do not properly handle
509: curItem.text += str;
510: } else {
511: // no x nor y were changed, just add text
512: curItem.text += str;
513: }
514:
515: end_ch = str.length() == 0 ? '\0' : str
516: .charAt(str.length() - 1);
517: // calculate the end of this record
518: if (x_changed)
519: lastX = newX;
520: for (int j = 0; j < tr.getSize(); j++) {
521: lastX += tr.getAdvance(j);
522: }
523: last_rem = (int) bounds.getMaxX() - lastX;
524: if (y_changed)
525: lastY = newY;
526: }
527:
528: if (curItem != null)
529: items.addElement(curItem);
530:
531: Text text = new Text(withAlpha);
532: text.setBounds(bounds);
533: text.setGenBounds(bounds);
534: text.setMatrix(matrix);
535: text.setTextItems(items);
536: //text.printContent(System.out, "");
537:
538: return text;
539: }
540:
541: public void apply(Context context) {
542: if (isConstant())
543: return;
544: text = createText();
545: if (text == null)
546: return;
547: text.apply(context);
548: //text.printContent(System.out, "apply: ");
549: }
550:
551: protected boolean _isConstant() {
552: return true;
553: }
554:
555: protected FlashItem copyInto(FlashItem item, ScriptCopier copier) {
556: super .copyInto(item, copier);
557: ((LazyText) item).withAlpha = withAlpha;
558: ((LazyText) item).bounds = (Rectangle2D) bounds.clone();
559: ((LazyText) item).matrix = (AffineTransform) matrix.clone();
560: ((LazyText) item).records = records.getCopy(copier);
561: ((LazyText) item).text = text == null ? null : (Text) text
562: .getCopy(copier);
563: return item;
564: }
565:
566: public FlashItem getCopy(ScriptCopier copier) {
567: return copyInto(new LazyText(), copier);
568: }
569: }
|