All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.xfa.text.markup.MarkupXHTMLIn Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
package com.adobe.xfa.text.markup;

import com.adobe.xfa.font.FontInfo;

import com.adobe.xfa.gfx.GFXColour;
import com.adobe.xfa.gfx.GFXDecorationInfo;
import com.adobe.xfa.gfx.GFXTextAttr;

import com.adobe.xfa.text.TextAttr;
import com.adobe.xfa.text.TextBaselineShift;
import com.adobe.xfa.text.TextField;
import com.adobe.xfa.text.TextMeasurement;
import com.adobe.xfa.text.TextPosn;
import com.adobe.xfa.text.TextResolver;
import com.adobe.xfa.text.TextStream;
import com.adobe.xfa.text.TextTab;
import com.adobe.xfa.text.TextTabList;

import com.adobe.xfa.ut.Storage;
import com.adobe.xfa.ut.StringUtils;
import com.adobe.xfa.ut.UnitSpan;
import com.adobe.xfa.ut.UniCharIterator;

import org.xml.sax.Attributes;

/**
 * This class is the XHTML input markup engine.  To use it, one creates
 * an instance, passing the string to be translated in a constructor
 * parameter.  Then the engine is passed to a Markup() method on either
 * a text stream or range.
 * 

* For more information, please see the extenral documentation. *

* @exclude from published api. */ public class MarkupXHTMLIn extends MarkupEngineIn { /** * @exclude from published api. */ private static class Frame { int meTag; // current tag, MARKUP_UNKNOWN if unrecognized int meSpace; // current space state (see above) Frame () { meTag = MarkupAttr.MARKUP_UNKNOWN; meSpace = SPACE_NORMAL; } Frame (int eTag, int eSpace) { meTag = eTag; meSpace = eSpace; } } /** * @exclude from published api. */ private static class Stack extends Storage { static final long serialVersionUID = 1010338834063169704L; Stack () { } final Frame frameAt (int index) { return get (index); } final Frame top () { return frameAt (size() - 1); } final void push (Frame frame) { add (frame); } final void pop () { removeLast(); } } /** * @exclude from published api. */ private static class LeaderInfo { final MarkupXHTMLIn mpoIn; final TextStream moContent; final TextPosn moPosn; public LeaderInfo (MarkupXHTMLIn poIn) { mpoIn = poIn; moContent = new TextStream(); moPosn = new TextPosn (moContent); } } /** * @exclude from published api. */ private static class StyleInfo { final String attrValue; int offset; int commandEnum; final StringBuilder command = new StringBuilder(); final StringBuilder parameter = new StringBuilder(); StyleInfo (String attrValue) { this.attrValue = attrValue; } } /** * @exclude from published api. */ private static class XHTMLParser extends XMLParserBase { private static class StackData { final MarkupXHTMLIn mpoProcessor; final int mnPopDepth; StackData (MarkupXHTMLIn processor, int popDepth) { mpoProcessor = processor; mnPopDepth = popDepth; } } private MarkupXHTMLIn mpoCurrent; private final Storage moStack = new Storage(); private int mnDepth; private int mnPopDepth; XHTMLParser (MarkupXHTMLIn poXHTMLIn) { mpoCurrent = poXHTMLIn; push (poXHTMLIn); } void cleanup () { while (moStack.size() > 0) { pop(); } } public void onStartTag (String name, Attributes attributes) { mnDepth++; MarkupXHTMLIn poProcessor = mpoCurrent.onStartTag (name, attributes); if (poProcessor != null) { push (poProcessor); } } public void onEndTag (String name) { if (mnDepth == mnPopDepth) { pop(); } mpoCurrent.onEndTag (name); assert (mnDepth > 0); mnDepth--; } public void onContent (String content) { mpoCurrent.onHandleText (content); } private void push (MarkupXHTMLIn poProcessor) { StackData oStackData = new StackData (poProcessor, mnDepth); moStack.add (oStackData); mpoCurrent = poProcessor; mnPopDepth = mnDepth; } private void pop () { assert (moStack.size() > 0); if (mnPopDepth > 0) { mpoCurrent.commit(); } moStack.removeLast(); if (moStack.size() > 0) { StackData oStackData = moStack.last(); mpoCurrent = oStackData.mpoProcessor; mnPopDepth = oStackData.mnPopDepth; } } } static final int SPACE_SUPPRESS_BREAK = 0; // suppress whitespace and breaks around block elements static final int SPACE_SUPPRESS = 1; // suppress whitespace around breaks static final int SPACE_NORMAL = 2; // normal collapsing static final int SPACE_XML = 3; // future: for xml:space attribute static final int SPACE_RUN = 4; // corresponds to xfa-spacerun:yes static final int SPACE_FORCED = 5; // preserve all space, FF99 legacy private static final int PENDING_DEFAULT = 0; // use default, depending on legacy blank line mode private static final int PENDING_NONE = 1; // no space/break pending private static final int PENDING_DIV = 2; // div start/end line break pending private static final int PENDING_SPACE = 3; // space pending private static final int PENDING_BREAK = 4; // break pending (supersedes pending space) //private static final char NAMESPACE_SEPARATOR = '`'; private final static String gsXHTMLNS = "http://www.w3.org/1999/xhtml`"; private final static String gsDefaultTypefaceDefault = "Arial"; private final static String gsNewLine = "\n"; private final static String gsAPIVersion = "xfa:APIVersion"; private final static String gsRGBStart = "rgb("; private final static String gsRGBEnd = ")"; private final static String gsNumeric = "-0123456789."; private final static int gnVersion_1_0_0_0[] = {1, 0, 0, 0}; private final static int gnVersion_2_5_6129_0[] = {2, 5, 6129, 0}; private XHTMLParser mpoParser; private final Stack moStack = new Stack(); private boolean mbVersionDetermined; // TRUE once version heuristically determined private boolean mbTextAccumulated; // TRUE once text has been accumulated after block element private boolean mbParaStarted; // TRUE if started paragraph with no text yet private boolean mbAmbientSupplied; private int mePending; private final TextAttr moPendingAttr = new TextAttr(); private final TextAttr moPrevParaAttr = new TextAttr(); private final TextResolver mpoResolver; private UniCharIterator mIterator; private LeaderInfo mpoLeaderInfo; private int mnTabCount; // if >0, this span starts with mnTabCount tabs /** * Constructor. * @param oMarkupSource - XHTML markup to be processed. * @param pMarkupAttr - (optional) Markup attribute table to use for the * translation. It is strongly recommended that you pass a NULL pointer * (default). * @param poAmbientAttr - (optional) Overall text attribute settings, * for those not specified in the markup. NULL (default) establishes no * overall attributes. * @param poResolver - Callback interface for embedded field references. */ public MarkupXHTMLIn (String oMarkupSource, MarkupAttr pMarkupAttr, TextAttr poAmbientAttr, TextResolver poResolver) { super (oMarkupSource, (pMarkupAttr == null) ? MarkupXHTMLAttr.getDefault() : pMarkupAttr); mpoResolver = poResolver; initialize (poAmbientAttr); } void commit () { if (mpoLeaderInfo != null) { mpoLeaderInfo.mpoIn.commitTabs (mpoLeaderInfo); } } /** * Translate the XHTML markup. *

* This is not normally called by the creator of the XHTML markup * engine. Instead, the engine is passed to a text stream or range in a * Markup() method that makes the call. */ public void translate () { mbVersionDetermined = false; // Establish our default attribute settings. moCurrentAttr.setDefault (true); // Special handling for Type and size. We don't want // specified from the start, since if they're not, the environment should // take over and provide the default. e.g. The default font for a field. // The old FF99 edit control will emit xhtml with no font specified if the // font is the same as the field. moCurrentAttr.typefaceEnable (false); moCurrentAttr.sizeEnable (false); // However, if they supplied an ambient attribute in our constructor, we // can deal with our font a little more intelligently if (mbAmbientSupplied) { moCurrentAttr.override (moAmbientAttr); } // Special handling for background colour. This is not set in xhtml, so // we don't want this attr's default (white) clobbering the control values. moCurrentAttr.colourBgEnable (false); moCurrentAttr.transparentEnable (false); // Make the current attribute stick before disabling all the paragraph // attributes. We want to set the ambient paragraph attributes, but we // don't want them reset when we pop the stack back. // Set all the paragraph attributes to the default values that they share // with xhtml. if (! moCurrentAttr.specialEnable()) { moCurrentAttr.special (TextMeasurement.zero()); } if (! moCurrentAttr.justifyVEnable()) { moCurrentAttr.justifyV (TextAttr.JUST_V_TOP); } if (! moCurrentAttr.justifyHEnable()) { moCurrentAttr.justifyH (TextAttr.JUST_H_LEFT); } if (! moCurrentAttr.tabsEnable()) { moCurrentAttr.tabs (new TextTabList()); } if (! moCurrentAttr.spacingEnable()) { moCurrentAttr.spacing (TextMeasurement.zero()); } attr (moCurrentAttr); moAttrList.add (moCurrentAttr); moStack.add (new Frame (MarkupAttr.MARKUP_HTML, SPACE_NORMAL)); if (mpoParser == null) { XHTMLParser oParser = new XHTMLParser (this); mpoParser = oParser; oParser.processText (sourceText()); mpoParser = null; commit(); } } /** * Set the default tab increment. *

* NOTE: As a static method, there may be multithreading issues. * @param sAttrValue - a unit span value for a left tab. * @param oAttr - the text attribute to be modified * @param pMarkupAttr - the table of markup strings to be used for unit * conversion. */ public static void tabDefault (String sAttrValue, TextAttr oAttr, MarkupAttr pMarkupAttr) { TextTab oTab = extractTab (pMarkupAttr, sAttrValue, TextTab.TYPE_ALIGN_AFTER); TextTabList oTabList = getTabList (oAttr); if (oTab.value() <= 0) { oTabList.noUniform (true); } else { oTabList.uniform (oTab); } oAttr.tabs (oTabList); } /** * Set the tab stops. *

* NOTE: As a static method, there may be multithreading issues. Tab * types can be decimal, left, right and center.
Example:
* "decimal 2cm left 5cm" specifies two tabs. * @param sAttrValue - a collection of tab types and unitspans. * @param oAttr - the text attribute to be modified * @param pMarkupAttr - the table of markup strings to be used for unit * conversion and looking up tab types. */ public static void tabSet (String sAttrValue, TextAttr oAttr, MarkupAttr pMarkupAttr) { TextTabList oTabList = getTabList (oAttr); int nOffset = 0; int nFoundAt = sAttrValue.indexOf (' '); while (nFoundAt >= nOffset) { String sAlign = sAttrValue.substring (nOffset, nFoundAt); nOffset = nFoundAt + 1; nFoundAt = sAttrValue.indexOf (' ', nOffset); if (nFoundAt < 0) { nFoundAt = sAttrValue.length(); } String sValue = sAttrValue.substring (nOffset, nFoundAt); UnitSpan oValue = new UnitSpan (sValue); TextTab oTab = new TextTab (oValue, stringToAlign (pMarkupAttr, sAlign)); oTabList.set (oTab); nOffset = nFoundAt + 1; nFoundAt = sAttrValue.indexOf (' ', nOffset); } oAttr.tabs (oTabList); } // Not intended for general public consumption. public String defaultTypeface () { return gsDefaultTypefaceDefault; } public boolean skipThisCommand (int eTag) { return false; } MarkupXHTMLIn onStartTag (String pcName, Attributes attributes) { String sTagName = pcName; int nPos = sTagName.indexOf (gsXHTMLNS); if (nPos > 0) { StringBuilder noNS = new StringBuilder(); noNS.append(pcName, 0, nPos); noNS.append(pcName, nPos + gsXHTMLNS.length(), pcName.length()); sTagName = noNS.toString(); } mnTabCount = 0; String sAttr = null; if (inUnknownElement()) { moStack.push (new Frame (MarkupAttr.MARKUP_UNKNOWN, moStack.top().meSpace)); } else { int eTag = markupAttr().lookup (sTagName); moStack.push (new Frame (eTag, moStack.top().meSpace)); updateSpaceStatus (eTag); switch (eTag) { case MarkupAttr.MARKUP_BREAK: onCommand (MarkupAttr.MARKUP_BREAK, ""); break; case MarkupAttr.MARKUP_BOLD: onCommand (MarkupAttr.MARKUP_BOLD, ""); break; case MarkupAttr.MARKUP_ITALIC: onCommand (MarkupAttr.MARKUP_ITALIC, ""); break; case MarkupAttr.MARKUP_BODY: if (! mbVersionDetermined) { String sVersion = getAttr (attributes, gsAPIVersion); if (sVersion != null) { mbVersionDetermined = true; int[] nVersion = new int [4]; if (extractHTMLVersion (sVersion, nVersion)) { if (compareHTMLVersions (nVersion, gnVersion_1_0_0_0) == 0) { switchToFF99Mode(); // version 1.0.0.0 } else if (compareHTMLVersions (nVersion, gnVersion_2_5_6129_0) >= 0) { setLegacyBlankLineMode (false); for (int i = 0; i < moStack.size(); i++) { if (moStack.frameAt(i).meSpace == SPACE_SUPPRESS_BREAK) { moStack.frameAt(i).meSpace = SPACE_SUPPRESS; } } } } } } pushAttr(); handleStylingAttributes (attributes, true); onCommand (MarkupAttr.MARKUP_BODY, ""); break; case MarkupAttr.MARKUP_HTML: pushAttr(); handleStylingAttributes (attributes, true); onCommand (MarkupAttr.MARKUP_HTML, ""); break; case MarkupAttr.MARKUP_PARAGRAPH_START: case MarkupAttr.MARKUP_DIV: if ((legacyBlankLineMode() && (eTag == MarkupAttr.MARKUP_PARAGRAPH_START)) || (! mbParaStarted)) { if (legacyPositioning()) { para(); } else { pushAttr(); attr (moPrevParaAttr); para(); popAttr(); } } pushAttr(); if (! handleStylingAttributes (attributes, eTag == MarkupAttr.MARKUP_PARAGRAPH_START)) { // this needs to be done, otherwise the underlying // paragraph style settings are lost TextAttr oParaAttrs = new TextAttr (textAttr()); oParaAttrs.isolatePara (true, posn().legacyPositioning()); attr (oParaAttrs); } onCommand (eTag, ""); break; case MarkupAttr.MARKUP_SUPER: onCommand (MarkupAttr.MARKUP_SUPER, ""); break; case MarkupAttr.MARKUP_SUB: onCommand (MarkupAttr.MARKUP_SUB, ""); break; // For now, treat Anchor tags the same as spans so that we don't // discard their contents. In some future release we'll do a better job // supporting proper hyperlinks case MarkupAttr.MARKUP_SPAN: case MarkupAttr.MARKUP_ANCHOR: flushPendingText (PENDING_SPACE); openScopedBlock(); pushAttr(); if (handleStylingAttributes (attributes)) { if (mnTabCount != 0) { if (moCurrentAttr.leaderPatternEnable() && (moCurrentAttr.leaderPattern() == TextAttr.LEADER_PATTERN_USE_CONTENT)) { LeaderInfo poLeaderInfo = new LeaderInfo (this); MarkupXHTMLIn poReturn = new MarkupXHTMLIn (poLeaderInfo); return poReturn; } else { commitTabs (null); // pops attr stack and scoped block } } } else { String sExpression = getAttr (attributes, MarkupAttr.MARKUP_EMBED); if (sExpression != null) { flushPendingText (PENDING_SPACE); // set up defaults int eEmbedType = TextField.EMBEDTYPE_SOM; int eEmbedMode = TextField.EMBED_FORMATTED; sAttr = getAttr (attributes, MarkupAttr.MARKUP_EMBEDTYPE); if (sAttr != null) { if (sAttr.equals ("uri")) { eEmbedType = TextField.EMBEDTYPE_URI; } else if (sAttr.equals ("som")) { eEmbedType = TextField.EMBEDTYPE_SOM; } } sAttr = getAttr (attributes, MarkupAttr.MARKUP_EMBEDMODE); if (sAttr != null) { if (sAttr.equals ("raw")) { eEmbedMode = TextField.EMBED_RAW; } else if (sAttr.equals ("formatted")) { eEmbedMode = TextField.EMBED_FORMATTED; } } embed (sExpression, eEmbedType, eEmbedMode); if (! legacyBlankLineMode()) { mbParaStarted = false; // paragraph has no content } } } onCommand (MarkupAttr.MARKUP_SPAN, ""); break; case MarkupAttr.MARKUP_UNDERLINE_TAG: onCommand (MarkupAttr.MARKUP_UNDERLINE_TAG, ""); break; } } return null; } void onEndTag (String pcTag) { int eTag = moStack.top().meTag; moStack.removeLast(); if (moStack.top().meSpace < SPACE_NORMAL) { moStack.top().meSpace = SPACE_NORMAL; } switch (eTag) { case MarkupAttr.MARKUP_PARAGRAPH_START: onCommand (MarkupAttr.MARKUP_PARAGRAPH_END, ""); break; case MarkupAttr.MARKUP_DIV: onCommand (MarkupAttr.MARKUP_DIV_END, ""); break; case MarkupAttr.MARKUP_BOLD: onCommand (MarkupAttr.MARKUP_BOLD_END, ""); break; case MarkupAttr.MARKUP_UNDERLINE_TAG: onCommand (MarkupAttr.MARKUP_UNDERLINE_END, ""); break; case MarkupAttr.MARKUP_ITALIC: onCommand (MarkupAttr.MARKUP_ITALIC_END, ""); break; case MarkupAttr.MARKUP_SUB: onCommand (MarkupAttr.MARKUP_SUB_END, ""); break; case MarkupAttr.MARKUP_SUPER: onCommand (MarkupAttr.MARKUP_SUPER_END, ""); break; case MarkupAttr.MARKUP_BODY: onCommand (MarkupAttr.MARKUP_BODY_END, ""); break; case MarkupAttr.MARKUP_HTML: onCommand (MarkupAttr.MARKUP_HTML_END, ""); break; case MarkupAttr.MARKUP_SPAN: case MarkupAttr.MARKUP_ANCHOR: onCommand (MarkupAttr.MARKUP_SPAN_END, ""); break; } updateSpaceStatus (eTag, true); } void onHandleText (String sText) { if (inUnknownElement()) { // ignore the content of any unknown element return; } StringBuilder sAccumulate = new StringBuilder(); int eSpace = moStack.top().meSpace; // Process the text character-by-character. The handling of the // character varies with each space handling state. Note that the space // handling state may change from one character to the next. if (mIterator == null) { mIterator = new UniCharIterator(); } mIterator.attach (sText); for (int c = mIterator.next(); c != '\0'; c = mIterator.next()) { switch (eSpace) { // Suppressing whitespace (e.g., after a

or

): As long as we're // processing whitespace, just ignore it. Once we hit a "real" // character, accumulate it and switch to normal mode to get normal // whitespace collapsing for subsequent whitespace. case SPACE_SUPPRESS: if (legacyBlankLineMode()) { flushPendingText (PENDING_BREAK); // any pending break } if (! isXHTMLSpace (c)) { if (! legacyBlankLineMode()) { flushPendingText (PENDING_BREAK); } UniCharIterator.append (sAccumulate, c); eSpace = SPACE_NORMAL; moStack.top().meSpace = SPACE_NORMAL; } break; case SPACE_SUPPRESS_BREAK: if (legacyBlankLineMode()) { flushPendingText (PENDING_BREAK); // any pending break if (! isXHTMLSpace (c)) { UniCharIterator.append (sAccumulate, c); eSpace = SPACE_NORMAL; moStack.top().meSpace = SPACE_NORMAL; } } break; // Normal space collapsing: Don't accumulate spaces as we hit them. // Instead, mark a pending space, and accumulate the space only when we // hit the next "real" character. case SPACE_NORMAL: if (isXHTMLSpace (c)) { if (mePending == PENDING_NONE) { mePending = PENDING_SPACE; } } else { switch (mePending) { case PENDING_SPACE: sAccumulate.append (' '); break; case PENDING_DIV: if (legacyBlankLineMode()) { if (mbTextAccumulated) { sAccumulate.append ('\n'); } } break; case PENDING_BREAK: sAccumulate.append ('\n'); break; } mePending = PENDING_NONE; UniCharIterator.append (sAccumulate, c); } if (mePending == PENDING_SPACE) { moPendingAttr.copyFrom (textAttr()); } break; // XFA Space runs: Accumulate all text as is, including spaces, except for non-breaking // spaces which are translated to regular spaces. Do not accumulate other whitespace. case SPACE_RUN: if (legacyBlankLineMode()) { flushPendingText (PENDING_NONE); // any pending break or div } if (c == 160) { c = ' '; } if ((! isXHTMLSpace (c)) || (c == ' ')) { if (! legacyBlankLineMode()) { flushPendingText (PENDING_NONE); // any pending break or div } UniCharIterator.append (sAccumulate, c); } break; // XML:Space (future): Accumulate all text as is, including spaces. case SPACE_XML: flushPendingText (PENDING_NONE); // any pending break or div UniCharIterator.append (sAccumulate, c); break; // Forced spaces (old FF99 and test suite compatibility): Treat new-lines // as pending breaks, only to be accumulated when followed by "real" // text. Real text includes all other whitespace characters. The point // of this is to suppress the new-line following the last line of text case SPACE_FORCED: if ((legacyBlankLineMode() && (mePending == PENDING_DIV)) || (mePending == PENDING_BREAK)) { if (mbTextAccumulated || (mePending == PENDING_BREAK)) { sAccumulate.append ('\n'); } mePending = PENDING_NONE; } if (c == '\n') { mePending = PENDING_BREAK; } else { UniCharIterator.append (sAccumulate, c); } break; } } if (sAccumulate.length() > 0) { text (sAccumulate.toString()); } } private MarkupXHTMLIn (LeaderInfo poLeaderInfo) { super ("", poLeaderInfo.mpoIn.markupAttr()); mpoParser = poLeaderInfo.mpoIn.mpoParser; mpoResolver = poLeaderInfo.mpoIn.mpoResolver; mpoLeaderInfo = poLeaderInfo; // TBD: may need special initialization of space handling and may need to // be flushed on pop. initialize ((poLeaderInfo.mpoIn.moCurrentAttr)); setup (poLeaderInfo.moPosn, null); moStack.add (new Frame (MarkupAttr.MARKUP_SPAN, SPACE_NORMAL)); updateSpaceStatus (MarkupAttr.MARKUP_SPAN); } //if the particular markup attribute tag is found, puts the value in sAttr and returns TRUE //if not found, returns FALSE and an empty string parameter public String getAttr (Attributes attrlist, int eTag) { return getAttr (attrlist, markupAttr().lookup (eTag)); } public String getAttr (Attributes attrlist, String sName) { if (attrlist == null) { return null; } String result = attrlist.getValue (sName); if ((result != null) && (result.length() == 0)) { result = null; } return result; } // inherited from class TextMkBase public void text (String sText) { super.text (sText); mbTextAccumulated = true; mbParaStarted = false; moPrevParaAttr.setDefault (false); // in case text between paragraphs } // Inherited from TextMarkupEngineIn protected boolean onCommand (int eTag, String sParameter) { TextMeasurement oMeasure = null; int nOffset; int eParm; double dScale; boolean bCommandHandled = false; switch (eTag) { case MarkupAttr.MARKUP_PARAGRAPH_START: // +++ case MarkupAttr.MARKUP_DIV: mbTextAccumulated = false; mbParaStarted = true; break; case MarkupAttr.MARKUP_BREAK: if (legacyBlankLineMode()) { if (moStack.top().meSpace != SPACE_SUPPRESS_BREAK) { flushPendingText (PENDING_BREAK); mePending = PENDING_BREAK; moPendingAttr.setDefault (false); mbTextAccumulated = false; } } else { flushPendingText (PENDING_BREAK); // previous break gets written mePending = PENDING_BREAK; // this break is pending moPendingAttr.setDefault (false); mbTextAccumulated = false; mbParaStarted = false; // break-only para is valid para } break; // Special character case MarkupAttr.MARKUP_HTML: case MarkupAttr.MARKUP_BODY: case MarkupAttr.MARKUP_SPAN: mbTextAccumulated = false; break; case MarkupAttr.MARKUP_HTML_END: case MarkupAttr.MARKUP_BODY_END: popAttr(); mbTextAccumulated = false; break; case MarkupAttr.MARKUP_SPAN_END: if (legacyBlankLineMode()) { flushPendingText (PENDING_BREAK); } moPrevParaAttr.copyFrom (moCurrentAttr); closeScopedBlock(); popAttr(); break; case MarkupAttr.MARKUP_SPACE_RUN: switch (markupAttr().lookup (sParameter)) { case MarkupAttr.MARKUP_SPACE_RUN_YES: moStack.top().meSpace = SPACE_RUN; break; case MarkupAttr.MARKUP_SPACE_RUN_NO: moStack.top().meSpace = moStack.frameAt(moStack.size()-2).meSpace; // TBD: is this correct and safe? break; } break; // indentation case MarkupAttr.MARKUP_INDENT_FIRST_LINE: oMeasure = TextMeasurement.fromString (sParameter, stringToUnit (markupAttr(), sParameter)); // -ve Special means indent all but the 1st line; +ve Special // means indent 1st line only. So, if we get a +ve indent 1st line // from XHTML, we're OK. If it's negative, subtract it from our // current Left Margin. moCurrentAttr.special (oMeasure); break; case MarkupAttr.MARKUP_INDENT_LEFT: if (sParameter.length() == 0) { flushAttr(); } else { // See comments under INDENT_FIRST_LINE oMeasure = TextMeasurement.fromString (sParameter, stringToUnit (markupAttr(), sParameter)); moCurrentAttr.marginL (oMeasure); } break; case MarkupAttr.MARKUP_INDENT_RIGHT: oMeasure = TextMeasurement.fromString (sParameter, stringToUnit (markupAttr(), sParameter)); moCurrentAttr.marginR (oMeasure); break; case MarkupAttr.MARKUP_SPACE_BEFORE: oMeasure = TextMeasurement.fromString (sParameter, stringToUnit (markupAttr(), sParameter)); moCurrentAttr.spaceBefore (oMeasure); break; case MarkupAttr.MARKUP_SPACE_AFTER: oMeasure = TextMeasurement.fromString (sParameter, stringToUnit (markupAttr(), sParameter)); moCurrentAttr.spaceAfter (oMeasure); break; case MarkupAttr.MARKUP_MARGIN: StringBuilder sCmd = new StringBuilder (sParameter); StringUtils.trimStart (sCmd); int nCount = 0; int nFoundAt = sCmd.indexOf (" "); nOffset = 0; while ((nFoundAt >= 0) && (nCount < 4)) { String sSize = sCmd.substring (nOffset, nFoundAt); nCount++; oMeasure = TextMeasurement.fromString(sSize, stringToUnit (markupAttr(), sSize)); if (nCount == 1) { //if only one value: all margins = value moCurrentAttr.marginL (oMeasure); moCurrentAttr.marginR (oMeasure); moCurrentAttr.spaceBefore (oMeasure); moCurrentAttr.spaceAfter (oMeasure); } else if (nCount == 2) { // if two values: top, bottom = 1; left, right = 2 moCurrentAttr.marginL (oMeasure); moCurrentAttr.marginR (oMeasure); } else if (nCount == 3) { // if three values: top = 1, left, right = 2, bottom = 3 moCurrentAttr.spaceAfter (oMeasure); } else if (nCount == 4) { // if four values: top=1, right=2, bottom=3, left=4 moCurrentAttr.marginL (oMeasure); } nOffset = nFoundAt + 1; nFoundAt = sCmd.indexOf (" ", nOffset); } break; case MarkupAttr.MARKUP_LINE_HEIGHT: oMeasure = TextMeasurement.fromString (sParameter, stringToUnit (markupAttr(), sParameter)); moCurrentAttr.spacing (oMeasure); break; // font case MarkupAttr.MARKUP_FONT_NAME: moCurrentAttr.typeface (sParameter); break; case MarkupAttr.MARKUP_FONT_SIZE: { UnitSpan size = new UnitSpan (sParameter, stringToUnit (markupAttr(), sParameter), false); moCurrentAttr.size (size); break; } case MarkupAttr.MARKUP_FONT_WEIGHT: updateWeight(sParameter); break; case MarkupAttr.MARKUP_FONT_STYLE: updateItalic(sParameter); break; case MarkupAttr.MARKUP_FONT: updateFont(sParameter); break; case MarkupAttr.MARKUP_FONT_HORIZONTAL_SCALE: dScale = TextAttr.parsePercent (sParameter, true); if (! Double.isNaN(dScale)) { moCurrentAttr.horizontalScale (dScale); } break; case MarkupAttr.MARKUP_FONT_VERTICAL_SCALE: dScale = TextAttr.parsePercent (sParameter, true); if (! Double.isNaN(dScale)) { moCurrentAttr.verticalScale (dScale); } break; case MarkupAttr.MARKUP_COLOUR: int lR = 0; int lG = 0; int lB = 0; // #FFFFFF style if (sParameter.charAt (0) == '#') { lR = Integer.parseInt (sParameter.substring (1, 3), 16); lG = Integer.parseInt (sParameter.substring (3, 5), 16); lB = Integer.parseInt (sParameter.substring (5, 7), 16); } // rgb(999,999,999) style else if ((sParameter.substring(0,4).equalsIgnoreCase (gsRGBStart)) && (sParameter.substring(sParameter.length()-1).equals (gsRGBEnd))) { nOffset = 4; int nFound = sParameter.indexOf (',', nOffset); lR = Integer.parseInt (sParameter.substring (nOffset, nFound)); nOffset = nFound + 1; nFound = sParameter.indexOf (',', nOffset); lG = Integer.parseInt (sParameter.substring (nOffset, nFound)); nOffset = nFound + 1; nFound = sParameter.indexOf (',', nOffset); lB = Integer.parseInt (sParameter.substring (nOffset, nFound)); } GFXColour oColour = new GFXColour (lR, lG, lB, 255); moCurrentAttr.colour (oColour); break; // effects case MarkupAttr.MARKUP_TEXT_DECORATION: int eStrikeCode = GFXTextAttr.STRIKEOUT_NONE; if (findAttr (sParameter, MarkupAttr.MARKUP_STRIKEOUT)) { eStrikeCode = GFXTextAttr.STRIKEOUT_SINGLE; } moCurrentAttr.strikeout (eStrikeCode); int nUnderType = GFXTextAttr.UNDER_NONE; int nUnderCount = 0; if (findAttr (sParameter, MarkupAttr.MARKUP_UNDERLINE)) { nUnderType = GFXDecorationInfo.DECORATE_ALL; nUnderCount = GFXDecorationInfo.DECORATE_SINGLE; } if (findAttr (sParameter, MarkupAttr.MARKUP_UNDERLINE_DOUBLE)) { nUnderType = GFXDecorationInfo.DECORATE_ALL; nUnderCount = GFXDecorationInfo.DECORATE_DOUBLE; } if (findAttr (sParameter, MarkupAttr.MARKUP_UNDERLINE_WORD)) { nUnderType = GFXDecorationInfo.DECORATE_WORD; if (nUnderCount == 0) { nUnderCount = GFXDecorationInfo.DECORATE_SINGLE; } } moCurrentAttr.underline (nUnderType | nUnderCount); break; case MarkupAttr.MARKUP_KERNING_MODE: switch (markupAttr().lookup (sParameter)) { case MarkupAttr.MARKUP_NONE: moCurrentAttr.kerning (false); break; case MarkupAttr.MARKUP_KERN_PAIR: moCurrentAttr.kerning (true); break; } break; case MarkupAttr.MARKUP_BOLD: pushAttr(); moCurrentAttr.weight (FontInfo.WEIGHT_BOLD); flushPendingText (PENDING_DEFAULT); attr (moCurrentAttr); break; case MarkupAttr.MARKUP_ITALIC: pushAttr(); moCurrentAttr.italic (true); flushPendingText (PENDING_DEFAULT); attr (moCurrentAttr); break; // underlining case MarkupAttr.MARKUP_UNDERLINE_TAG: pushAttr(); moCurrentAttr.underline (GFXTextAttr.UNDER_ALL | GFXTextAttr.UNDER_SINGLE); flushPendingText (PENDING_DEFAULT); attr (moCurrentAttr); break; case MarkupAttr.MARKUP_UNDERLINE_END: moPrevParaAttr.copyFrom (moCurrentAttr); popAttr(); break; // justification // Horizontal case MarkupAttr.MARKUP_JUSTIFY: switch (markupAttr().lookup (sParameter)) { case MarkupAttr.MARKUP_JUSTIFY_SPREAD: moCurrentAttr.justifyH (TextAttr.JUST_H_SPREAD); break; case MarkupAttr.MARKUP_JUSTIFY_SPREAD_ALL: moCurrentAttr.justifyH (TextAttr.JUST_H_SPREAD_ALL); break; case MarkupAttr.MARKUP_XHTML_LEFT: moCurrentAttr.justifyH (TextAttr.JUST_H_LEFT); break; case MarkupAttr.MARKUP_XHTML_CENTER: moCurrentAttr.justifyH (TextAttr.JUST_H_CENTRE); break; case MarkupAttr.MARKUP_XHTML_RIGHT: moCurrentAttr.justifyH (TextAttr.JUST_H_RIGHT); break; case MarkupAttr.MARKUP_JUSTIFY_COMB_LEFT: moCurrentAttr.justifyH (TextAttr.JUST_H_COMB_LEFT); break; case MarkupAttr.MARKUP_JUSTIFY_COMB_CENTER: moCurrentAttr.justifyH (TextAttr.JUST_H_COMB_CENTRE); break; case MarkupAttr.MARKUP_JUSTIFY_COMB_RIGHT: moCurrentAttr.justifyH (TextAttr.JUST_H_COMB_RIGHT); break; } break; // Vertical case MarkupAttr.MARKUP_JUSTIFY_VERT: case MarkupAttr.MARKUP_TEXT_VALIGN: switch (markupAttr().lookup (sParameter)) { case MarkupAttr.MARKUP_TEXT_VALIGN_TOP: moCurrentAttr.justifyV (TextAttr.JUST_V_TOP); break; case MarkupAttr.MARKUP_TEXT_VALIGN_MIDDLE: moCurrentAttr.justifyV (TextAttr.JUST_V_MIDDLE); break; case MarkupAttr.MARKUP_TEXT_VALIGN_BOTTOM: moCurrentAttr.justifyV (TextAttr.JUST_V_BOTTOM); break; default: // also use vertical-align for baseline shift boolean bSuppressInversion = (moStack.size() > 0) // check old FF99 && (moStack.frameAt(0).meSpace == SPACE_FORCED); TextBaselineShift oShift = new TextBaselineShift (sParameter, bSuppressInversion); moCurrentAttr.baselineShift (oShift); } break; case MarkupAttr.MARKUP_SUPER: pushAttr(); UnitSpan oShift = textAttr().size(); oShift = oShift.multiply (-0.31); if (moCurrentAttr.baselineShiftEnable()) { UnitSpan oBase = new UnitSpan (moCurrentAttr.baselineShift().getString (false)); oShift = oShift.add (oBase); } moCurrentAttr.baselineShift (new TextBaselineShift (oShift)); moCurrentAttr.size (moCurrentAttr.size().multiply (0.66)); flushPendingText (PENDING_DEFAULT); attr (moCurrentAttr); break; case MarkupAttr.MARKUP_SUB_END: case MarkupAttr.MARKUP_SUPER_END: case MarkupAttr.MARKUP_BOLD_END: case MarkupAttr.MARKUP_ITALIC_END: flushPendingText (PENDING_BREAK); moPrevParaAttr.copyFrom (moCurrentAttr); popAttr(); break; case MarkupAttr.MARKUP_PARAGRAPH_END: case MarkupAttr.MARKUP_DIV_END: moPrevParaAttr.copyFrom (moCurrentAttr); popAttr(); mbTextAccumulated = false; break; case MarkupAttr.MARKUP_SUB: pushAttr(); oShift = moCurrentAttr.size(); oShift = oShift.multiply (0.15); if (moCurrentAttr.baselineShiftEnable()) { UnitSpan oBase = new UnitSpan (moCurrentAttr.baselineShift().getString (false)); oShift = oShift.add (oBase); } moCurrentAttr.baselineShift (new TextBaselineShift (oShift)); moCurrentAttr.size (moCurrentAttr.size().multiply (0.66)); flushPendingText (PENDING_DEFAULT); attr (moCurrentAttr); break; // tabs case MarkupAttr.MARKUP_TAB_DEFAULT: tabDefault(sParameter, moCurrentAttr, markupAttr()); break; case MarkupAttr.MARKUP_TAB: break; case MarkupAttr.MARKUP_TAB_POSITION: tabSet (sParameter, moCurrentAttr, markupAttr()); break; case MarkupAttr.MARKUP_XHTML_LEFT: pendingTab(TextTab.TYPE_LEFT); break; case MarkupAttr.MARKUP_XHTML_CENTER: pendingTab(TextTab.TYPE_CENTRE); break; case MarkupAttr.MARKUP_XHTML_RIGHT: pendingTab(TextTab.TYPE_RIGHT); break; case MarkupAttr.MARKUP_TAB_ALIGN_DECIMAL: pendingTab(TextTab.TYPE_DECIMAL); break; case MarkupAttr.MARKUP_TAB_COUNT: Integer count = StringUtils.number (sParameter); if (count != null) { int tabs = count.intValue(); if (tabs > 0) { mnTabCount += tabs; } } break; case MarkupAttr.MARKUP_DIGITS: eParm = markupAttr().lookup (sParameter); int eDigits = TextAttr.DIGITS_LOCALE; switch (eParm) { case MarkupAttr.MARKUP_DIGITS_ARABIC: eDigits = TextAttr.DIGITS_ARABIC; break; case MarkupAttr.MARKUP_DIGITS_INDIC: eDigits = TextAttr.DIGITS_INDIC; break; } moCurrentAttr.digits (eDigits); break; case MarkupAttr.MARKUP_LIGATURE: eParm = markupAttr().lookup (sParameter); int eLigature; switch (eParm) { case MarkupAttr.MARKUP_LIGATURE_COMMON: eLigature = TextAttr.LIGATURE_COMMON; break; default: eLigature = TextAttr.LIGATURE_MINIMUM; break; } moCurrentAttr.ligature (eLigature); break; case MarkupAttr.MARKUP_CHAR_SPACING: oMeasure = TextMeasurement.fromString (sParameter, stringToUnit (markupAttr(), sParameter)); moCurrentAttr.charSpacing (oMeasure); break; case MarkupAttr.MARKUP_WORD_SPACING: oMeasure = TextMeasurement.fromString (sParameter, stringToUnit (markupAttr(), sParameter)); moCurrentAttr.wordSpacing (oMeasure); break; case MarkupAttr.MARKUP_HYPHENATION: eParm = markupAttr().lookup (sParameter); if (eParm == MarkupAttr.MARKUP_AUTO) { if (moCurrentAttr.hyphLevelEnable() && (moCurrentAttr.hyphLevel() == TextAttr.HYPHEN_OFF)) moCurrentAttr.hyphLevel (TextAttr.HYPHEN_NORMAL); } else { moCurrentAttr.hyphLevel (TextAttr.HYPHEN_OFF); } break; case MarkupAttr.MARKUP_HYPHENATION_LEVEL: eParm = markupAttr().lookup (sParameter); int eLevel = TextAttr.HYPHEN_OFF; switch (eParm) { case MarkupAttr.MARKUP_HYPHENATION_PREFERRED: eLevel = TextAttr.HYPHEN_PREFERRED; break; case MarkupAttr.MARKUP_NORMAL: eLevel = TextAttr.HYPHEN_NORMAL; break; case MarkupAttr.MARKUP_ALL: eLevel = TextAttr.HYPHEN_ALL; break; } if (eLevel != TextAttr.HYPHEN_OFF) { moCurrentAttr.hyphLevel (eLevel); } break; case MarkupAttr.MARKUP_HYPHENATION_LENGTH: StringBuilder sTokens = new StringBuilder (sParameter); int nMin[] = new int [3]; int nValues = 0; while (nValues < 3) { String sToken = StringUtils.parseToken (sTokens); if (sToken == null) { break; } Integer convert = StringUtils.number (sToken); if (convert == null) { break; } int result = convert.intValue(); if (result < 0) { break; } nMin[nValues] = result; nValues++; } if (nValues > 0) { moCurrentAttr.hyphMinWord (nMin[0]); if (nValues > 1) { moCurrentAttr.hyphMinPrefix (nMin[1]); if (nValues > 2) { moCurrentAttr.hyphMinSuffix (nMin[2]); } } } break; case MarkupAttr.MARKUP_HYPHENATION_ACRONYMS: boolean bAcronyms = markupAttr().lookup (sParameter) == MarkupAttr.MARKUP_AUTO; moCurrentAttr.hyphSuppressAcronyms (! bAcronyms); break; case MarkupAttr.MARKUP_HYPHENATION_NAMES: boolean bNames = markupAttr().lookup (sParameter) == MarkupAttr.MARKUP_AUTO; moCurrentAttr.hyphSuppressNames (! bNames); break; case MarkupAttr.MARKUP_LEADER_ALIGN: { int eAlign = TextAttr.LEADER_ALIGN_NONE; if (markupAttr().lookup (sParameter) == MarkupAttr.MARKUP_LEADER_ALIGN_PAGE) { eAlign = TextAttr.LEADER_ALIGN_PAGE; } moCurrentAttr.leaderAlign (eAlign); break; } case MarkupAttr.MARKUP_LEADER_PATTERN: { int ePattern = TextAttr.LEADER_PATTERN_SPACE; switch (markupAttr().lookup (sParameter)) { case MarkupAttr.MARKUP_LEADER_PATTERN_RULE: ePattern = TextAttr.LEADER_PATTERN_RULE; break; case MarkupAttr.MARKUP_LEADER_PATTERN_DOTS: ePattern = TextAttr.LEADER_PATTERN_DOTS; break; case MarkupAttr.MARKUP_LEADER_PATTERN_USE_CONTENT: case MarkupAttr.MARKUP_LEADER_PATTERN_USE_CONTENT2: ePattern = TextAttr.LEADER_PATTERN_USE_CONTENT; break; } moCurrentAttr.leaderPattern (ePattern); break; } case MarkupAttr.MARKUP_LEADER_PATTERN_WIDTH: oMeasure = TextMeasurement.fromString (sParameter); moCurrentAttr.leaderPatternWidth (oMeasure); break; case MarkupAttr.MARKUP_RULE_STYLE: { int eStyle = TextAttr.RULE_STYLE_SOLID; switch (markupAttr().lookup (sParameter)) { case MarkupAttr.MARKUP_NONE: eStyle = TextAttr.RULE_STYLE_NONE; break; case MarkupAttr.MARKUP_RULE_STYLE_DOTTED: eStyle = TextAttr.RULE_STYLE_DOTTED; break; case MarkupAttr.MARKUP_RULE_STYLE_DASHED: eStyle = TextAttr.RULE_STYLE_DASHED; break; } moCurrentAttr.ruleStyle (eStyle); break; } case MarkupAttr.MARKUP_RULE_THICKNESS: oMeasure = TextMeasurement.fromString (sParameter); moCurrentAttr.ruleThickness (oMeasure); break; case MarkupAttr.MARKUP_UNKNOWN: default: bCommandHandled = false; // not handled here break; } return bCommandHandled; } private boolean handleStylingAttributes (Attributes attributes, boolean bIsPara) { String sAttr; boolean bHasStyle = false; sAttr = getAttr (attributes, MarkupAttr.MARKUP_STYLE); if (sAttr != null) { parseStyleAttr (sAttr, false); bHasStyle = true; } sAttr = getAttr (attributes, MarkupAttr.MARKUP_DIRECTION); if (sAttr != null) { int eParm = markupAttr().lookup (sAttr); int eDirection = TextAttr.DIRECTION_NEUTRAL; switch (eParm) { case MarkupAttr.MARKUP_DIRECTION_LTR: eDirection = TextAttr.DIRECTION_LTR; break; case MarkupAttr.MARKUP_DIRECTION_RTL: eDirection = TextAttr.DIRECTION_RTL; break; } if (bIsPara) { moCurrentAttr.paraDirection (eDirection); } else { moCurrentAttr.direction (eDirection); } attr (moCurrentAttr); bHasStyle = true; } return bHasStyle; } private void initialize (TextAttr poAmbientAttr) { mbVersionDetermined = false; mbTextAccumulated = false; mbParaStarted = true; mePending = PENDING_NONE; mnTabCount = 0; mbAmbientSupplied = false; if (poAmbientAttr != null) { moAmbientAttr = poAmbientAttr; mbAmbientSupplied = true; } } private boolean handleStylingAttributes (Attributes attributes) { return handleStylingAttributes (attributes, false); } private void parseStyleAttr (String sAttr, boolean bIsInlineTag) { final int BIT_INDENT_LEFT = 0x001; final int BIT_SPACE_BEFORE = 0x002; final int BIT_INDENT_RIGHT = 0x004; final int BIT_SPACE_AFTER = 0x008; final int BIT_INDENT_FIRST_LINE = 0x010; final int BIT_LINE_HEIGHT = 0x020; final int BIT_JUSTIFY_VERT = 0x040; final int BIT_JUSTIFY = 0x080; final int BIT_TAB_DEFAULT = 0x100; final int BIT_ALL_PARA = 0x1FF; int nBits = 0; String sName = null; int nItalic = FontInfo.STYLE_UNKNOWN; int nWeight = FontInfo.WEIGHT_UNKNOWN; UnitSpan oSize = null; StyleInfo info = new StyleInfo (sAttr); // Watson# 1311258. Collect all of the font parameters before requesting a font for a style. while (extractStyleElement (info)) { String value = info.parameter.toString(); switch (info.commandEnum) { case MarkupAttr.MARKUP_FONT_NAME: sName = value; break; case MarkupAttr.MARKUP_FONT_WEIGHT: if (findAttr (value, MarkupAttr.MARKUP_FONT_BOLD)) nWeight = FontInfo.WEIGHT_BOLD; else if (findAttr (value, MarkupAttr.MARKUP_NORMAL)) nWeight = FontInfo.WEIGHT_NORMAL; break; case MarkupAttr.MARKUP_FONT_STYLE: if (findAttr (value, MarkupAttr.MARKUP_FONT_ITALIC)) nItalic = FontInfo.STYLE_ITALIC; else if (findAttr (value, MarkupAttr.MARKUP_NORMAL)) nItalic = FontInfo.STYLE_UPRIGHT; break; case MarkupAttr.MARKUP_FONT_SIZE: oSize = new UnitSpan (value, stringToUnit (markupAttr(), value), false); break; } } // Send the font request with all of the font-related properties from the style attribute in one call. if ((sName != null) || (nWeight != FontInfo.WEIGHT_UNKNOWN) || (nItalic != FontInfo.STYLE_UNKNOWN) || (oSize != null)) { moCurrentAttr.typeface (sName, oSize, nWeight, nItalic); } // Now process complete style attribute, ignoring the above font-related properties that have already been processed. info.offset = 0; while (extractStyleElement (info)) { if ((info.commandEnum != MarkupAttr.MARKUP_FONT_NAME) && (info.commandEnum != MarkupAttr.MARKUP_FONT_WEIGHT) && (info.commandEnum != MarkupAttr.MARKUP_FONT_STYLE) && (info.commandEnum != MarkupAttr.MARKUP_FONT_SIZE)) { onCommand (info.commandEnum, info.parameter.toString()); } switch (info.commandEnum) { case MarkupAttr.MARKUP_INDENT_LEFT: nBits |= BIT_INDENT_LEFT; break; case MarkupAttr.MARKUP_SPACE_BEFORE: nBits |= BIT_SPACE_BEFORE; break; case MarkupAttr.MARKUP_INDENT_RIGHT: nBits |= BIT_INDENT_RIGHT; break; case MarkupAttr.MARKUP_SPACE_AFTER: nBits |= BIT_SPACE_AFTER; break; case MarkupAttr.MARKUP_INDENT_FIRST_LINE: nBits |= BIT_INDENT_FIRST_LINE; break; case MarkupAttr.MARKUP_LINE_HEIGHT: nBits |= BIT_LINE_HEIGHT; break; case MarkupAttr.MARKUP_JUSTIFY_VERT: nBits |= BIT_JUSTIFY_VERT; break; case MarkupAttr.MARKUP_JUSTIFY: nBits |= BIT_JUSTIFY; break; case MarkupAttr.MARKUP_TAB_DEFAULT: nBits |= BIT_TAB_DEFAULT; break; } } // At one time, FF99 put out HTML with no version number. The following // heuristic attempts to identify FF99 by checking for all paragraph // attributes on the first style attribute encountered. if (! mbVersionDetermined) { mbVersionDetermined = true; if (nBits == BIT_ALL_PARA) { switchToFF99Mode(); } } flushPendingText (bIsInlineTag ? PENDING_DEFAULT : PENDING_BREAK); attr (moCurrentAttr); } private static int stringToUnit (MarkupAttr pMarkupAttr, String sString) { int lStart = StringUtils.skipOver (sString, gsNumeric, 0); String sUnit = sString.substring (lStart); if (sUnit.equals (pMarkupAttr.lookup (MarkupAttr.MARKUP_CM))) { return UnitSpan.CM_250K; } else if (sUnit.equals (pMarkupAttr.lookup (MarkupAttr.MARKUP_PT))) { return UnitSpan.POINTS_1K; } else if (sUnit.equals (pMarkupAttr.lookup (MarkupAttr.MARKUP_INCHES))) { return UnitSpan.INCHES_72K; } else if (sUnit.equals (pMarkupAttr.lookup (MarkupAttr.MARKUP_MM))) { return UnitSpan.MM_25K; } else { return UnitSpan.UNIT_UNKNOWN; } } private static int stringToAlign (MarkupAttr pMarkupAttr, String sString) { if (sString.equals (pMarkupAttr.lookup (MarkupAttr.MARKUP_XHTML_LEFT))) { return TextTab.TYPE_LEFT; } else if (sString.equals (pMarkupAttr.lookup (MarkupAttr.MARKUP_XHTML_RIGHT))) { return TextTab.TYPE_RIGHT; } else if (sString.equals (pMarkupAttr.lookup (MarkupAttr.MARKUP_XHTML_CENTER))) { return TextTab.TYPE_CENTRE; } else if (sString.equals (pMarkupAttr.lookup (MarkupAttr.MARKUP_TAB_ALIGN_DECIMAL))) { return TextTab.TYPE_DECIMAL; } else { return TextTab.TYPE_LEFT; } } private void updateWeight (String sString) { if (findAttr (sString, MarkupAttr.MARKUP_FONT_BOLD)) { moCurrentAttr.weight (FontInfo.WEIGHT_BOLD); } else if (findAttr (sString, MarkupAttr.MARKUP_NORMAL)) { moCurrentAttr.weight (FontInfo.WEIGHT_NORMAL); } } private void updateItalic (String sString) { if (findAttr (sString, MarkupAttr.MARKUP_FONT_ITALIC)) { moCurrentAttr.italic (true); } else if (findAttr (sString, MarkupAttr.MARKUP_NORMAL)) { moCurrentAttr.italic (false); } } private void updateFont (String sString) { updateWeight (sString); updateItalic (sString); int nFoundAt = sString.indexOf (' '); int nOffset = 0; while (nFoundAt >= nOffset) { String sCmd = sString.substring (nOffset, nFoundAt); //we've already dealt with style and weight int eCmd = markupAttr().lookup (sCmd); if ((eCmd != MarkupAttr.MARKUP_FONT_BOLD) && (eCmd != MarkupAttr.MARKUP_NORMAL) && (eCmd != MarkupAttr.MARKUP_FONT_ITALIC)) { char c = (sCmd.length() == 0) ? '\0' : sCmd.charAt (0); if ((c >= '0') && (c <= '9')) { //assume this is the size //check for line-height int nFoundLH = sCmd.indexOf ('/'); if (nFoundLH >= 0) { String sLineHeight = sCmd.substring (nFoundLH + 1); onCommand (MarkupAttr.MARKUP_LINE_HEIGHT, sLineHeight); sCmd = sCmd.substring (nFoundLH); } onCommand (MarkupAttr.MARKUP_FONT_SIZE, sCmd); } else { //assume this is the typeface if ((c == '\'') || (c == '"')) { nFoundAt = sString.indexOf (c, nFoundAt + 1); sCmd = sString.substring (nOffset, nFoundAt); } onCommand (MarkupAttr.MARKUP_FONT_NAME, sCmd); } } nOffset = nFoundAt + 1; nFoundAt = sString.indexOf (' ', nOffset); } } private boolean findAttr (String sCommand, int eTag) { return sCommand.contains(markupAttr().lookup (eTag)); // TODO: is/should this be case sensitive? } // private boolean compareAttr (String sAttr, int eTag) { // return sAttr.compareToIgnoreCase (markupAttr().lookup (eTag)) == 0; // TODO: case sensitivity? // } private void embed (String sExpression, int eEmbedType, int eEmbedMode) { // embedded content shouldn't always be resolved TextField oTextField = new TextField (eEmbedType, eEmbedMode, sExpression); // oTextField.attrPool (AttrPool()); oTextField.fontService (fontService()); if (mpoResolver != null) { mpoResolver.embedContent (oTextField); } field (oTextField); } //---------------------------------------------------------------------- // // SwitchToFF99Mode - Legacy: Apparently FF99 expects all // spaces to be rendered, so if during processing of the // HTML, we determine that this is a FF99 form, we need to // update our stack to force spaces. // //---------------------------------------------------------------------- private void switchToFF99Mode () { for (int i = 0; i < moStack.size(); i++) { if (moStack.frameAt(i).meSpace <= SPACE_NORMAL) { moStack.frameAt(i).meSpace = SPACE_FORCED; } } } private boolean extractStyleElement (StyleInfo info) { final int nCommandInitialAlloc = 32; final int nParameterInitialAlloc = 128; if (mIterator == null) { mIterator = new UniCharIterator(); } mIterator.attach (info.attrValue, info.offset); info.commandEnum = MarkupAttr.MARKUP_UNKNOWN; while ((info.commandEnum == MarkupAttr.MARKUP_UNKNOWN) && (mIterator.getIndex() < info.attrValue.length())) { info.command.delete (0, info.command.length()); info.parameter.delete (0, info.parameter.length()); int nAlloc = info.attrValue.length() - info.offset; if (nAlloc < nCommandInitialAlloc) { nAlloc = nCommandInitialAlloc; } info.command.ensureCapacity (nAlloc); int cQuote = '\0'; int nQuoteStart = 0; final int STATE_PRE_COMMAND = 0; final int STATE_COMMAND = 1; final int STATE_POST_COMMAND = 2; final int STATE_PRE_PARAMETER = 3; final int STATE_PARAMETER = 4; final int STATE_DONE = 5; int eState = STATE_PRE_COMMAND; while ((mIterator.getIndex() < info.attrValue.length()) && (eState != STATE_DONE)) { int nPrevOffset = mIterator.getIndex(); int c = mIterator.next(); if (cQuote != '\0') { if (c == cQuote) { cQuote = '\0'; switch (eState) { case STATE_PRE_PARAMETER: case STATE_PARAMETER: info.parameter.append(info.attrValue, nQuoteStart, nPrevOffset); break; } } } else if ((c == '"') || (c == '\'')) { nQuoteStart = nPrevOffset + 1; cQuote = c; } else { switch (eState) { case STATE_PRE_COMMAND: switch (c) { case ' ': case ':': case ';': break; default: UniCharIterator.append (info.command, c); eState = STATE_COMMAND; } break; case STATE_COMMAND: switch (c) { case ' ': eState = STATE_POST_COMMAND; break; case ';': eState = STATE_DONE; break; case ':': eState = STATE_PRE_PARAMETER; break; default: UniCharIterator.append (info.command, c); } break; case STATE_POST_COMMAND: switch (c) { case ';': eState = STATE_DONE; break; case ':': eState = STATE_PRE_PARAMETER; break; } break; case STATE_PRE_PARAMETER: switch (c) { case ';': eState = STATE_DONE; break; case ' ': break; default: nAlloc = info.attrValue.length() - mIterator.getIndex(); if (nAlloc < nParameterInitialAlloc) { nAlloc = nParameterInitialAlloc; } info.parameter.ensureCapacity (nAlloc); UniCharIterator.append (info.parameter, c); eState = STATE_PARAMETER; } break; case STATE_PARAMETER: switch (c) { case ';': eState = STATE_DONE; break; default: UniCharIterator.append (info.parameter, c); } break; } } } if (eState != STATE_PRE_COMMAND) { info.command.append (':'); // so we don't confuse styles with tags and attributes String cmd = info.command.toString(); info.commandEnum = markupAttr().lookup (cmd); int nLength; for (nLength = info.parameter.length(); nLength > 0; ) { nLength--; // length to index if (! isXHTMLSpace (info.parameter.charAt (nLength))) { nLength++; // back to a length again break; } } info.parameter.delete (nLength, info.parameter.length()); } } info.offset = mIterator.getIndex(); return info.commandEnum != MarkupAttr.MARKUP_UNKNOWN; } private boolean inUnknownElement () { return moStack.top().meTag == MarkupAttr.MARKUP_UNKNOWN; } private static boolean isXHTMLSpace (int c) { return (c == ' ') || (c == '\n') || (c == '\r') || (c == '\t') || (c == '\u000C'); // return: should never happen // formfeed: should never happen } private void updateSpaceStatus (int eTag, boolean bEnd) { int eSpace = moStack.top().meSpace; if ((eTag == MarkupAttr.MARKUP_PARAGRAPH_START) || (eTag == MarkupAttr.MARKUP_BODY) || (eTag == MarkupAttr.MARKUP_DIV) || (eTag == MarkupAttr.MARKUP_HTML)) { if ((! bEnd) || (eTag == MarkupAttr.MARKUP_BODY)) { if ((eSpace == SPACE_NORMAL) || (eSpace == SPACE_SUPPRESS)) { moStack.top().meSpace = legacyBlankLineMode() // suppress at start of block ? SPACE_SUPPRESS_BREAK : SPACE_SUPPRESS; } mePending = PENDING_NONE; } else { if (mePending == PENDING_SPACE) { flushPendingText (PENDING_DEFAULT); } } } else if (eTag == MarkupAttr.MARKUP_BREAK) { if (eSpace == SPACE_NORMAL) { moStack.top().meSpace = SPACE_SUPPRESS; } } else { if (eSpace < SPACE_NORMAL) { moStack.top().meSpace = SPACE_NORMAL; } } } private void updateSpaceStatus (int eTag) { updateSpaceStatus (eTag, false); } private void flushPendingText (int ePending) { // special case: break and div only if (ePending == PENDING_DEFAULT) { ePending = legacyBlankLineMode() ? PENDING_DIV : PENDING_NONE; } if ((mePending >= ePending) || (legacyBlankLineMode() && (ePending == PENDING_NONE))) { // special case: break and div only String psText = null; switch (mePending) { case PENDING_DIV: if (legacyBlankLineMode()) { if (mbTextAccumulated) { psText = gsNewLine; } } break; case PENDING_BREAK: psText = gsNewLine; break; case PENDING_SPACE: if ((! legacyBlankLineMode()) || (ePending != PENDING_NONE)) { psText = " "; // special case: break and div only } break; } if (psText != null) { pushAttr(); attr (moPendingAttr); text (psText); if (moStack.top().meSpace == SPACE_NORMAL) { moStack.top().meSpace = SPACE_SUPPRESS; } popAttr(); mePending = PENDING_NONE; } } } private static boolean extractHTMLVersion (String sVersion, int[] nVersion) { if (sVersion.length() == 0) { return false; } int nVersionIndex; for (nVersionIndex = 0; nVersionIndex < 4; nVersionIndex++) { nVersion[nVersionIndex] = 0; } int nChar = 0; int nStartChar = 0; nVersionIndex = 0; while (nChar < sVersion.length()) { int nEndChar = nChar; char c = sVersion.charAt (nChar); nChar++; if (c == '.') { if (nEndChar >= nStartChar) { String sComponent = sVersion.substring (nStartChar, nEndChar); Integer value = StringUtils.number (sComponent); if (value == null) { return false; } nVersion[nVersionIndex] = value.intValue(); } nVersionIndex++; if (nVersionIndex >= 4) { return true; } nStartChar = nChar; } } return true; } private static int compareHTMLVersions (int[] nVersion1, int[] nVersion2) { for (int i = 0; i < 4; i++) { if (nVersion1[i] < nVersion2[i]) { return -1; } if (nVersion1[i] > nVersion2[i]) { return 1; } } return 0; } private static TextTab extractTab (MarkupAttr markupAttr, String measurement, int tabType) { UnitSpan oValue = new UnitSpan (measurement, stringToUnit (markupAttr, measurement), false); return new TextTab (oValue, tabType); } private static TextTabList getTabList (TextAttr attr) { return ((attr == null) || (! attr.tabsEnable())) ? new TextTabList () : new TextTabList (attr.tabs()); } private void commitTabs (LeaderInfo poLeaderInfo) { if (poLeaderInfo != null) { moCurrentAttr.leaderContent (poLeaderInfo.moContent); attr (moCurrentAttr); } StringBuilder sTabs = new StringBuilder(); sTabs.ensureCapacity (mnTabCount); for (int i = 0; i < mnTabCount; i++) { sTabs.append ('\t'); } text (sTabs.toString()); mnTabCount = 0; closeScopedBlock(); popAttr(); moStack.top().meTag = MarkupAttr.MARKUP_UNKNOWN; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy