001: /*****************************************************************************
002: * LineMetrics.java
003: * ****************************************************************************/package org.openlaszlo.compiler;
004:
005: import java.io.*;
006: import java.util.*;
007: import org.openlaszlo.xml.internal.XMLUtils;
008: import org.apache.log4j.*;
009:
010: /** Utility functions for measuring HTML content, and translating it into Flash strings.
011: *
012: * @author <a href="mailto:hminsky@laszlosystems.com">Henry Minsky</a>
013: */
014:
015: /** Used to hold line widths while calculating element text metrics */
016: class LineMetrics {
017: double maxw = 0.0;
018: double w = 0.0;
019: boolean verbatim = false;
020: int nlines = 1;
021:
022: int last_space_pos;
023: int last_newline_pos;
024: double last_spacewidth = 0;
025:
026: int trailing_newlines = 0;
027:
028: /* Cases where we need to normalize whitespace across element boundaries
029:
030: foo <i>bar</i>
031: foo <i> bar</i>
032: <i>foo</i> bar
033: <i>foo </i>bar
034: <i>foo </i> bar
035: <i>foo</i> <b>bar</b>
036: <i>foo</i> <b> bar</b>
037: <i>foo </i> <b> bar</b>
038: <i>foo </i><b> bar</b>
039: */
040: /** Keep swallowing whitespace until the first non-whitespace character is
041: encountered.
042: */
043: boolean trim = true;
044:
045: StringBuffer buf = new StringBuffer();
046:
047: public String toString() {
048: return "LineMetrics: maxw=" + maxw + " nlines=" + nlines
049: + " verbatim=" + verbatim + " str=|" + buf.toString()
050: + "|";
051: }
052:
053: /* Add a run of HTML, normalizing runs of whitespace to single
054: spaces if required. Flash's HTML text areas are sensitive to whitespace,
055: so we need to present exactly the amount of whitespace that is desired.
056:
057: The trick is to figure out when to insert whitespace,
058: especially as whitespace is carried across element boundaries.
059: We keep one bit of state across Elements:
060:
061: trim:
062: Tells the next element that it should
063: discard any leading whitespace, because either this is the
064: start of a line, or because we have inserted a trailing
065: whitespace already.
066:
067:
068: */
069:
070: void addHTML(String rawtext, String normalized_text,
071: FontInfo fontInfo, SWFWriter generator) {
072: if (rawtext.length() == 0) {
073: return;
074: }
075: boolean leading_whitespace = Character.isWhitespace(rawtext
076: .charAt(0));
077: boolean trailing_whitespace = Character.isWhitespace(rawtext
078: .charAt(rawtext.length() - 1));
079: boolean all_whitespace = (normalized_text.length() == 0);
080:
081: if (all_whitespace) {
082: if (!this .trim
083: && (leading_whitespace || trailing_whitespace)) {
084: normalized_text = " ";
085: } else {
086: normalized_text = "";
087: }
088: this .trim = true;
089: } else {
090: if (!this .trim && leading_whitespace) {
091: normalized_text = " " + normalized_text;
092: }
093: if (trailing_whitespace) {
094: normalized_text = normalized_text + " ";
095: }
096: this .trim = trailing_whitespace;
097: }
098:
099: addSpan(normalized_text, fontInfo, generator);
100: }
101:
102: void setVerbatim(boolean val) {
103: this .verbatim = val;
104: resetLineWidth();
105: }
106:
107: /** Add a run of text to the current text block, tracking the max width
108: and accumulating the text into a buffer. */
109: void addSpan(String str, FontInfo fontInfo, SWFWriter generator) {
110: if (str.length() > 0) {
111: if (generator != null) {
112: double sw = TextCompiler.getTextWidth(str, fontInfo,
113: generator, this );
114: w += sw;
115: }
116: str = XMLUtils.escapeXml(str);
117:
118: // Remember the position of the last space char on the
119: // line, in case we need to trim it later.
120: if (!verbatim) {
121: int buflen = buf.length();
122: char lastchar = str.charAt(str.length() - 1);
123: if (lastchar == ' ') {
124: last_space_pos = buflen + str.length() - 1;
125: } else {
126: last_space_pos = -1;
127: }
128:
129: int newline_pos = str.lastIndexOf('\n');
130: if (newline_pos >= 0) {
131: last_newline_pos = newline_pos + buflen;
132: }
133: }
134: buf.append(str);
135: }
136: }
137:
138: /** Add an HTML command, does not affect textwidth */
139: void addFormat(String str) {
140: buf.append(str);
141: }
142:
143: void addStartTag(String tagName, FontInfo fontInfo,
144: SWFWriter generator) {
145: addFormat("<" + tagName);
146: }
147:
148: void endStartTag() {
149: addFormat(">");
150: }
151:
152: void addEndTag(String tagName) {
153: addFormat("</" + tagName + ">");
154: }
155:
156: /* @return the normalized text string */
157: String getText() {
158: return buf.toString();
159: }
160:
161: /* Compute maxwidth of this line and any previous lines. */
162: void endOfLine() {
163: // Trim trailing whitespace at end of lines
164: if (!verbatim) {
165: if (last_space_pos > 0 && last_space_pos > last_newline_pos) {
166: buf.deleteCharAt(last_space_pos);
167: w -= last_spacewidth;
168: }
169: }
170: maxw = Math.max(maxw, w);
171: w = 0.0;
172: last_space_pos = -1;
173: }
174:
175: /** act as if a linebreak had occurred, for purposes of calculating max text width
176: */
177: void resetLineWidth() {
178: maxw = Math.max(maxw, w);
179: w = 0.0;
180: last_space_pos = -1;
181: }
182:
183: /** End the line and add an HTML linebreak command element to the output buffer. */
184: void newline() {
185: nlines++;
186: endOfLine();
187: trim = true;
188: buf.append("<br/>");
189: }
190:
191: /** Ensures that at least two newline chars are present at end of
192: buffer. */
193: void paragraphBreak() {
194: // Do not put blank space at beginning of text
195: if (buf.length() == 0)
196: return;
197:
198: // Count how many trailing newlines at end of output string
199: trailing_newlines = 0;
200: for (int i = buf.length() - 1; i >= 0; i--) {
201: char c = buf.charAt(i);
202: if (c == '\t' || c == ' ')
203: continue;
204: if (c == '\n') {
205: trailing_newlines++;
206: } else {
207: break;
208: }
209: }
210: if (trailing_newlines == 0) {
211: buf.append("<br/><br/>");
212: } else if (trailing_newlines == 1) {
213: buf.append("<br/>");
214: } else {
215: // don't need any more trailing newlines
216: }
217: }
218:
219: }
|