com.adobe.xfa.text.markup.MarkupXHTMLOut Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
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.TextTab;
import com.adobe.xfa.text.TextTabList;
import com.adobe.xfa.ut.UnitSpan;
import com.adobe.xfa.ut.Version;
/**
* @exclude from published api.
*/
public class MarkupXHTMLOut extends MarkupEngineOut {
private final StringBuilder mpTranslation = new StringBuilder();
private final int meDefaultUnits;
private boolean mbNewPara;
private boolean mbStarted;
private boolean mbParaHasContent;
private final boolean mbIncludeAmbientAttrs;
private boolean mbNeedSpanEnd;
private boolean mbSubordinate;
private final boolean mbBreakupOutput; // If TRUE, add carriage returns where possible
private boolean mbExpandEmbed;
private final boolean mbRoundTextSize;
private boolean mbRequiresReset;
private boolean mbTrailingBreak;
private TextAttr moParaAttr;
private final TextAttr moSpanAttr = new TextAttr();
private final TextAttr moAmbientAttr;
private final TextAttr moCSSDefault = new TextAttr (true);
private final static UnitSpan ROUND_SIZE = new UnitSpan (UnitSpan.POINTS_1K, 50);
private final static String gsDblSp = " ";
private final static String gsNBSP = " ";
private final static String gsTab = "\t";
private final static String gsNL = "\n";
private final static String gsBR = "
";
private final static String gsNineSpaces = " ";
private final static String gsBodyStart1 = "";
private final static String gsBodyEnd = "";
private final static String gsParaOpenTag = "";
private final static String gsSpaceRunSingle = " ";
private final static String gsTabCountStart = "";
private final static String gsStyleEndTag = "\"/>";
private final static String gsTagEnd = ">";
private final static String gsTextDirectionStart = "dir=\"";
private final static String gsTextDirectionEnd = "\"";
/**
* Constructor.
* @param pMarkupAttr - (optional) Markup attribute table to use instead
* of the defalt. It is strongly recommended that you pass NULL
* (default).
* eDefaultUnits - (optional) Default units. All non-text unit values
* are converted to this unit before writing. Default is inches.
* @param poAmbientAttrs - (optional) Ambient attributes that can be
* inherited from the environment and therefore don't need to be written
* out. Default is NULL, no ambient attributes.
* @param bIncludeAmbientAttrs - TRUE if ambient attributes are to be
* used to make up for attributes missing in the source text; FALSE
* (dafault) if ambient attributes can be suppressed altogether.
* bBreakOutput - TRUE (default) to put in the occasional new-line when
* allowed in order to prevent one very long line. FALSE to place all
* the output on a single line.
* @param bExpandEmbed - TRUE (default) if embedded field content is to
* be expanded in-line; FALSE to generate field references.
* @param bRoundTextSize - TRUE (default) if the text sizes are to be
* rounded to the nearest 0.05pt.
*/
public MarkupXHTMLOut (MarkupAttr pMarkupAttr, int eDefaultUnits, TextAttr poAmbientAttrs, boolean bIncludeAmbientAttrs, boolean bBreakupOutput, boolean bExpandEmbed, boolean bRoundTextSize) {
super ((pMarkupAttr == null) ? MarkupXHTMLAttr.getDefault() : pMarkupAttr);
mbIncludeAmbientAttrs = bIncludeAmbientAttrs;
mbBreakupOutput = bBreakupOutput;
mbExpandEmbed = bExpandEmbed;
mbRoundTextSize = bRoundTextSize;
mbRequiresReset = true;
mbTrailingBreak = false;
// Check for various attributes which might not be specified, but which
// we know are CSS and text services defaults.
moCSSDefault.special (TextMeasurement.zero());
moCSSDefault.justifyV (TextAttr.JUST_V_TOP);
moCSSDefault.justifyH (TextAttr.JUST_H_LEFT);
moCSSDefault.tabs (new TextTabList()); // TODO: is it necessary to create these?
moCSSDefault.baselineShift (new TextBaselineShift()); // TODO: is it necessary to create these?
moCSSDefault.colour (GFXColour.black());
moCSSDefault.underline (GFXTextAttr.UNDER_NONE);
moCSSDefault.weight (FontInfo.WEIGHT_NORMAL);
moCSSDefault.italic (false);
moCSSDefault.strikeout (GFXTextAttr.STRIKEOUT_NONE);
moCSSDefault.marginL (TextMeasurement.zero());
moCSSDefault.marginR (TextMeasurement.zero());
moCSSDefault.hyphLevel (TextAttr.HYPHEN_OFF);
moCSSDefault.leaderAlign (TextAttr.LEADER_ALIGN_NONE);
moCSSDefault.leaderPattern (TextAttr.LEADER_PATTERN_SPACE);
moCSSDefault.ruleStyle (TextAttr.RULE_STYLE_SOLID);
moCSSDefault.ruleThickness (new TextMeasurement (new UnitSpan (UnitSpan.POINTS_1K, 1000)));
moCSSDefault.horizontalScale (1.0);
moCSSDefault.verticalScale (1.0);
// no reasonable default for these
moCSSDefault.spaceBeforeEnable (false);
moCSSDefault.spaceAfterEnable (false);
moCSSDefault.typefaceEnable (false);
moCSSDefault.sizeEnable (false);
moCSSDefault.spacingEnable (false); // Not enabled == single spacing
moParaAttr = new TextAttr (moCSSDefault);
if (poAmbientAttrs == null) {
moAmbientAttr = new TextAttr();
} else {
moAmbientAttr = new TextAttr (poAmbientAttrs);
}
moAmbientAttr.addDisabled (moCSSDefault);
meDefaultUnits = eDefaultUnits;
startDoc();
}
public MarkupXHTMLOut (MarkupAttr pMarkupAttr, int eDefaultUnits, TextAttr poAmbientAttrs, boolean bIncludeAmbientAttrs, boolean bBreakupOutput, boolean bExpandEmbed) {
this (pMarkupAttr, eDefaultUnits, poAmbientAttrs, bIncludeAmbientAttrs, bBreakupOutput, bExpandEmbed, true);
}
public MarkupXHTMLOut (MarkupAttr pMarkupAttr, int eDefaultUnits, TextAttr poAmbientAttrs, boolean bIncludeAmbientAttrs, boolean bBreakupOutput) {
this (pMarkupAttr, eDefaultUnits, poAmbientAttrs, bIncludeAmbientAttrs, bBreakupOutput, true, true);
}
public MarkupXHTMLOut (MarkupAttr pMarkupAttr, int eDefaultUnits, TextAttr poAmbientAttrs, boolean bIncludeAmbientAttrs) {
this (pMarkupAttr, eDefaultUnits, poAmbientAttrs, bIncludeAmbientAttrs, true, true, true);
}
public MarkupXHTMLOut (MarkupAttr pMarkupAttr, int eDefaultUnits, TextAttr poAmbientAttrs) {
this (pMarkupAttr, eDefaultUnits, poAmbientAttrs, false, true, true, true);
}
public MarkupXHTMLOut (MarkupAttr pMarkupAttr, int eDefaultUnits) {
this (pMarkupAttr, eDefaultUnits, null, false, true, true, true);
}
public MarkupXHTMLOut (MarkupAttr pMarkupAttr) {
this (pMarkupAttr, UnitSpan.INCHES_72K, null, false, true, true, true);
}
public MarkupXHTMLOut () {
this (null, UnitSpan.INCHES_72K, null, false, true, true, true);
}
public void reset () {
if (mbRequiresReset) {
super.reset();
startDoc();
}
}
/**
* Obtain the resulting markup.
* @return Generated markup, as a string.
*/
public String translation () {
if (! mbSubordinate) {
endPara();
mpTranslation.append (gsBodyEnd);
}
return mpTranslation.toString();
}
public void text (String oStrText) {
if (oStrText.length() == 0) {
return;
}
mbTrailingBreak = false;
// translate runs of spaces to a span that contains enough   chars
// + one space at the end to equal the # of spaces in the text run
StringBuilder sText = new StringBuilder (oStrText); // TODO: toXML()
int nFoundAt;
int nOffset = 0;
nFoundAt = sText.indexOf (gsDblSp);
boolean bNewRun = true;
while (nFoundAt > 0) {
if (bNewRun) {
sText.insert (nFoundAt, gsSpaceRunStart);
nFoundAt = sText.indexOf (gsDblSp);
bNewRun = false;
}
sText.replace (nFoundAt, 1, gsNBSP);
nOffset = nFoundAt + gsNBSP.length();
// Replaced comparison of two character substring because taking a
// substring could generate invalid UTF-8 and cause an assert in
// string.cpp. RD 08-Apr-2005.
if ((sText.charAt (nOffset) != ' ') || (sText.charAt (nOffset + 1) != ' ')) {
sText.insert (nOffset + 1, gsSpanEnd);
bNewRun = true;
}
nFoundAt = sText.indexOf (gsDblSp, nOffset);
}
// Be sure and preserve single spaces at the beginning and end of our text
// We only need to protect an starting space if it's the first one in a
// paragraph or it's the only character in the span.
if ((mbNewPara || sText.length() == 1) && (sText.length() > 0 && sText.charAt(0) == ' ')) {
boolean bFoundNonWhiteSpace = false;
for (int i = 1; i < sText.length(); i++) {
if (sText.charAt (i) != ' ') {
bFoundNonWhiteSpace = true;
break;
}
}
if (bFoundNonWhiteSpace) {
sText.replace (0, 1, gsSpaceRunSingle);
}
}
if (sText.charAt (sText.length()-1) == ' ') {
sText.replace (sText.length() - 1, 1, gsSpaceRunSingle);
}
nOffset = 0;
nFoundAt = sText.indexOf (gsNL);
while (nFoundAt >= 0) {
if (nFoundAt == sText.length() - 1) {
mbTrailingBreak = true;
}
sText.replace (nFoundAt, 1, gsBR);
nOffset = nFoundAt + gsBR.length();
nFoundAt = sText.indexOf (gsNL, nOffset);
}
// Replace tabs with spans that will preserve the tabs
// The style attribute will be used to replace the tabs, the span content
// will be discarded, but should be somewhat useful to browsers.
nOffset = 0;
nFoundAt = sText.indexOf (gsTab);
while (nFoundAt >= 0) {
// Find out how many consecutive tabs we're dealing with
int nTabCount = 1;
int nTabPos = nFoundAt;
while (sText.charAt (++nTabPos) == '\t') {
nTabCount++;
}
StringBuilder sTabReplace = new StringBuilder (gsTabCountStart);
sTabReplace.append (Integer.toString (nTabCount));
sTabReplace.append (gsStyleEnd);
if (moSpanAttr.leaderPatternEnable() && (moSpanAttr.leaderPattern() == TextAttr.LEADER_PATTERN_USE_CONTENT) && moSpanAttr.leaderContentEnable()) {
MarkupXHTMLOut oMarkup = new MarkupXHTMLOut (markupAttr(), meDefaultUnits, moSpanAttr);
oMarkup.mbSubordinate = true;
moSpanAttr.leaderContent().markup (oMarkup, moSpanAttr, true);
sTabReplace.append (oMarkup.translation());
} else {
for (int i = 0; i < nTabCount; i++) {
// For each tab, add 9 spaces (a pathetic replacement, but
// better than nothing)
sTabReplace.append (gsNineSpaces);
}
}
sTabReplace.append (gsSpanEnd);
sText.replace (nFoundAt, nTabCount, sTabReplace.toString());
nOffset = nTabPos + sTabReplace.length();
nFoundAt = sText.indexOf (gsTab, nOffset);
}
mbStarted = true;
startPara();
// Create the necessary , and nodes.
boolean bSpan = false;
StringBuilder sMarkup = new StringBuilder();
String sTextDirection = getTextDirection (false);
if (sTextDirection.length() > 0) {
bSpan = true;
sMarkup.append (gsSpanOpenTag);
sMarkup.append (' ');
sMarkup.append (gsTextDirectionStart);
sMarkup.append (sTextDirection);
sMarkup.append ('"');
}
String sSpanStyle = getSpanStyle (false);
if (sSpanStyle.length() > 0) {
if (bSpan) {
sMarkup.append (' ');
sMarkup.append (gsStyleAttr);
} else {
sMarkup.append (gsStyleStart);
}
bSpan = true;
sMarkup.append (sSpanStyle);
sMarkup.append ('"');
}
if (bSpan) {
sMarkup.append (">");
}
sMarkup.append (sText);
//in case of conflict between adding a text decoration and turning another off
if (mbNeedSpanEnd) {
sMarkup.append (gsSpanEnd);
}
if (bSpan) {
sMarkup.append (gsSpanEnd);
}
mpTranslation.append (sMarkup);
mbNeedSpanEnd = false;
}
public void attr (TextAttr oAttr) {
// If we haven't started our first paragraph yet, initialize our
// attributes here.
//
// We might encounter several attribute changes before we get to any
// text. So make sure we initialize the starting paragraph attributes
// only once, and from then on merge attributes.
if (! mbStarted) {
mbStarted = true;
moParaAttr = oAttr;
pruneParaAttrs();
moSpanAttr.copyFrom (moParaAttr);
} else {
if (mbParaHasContent) {
// Merge in the new attributes so they'll take effect on our next span
moSpanAttr.override (oAttr);
} else {
// Or... if we're outside a paragraph, merge these attributes into
// our paragraph attributes.
moParaAttr.override (oAttr);
pruneParaAttrs();
moSpanAttr.copyFrom (moParaAttr);
}
}
}
public void para () {
endPara();
mbNewPara = true;
mbParaHasContent = false;
}
public void field (TextField poField) {
if (mbExpandEmbed) {
// Keep the embed field from blowing away the current
// translated contents.
mbRequiresReset = false;
poField.markup (this, null, false, true);
mbRequiresReset = true;
}
else {
startPara(); // put embeds inside paragraphs just like regular text
// Watson #1043623: "Cannot apply rich text solely to runtime property text w/ floating field."
// Need to add span node with the formatting options around the embedded field.
// Create the necessary nodes.
boolean bSpan = false;
StringBuilder sMarkup = new StringBuilder();
String sSpanStyle = getSpanStyle (false);
if (sSpanStyle.length() > 0) {
if (bSpan) {
sMarkup.append (' ');
sMarkup.append (gsStyleAttr);
} else {
sMarkup.append (gsStyleStart);
}
bSpan = true;
sMarkup.append (sSpanStyle);
sMarkup.append (gsStyleEnd);
}
mpTranslation.append (sMarkup);
mpTranslation.append (gsEmbedStartType);
switch (poField.getEmbedType())
{
case TextField.EMBEDTYPE_SOM:
mpTranslation.append (gsEmbedSom);
break;
case TextField.EMBEDTYPE_URI:
mpTranslation.append (gsEmbedUri);
break;
}
mpTranslation.append (gsEmbedAddMode);
switch (poField.getEmbedMode()) {
case TextField.EMBED_RAW:
mpTranslation.append (gsEmbedRaw);
break;
case TextField.EMBED_FORMATTED:
mpTranslation.append (gsEmbedFormatted);
break;
}
mpTranslation.append (gsEmbedAddEmbed);
mpTranslation.append (poField.getExpression());
mpTranslation.append (gsStyleEndTag);
if (bSpan) {
mpTranslation.append (gsSpanEnd);
}
}
}
public boolean issueFirstPara () {
return true;
}
public void expandEmbed (boolean bExpand) {
mbExpandEmbed = bExpand;
}
private void startDoc () {
mbNewPara = ! mbSubordinate;
mbStarted = false;
mbParaHasContent = false;
mpTranslation.setLength(0);
if (! mbSubordinate) {
mpTranslation.append (gsBodyStart1);
mpTranslation.append (Version.getImplementation());
mpTranslation.append (gsBodyStart2);
}
}
private void addAttr (StringBuilder sStyle, int eCommand, int eParameter) {
if ((sStyle.length() > 0) && (sStyle.charAt (sStyle.length()-1) != ';')) {
sStyle.append (';');
}
sStyle.append (getAttr (eCommand));
if (eParameter != MarkupAttr.MARKUP_UNKNOWN) {
sStyle.append (getAttr (eParameter));
}
}
private void addAttr (StringBuilder sStyle, int eCommand) {
addAttr (sStyle, eCommand, MarkupAttr.MARKUP_UNKNOWN);
}
private String getSpanStyle (boolean bIncludeAll) {
StringBuilder sStyle = new StringBuilder();
// Get a text attribute which specifies only those attributes that are
// different than the paragraph attributes.
TextAttr oAttr = new TextAttr (moSpanAttr);
// Check whether we want to skip those attributes already specified
// by the paragraph attributes.
if (bIncludeAll) {
if (mbIncludeAmbientAttrs) {
// Display all ambient attributes that don't match CSS defaults
TextAttr oAmbient = new TextAttr (moAmbientAttr);
oAmbient.dropSame (moCSSDefault);
oAttr.addDisabled (oAmbient);
} else {
// Don't include ambient attributes.
reconcileAttrs (moAmbientAttr, oAttr);
}
}
else {
// Set up the paragraph attributes so they represent the ambient settings
// as well so that spans don't re-specify the ambient settings.
TextAttr oParaAttr = new TextAttr (moParaAttr);
if (!mbIncludeAmbientAttrs) {
oParaAttr.addDisabled (moAmbientAttr);
}
reconcileAttrs (oParaAttr, oAttr);
}
if (oAttr.baselineShiftEnable()) {
addAttr (sStyle, MarkupAttr.MARKUP_JUSTIFY_VERT);
sStyle.append (oAttr.baselineShift().getString (false));
}
if (oAttr.typefaceEnable()) {
boolean bWhitespace = false;
addAttr (sStyle, MarkupAttr.MARKUP_FONT_NAME);
if (oAttr.typeface().indexOf (' ') > 0) {
bWhitespace = true;
}
if (bWhitespace) {
sStyle.append ('\'');
}
sStyle.append (oAttr.typeface());
if (bWhitespace) {
sStyle.append ('\'');
}
}
if (oAttr.sizeEnable()) {
// Write out the size only if it's not zero (or very close to zero)
//
UnitSpan oSize = oAttr.size();
// Vantive 637378: Rounding to 0.5pt too coarse; changed to 0.05pt. Not
// clear why rounding is here in the first place, but deemed safer to
// leave it in than remove it altogether. RD 14-Apr-2004.
if (mbRoundTextSize) {
oSize = oSize.round (ROUND_SIZE);
}
UnitSpan oCompareSize = bIncludeAll ? moAmbientAttr.size() : moParaAttr.size();
if (mbRoundTextSize) {
oCompareSize = oCompareSize.round (ROUND_SIZE); // TODO: C++ implementation rounds this differently
}
if ((oSize.value() != 0) && (bIncludeAll || (! oSize.equals (oCompareSize)))) {
addAttr (sStyle, MarkupAttr.MARKUP_FONT_SIZE);
addUnitSpan (oSize, sStyle, false, true);
}
}
if (oAttr.horizontalScaleEnable()) {
addAttr (sStyle, MarkupAttr.MARKUP_FONT_HORIZONTAL_SCALE);
sStyle.append (TextAttr.formatPercent (oAttr.horizontalScale()));
}
if (oAttr.verticalScaleEnable()) {
addAttr (sStyle, MarkupAttr.MARKUP_FONT_VERTICAL_SCALE);
sStyle.append (TextAttr.formatPercent (oAttr.verticalScale()));
}
// Colour
if (oAttr.colourEnable()) {
// Can't assume that all clients use a scale of 255.
GFXColour oColour = oAttr.colour();
oColour = oColour.scale (255);
addAttr (sStyle, MarkupAttr.MARKUP_COLOUR);
sStyle.append ('#');
addColour (oColour.r(), sStyle);
addColour (oColour.g(), sStyle);
addColour (oColour.b(), sStyle);
}
if (oAttr.weightEnable()) {
addAttr (sStyle, MarkupAttr.MARKUP_FONT_WEIGHT);
if (oAttr.weight() >= FontInfo.WEIGHT_BOLD) {
sStyle.append (getAttr (MarkupAttr.MARKUP_FONT_BOLD));
} else {
sStyle.append (getAttr (MarkupAttr.MARKUP_NORMAL));
}
}
// Italic
if (oAttr.italicEnable()) {
addAttr (sStyle, MarkupAttr.MARKUP_FONT_STYLE);
if (oAttr.italic()) {
sStyle.append (getAttr (MarkupAttr.MARKUP_FONT_ITALIC));
} else {
sStyle.append (getAttr (MarkupAttr.MARKUP_NORMAL));
}
}
// Underline and strikeout must be written out together, since they
// use the same text-decoration attribute.
if (oAttr.underlineEnable() || oAttr.strikeoutEnable()) {
addAttr (sStyle, MarkupAttr.MARKUP_TEXT_DECORATION);
GFXDecorationInfo underline = GFXTextAttr.extractDecoration (oAttr.underline());
GFXDecorationInfo lineThrough = GFXTextAttr.extractDecoration (oAttr.strikeout());
if ((underline == null) && (lineThrough == null)) {
sStyle.append (getAttr (MarkupAttr.MARKUP_NONE));
}
else {
if (underline != null) {
if (underline.mCount == 1) {
if (underline.mType == GFXDecorationInfo.DECORATE_WORD) {
sStyle.append (getAttr (MarkupAttr.MARKUP_UNDERLINE_WORD));
} else {
sStyle.append (getAttr (MarkupAttr.MARKUP_UNDERLINE));
}
} else {
if (underline.mType == GFXDecorationInfo.DECORATE_WORD) {
sStyle.append (getAttr (MarkupAttr.MARKUP_UNDERLINE_DOUBLE));
sStyle.append (' ');
sStyle.append (getAttr (MarkupAttr.MARKUP_UNDERLINE_WORD));
} else {
sStyle.append (getAttr (MarkupAttr.MARKUP_UNDERLINE_DOUBLE));
}
}
}
if (lineThrough != null) {
if (underline != null) {
sStyle.append (' ');
}
sStyle.append (getAttr (MarkupAttr.MARKUP_STRIKEOUT));
}
}
}
if (oAttr.digits() != TextAttr.DIGITS_LOCALE) {
int eDigits = (oAttr.digits() == TextAttr.DIGITS_INDIC)
? MarkupAttr.MARKUP_DIGITS_INDIC
: MarkupAttr.MARKUP_DIGITS_ARABIC;
addAttr (sStyle, MarkupAttr.MARKUP_DIGITS, eDigits);
}
if (oAttr.kerningEnable()) {
addAttr (sStyle, MarkupAttr.MARKUP_KERNING_MODE, oAttr.kerning() ? MarkupAttr.MARKUP_NONE : MarkupAttr.MARKUP_KERN_PAIR);
}
// Spacing
if (oAttr.charSpacingEnable() && (oAttr.charSpacing().getLengthValue() != 0)) {
addAttr (sStyle, MarkupAttr.MARKUP_CHAR_SPACING);
addMeasurement (oAttr, oAttr.charSpacing(), sStyle, false, true);
}
if (oAttr.wordSpacingEnable() && (oAttr.wordSpacing().getLengthValue() != 0)) {
addAttr (sStyle, MarkupAttr.MARKUP_WORD_SPACING);
addMeasurement (oAttr, oAttr.wordSpacing(), sStyle, false, true);
}
// Hyphenation
if (oAttr.hyphLevel() == TextAttr.HYPHEN_OFF) {
addAttr (sStyle, MarkupAttr.MARKUP_HYPHENATION, MarkupAttr.MARKUP_NONE);
} else {
addAttr (sStyle, MarkupAttr.MARKUP_HYPHENATION, MarkupAttr.MARKUP_AUTO);
switch (oAttr.hyphLevel()) {
case TextAttr.HYPHEN_PREFERRED:
addAttr (sStyle, MarkupAttr.MARKUP_HYPHENATION_LEVEL, MarkupAttr.MARKUP_HYPHENATION_PREFERRED);
break;
case TextAttr.HYPHEN_ALL:
addAttr (sStyle, MarkupAttr.MARKUP_HYPHENATION_LEVEL, MarkupAttr.MARKUP_ALL);
break;
}
}
if (oAttr.hyphMinWordEnable() || oAttr.hyphMinPrefixEnable() || oAttr.hyphMinSuffixEnable()) {
StringBuilder values = new StringBuilder();
values.append ('\'');
values.append (oAttr.hyphMinWord());
values.append (' ');
values.append (oAttr.hyphMinPrefix());
values.append (' ');
values.append (oAttr.hyphMinSuffix());
values.append ('\'');
addAttr (sStyle, MarkupAttr.MARKUP_HYPHENATION_LENGTH);
sStyle.append (values);
}
if (oAttr.hyphSuppressAcronymsEnable()) {
addAttr (sStyle,
MarkupAttr.MARKUP_HYPHENATION_ACRONYMS,
oAttr.hyphSuppressAcronyms() ? MarkupAttr.MARKUP_NONE
: MarkupAttr.MARKUP_AUTO);
}
if (oAttr.hyphSuppressNamesEnable()) {
addAttr (sStyle,
MarkupAttr.MARKUP_HYPHENATION_NAMES,
oAttr.hyphSuppressNames() ? MarkupAttr.MARKUP_NONE
: MarkupAttr.MARKUP_AUTO);
}
if (oAttr.leaderAlignEnable()) {
addAttr (sStyle, MarkupAttr.MARKUP_LEADER_ALIGN, (oAttr.leaderAlign() == TextAttr.LEADER_ALIGN_PAGE) ? MarkupAttr.MARKUP_LEADER_ALIGN_PAGE : MarkupAttr.MARKUP_NONE);
}
if (oAttr.leaderPatternEnable()) {
int ePattern = MarkupAttr.MARKUP_LEADER_PATTERN_SPACE;
switch (oAttr.leaderPattern()) {
case TextAttr.LEADER_PATTERN_RULE:
ePattern = MarkupAttr.MARKUP_LEADER_PATTERN_RULE;
break;
case TextAttr.LEADER_PATTERN_DOTS:
ePattern = MarkupAttr.MARKUP_LEADER_PATTERN_DOTS;
break;
case TextAttr.LEADER_PATTERN_USE_CONTENT:
ePattern = MarkupAttr.MARKUP_LEADER_PATTERN_USE_CONTENT;
break;
}
addAttr (sStyle, MarkupAttr.MARKUP_LEADER_PATTERN, ePattern);
}
if (oAttr.leaderPatternWidthEnable() && (! oAttr.leaderPatternWidth().isZero())) {
addAttr (sStyle, MarkupAttr.MARKUP_LEADER_PATTERN_WIDTH);
addMeasurement (oAttr, oAttr.leaderPatternWidth(), sStyle);
}
if (oAttr.ruleStyleEnable()) {
int eStyle = MarkupAttr.MARKUP_SOLID;
switch (oAttr.ruleStyle()) {
case TextAttr.RULE_STYLE_NONE:
eStyle = MarkupAttr.MARKUP_NONE;
break;
case TextAttr.RULE_STYLE_DOTTED:
eStyle = MarkupAttr.MARKUP_RULE_STYLE_DOTTED;
break;
case TextAttr.RULE_STYLE_DASHED:
eStyle = MarkupAttr.MARKUP_RULE_STYLE_DASHED;
break;
}
addAttr (sStyle, MarkupAttr.MARKUP_RULE_STYLE, eStyle);
}
if (oAttr.ruleThicknessEnable() && (! oAttr.ruleThickness().isZero())) {
addAttr (sStyle, MarkupAttr.MARKUP_RULE_STYLE);
addMeasurement (oAttr, oAttr.ruleThickness(), sStyle);
}
return sStyle.toString();
}
private String getParaStyle () {
StringBuilder sMarkup = new StringBuilder();
TextAttr oAttr = new TextAttr (moParaAttr);
TextAttr oAmbient = new TextAttr ();
if (mbIncludeAmbientAttrs) {
// Display all ambient attributes that don't match CSS defaults
oAmbient.copyFrom (moAmbientAttr);
oAmbient.dropSame (moCSSDefault);
oAttr.addDisabled (oAmbient);
} else {
// Don't include ambient.
reconcileAttrs (moAmbientAttr, oAttr);
}
boolean bWriteMarginL = oAttr.marginLEnable();
boolean bWriteMarginR = oAttr.marginREnable();
boolean bWriteMarginT = oAttr.spaceBeforeEnable();
boolean bWriteMarginB = oAttr.spaceAfterEnable();
// Check if we can write out our margins as one, rather than each
// individually
if (bWriteMarginL
&& bWriteMarginR
&& bWriteMarginT
&& bWriteMarginB
&& oAttr.spaceBefore().equals (oAttr.marginR())
&& oAttr.spaceBefore().equals (oAttr.spaceAfter())
&& oAttr.spaceBefore().equals (oAttr.marginL())) {
addAttr (sMarkup, MarkupAttr.MARKUP_MARGIN);
addMeasurement (oAttr, oAttr.spaceBefore(), sMarkup);
} else {
// Write the margins individually
if (bWriteMarginT) {
addAttr (sMarkup, MarkupAttr.MARKUP_SPACE_BEFORE);
addMeasurement (oAttr, oAttr.spaceBefore(), sMarkup);
}
if (bWriteMarginR) {
addAttr (sMarkup, MarkupAttr.MARKUP_INDENT_RIGHT);
addMeasurement (oAttr, oAttr.marginR(), sMarkup);
}
if (bWriteMarginB) {
addAttr (sMarkup, MarkupAttr.MARKUP_SPACE_AFTER);
addMeasurement (oAttr, oAttr.spaceAfter(), sMarkup);
}
if (bWriteMarginL) {
addAttr (sMarkup, MarkupAttr.MARKUP_INDENT_LEFT);
addMeasurement (oAttr, oAttr.marginL(), sMarkup);
}
}
if (oAttr.specialEnable()) {
addAttr (sMarkup, MarkupAttr.MARKUP_INDENT_FIRST_LINE);
addMeasurement (oAttr, oAttr.special(), sMarkup);
}
if (oAttr.spacingEnable()) {
// Spacing often comes to us as zero (single spacing)
// Never output zero, as it seldom makes sense to have
// overlapping lines.
// FF99 designer doesn't currently support changing lineheight
// inside a control, so we don't have to worry about the scenario
// where we might switch from e.g. double to single spacing.
if (oAttr.spacing().getLengthValue() != 0) {
addAttr (sMarkup, MarkupAttr.MARKUP_LINE_HEIGHT);
addMeasurement (oAttr, oAttr.spacing(), sMarkup, false, true);
}
}
//justification
if (oAttr.justifyVEnable()) {
switch(oAttr.justifyV()) {
case TextAttr.JUST_V_TOP:
addAttr (sMarkup,
MarkupAttr.MARKUP_TEXT_VALIGN,
MarkupAttr.MARKUP_TEXT_VALIGN_TOP);
break;
case TextAttr.JUST_V_MIDDLE:
addAttr (sMarkup,
MarkupAttr.MARKUP_TEXT_VALIGN,
MarkupAttr.MARKUP_TEXT_VALIGN_MIDDLE);
break;
case TextAttr.JUST_V_BOTTOM:
addAttr (sMarkup,
MarkupAttr.MARKUP_TEXT_VALIGN,
MarkupAttr.MARKUP_TEXT_VALIGN_BOTTOM);
break;
}
}
if (oAttr.justifyHEnable()) {
switch(oAttr.justifyH()) {
case TextAttr.JUST_H_LEFT:
addAttr (sMarkup,
MarkupAttr.MARKUP_JUSTIFY,
MarkupAttr.MARKUP_XHTML_LEFT);
break;
case TextAttr.JUST_H_CENTRE:
addAttr (sMarkup,
MarkupAttr.MARKUP_JUSTIFY,
MarkupAttr.MARKUP_XHTML_CENTER);
break;
case TextAttr.JUST_H_RIGHT:
addAttr (sMarkup,
MarkupAttr.MARKUP_JUSTIFY,
MarkupAttr.MARKUP_XHTML_RIGHT);
break;
case TextAttr.JUST_H_SPREAD:
addAttr (sMarkup,
MarkupAttr.MARKUP_JUSTIFY,
MarkupAttr.MARKUP_JUSTIFY_SPREAD);
break;
case TextAttr.JUST_H_SPREAD_ALL:
addAttr (sMarkup,
MarkupAttr.MARKUP_JUSTIFY,
MarkupAttr.MARKUP_JUSTIFY_SPREAD_ALL);
break;
case TextAttr.JUST_H_COMB_LEFT:
addAttr (sMarkup,
MarkupAttr.MARKUP_JUSTIFY,
MarkupAttr.MARKUP_JUSTIFY_COMB_LEFT);
break;
case TextAttr.JUST_H_COMB_CENTRE:
addAttr (sMarkup,
MarkupAttr.MARKUP_JUSTIFY,
MarkupAttr.MARKUP_JUSTIFY_COMB_CENTER);
break;
case TextAttr.JUST_H_COMB_RIGHT:
addAttr (sMarkup,
MarkupAttr.MARKUP_JUSTIFY,
MarkupAttr.MARKUP_JUSTIFY_COMB_RIGHT);
break;
}
}
//tabs
if (oAttr.tabsEnable()) {
addAttr (sMarkup, MarkupAttr.MARKUP_TAB_DEFAULT);
addUnitSpan (oAttr.tabs().noUniform() ? UnitSpan.ZERO
: oAttr.tabs().uniform().tabStop(),
sMarkup,
false);
if (oAttr.tabs().size() > 0) {
addAttr (sMarkup, MarkupAttr.MARKUP_TAB_POSITION);
}
for (int i = 1; i <= oAttr.tabs().size(); i++) {
if (i != 1) {
sMarkup.append (' ');
}
//get the stop
TextTab oTab = oAttr.tabs().tabAt (i);
UnitSpan oStop = oTab.tabStop();
String sAlign = "";
switch (oTab.tabType()) {
case TextTab.TYPE_LEFT:
case TextTab.TYPE_ALIGN_AFTER:
sAlign = getAttr (MarkupAttr.MARKUP_XHTML_LEFT);
break;
case TextTab.TYPE_CENTRE:
sAlign = getAttr (MarkupAttr.MARKUP_XHTML_CENTER);
break;
case TextTab.TYPE_RIGHT:
case TextTab.TYPE_ALIGN_BEFORE:
sAlign = getAttr (MarkupAttr.MARKUP_XHTML_RIGHT);
break;
case TextTab.TYPE_DECIMAL:
sAlign = getAttr (MarkupAttr.MARKUP_TAB_ALIGN_DECIMAL);
sMarkup.append (sAlign);
sMarkup.append (' ');
addUnitSpan (oStop, sMarkup, false);
}
}
}
if (oAttr.ligatureEnable()) {
addAttr (sMarkup,
MarkupAttr.MARKUP_LIGATURE,
(oAttr.ligature() == TextAttr.LIGATURE_COMMON) ? MarkupAttr.MARKUP_LIGATURE_COMMON
: MarkupAttr.MARKUP_LIGATURE_MINIMUM);
}
return sMarkup.toString();
}
private String getTextDirection (boolean bIsPara) {
int eAttrDirection = TextAttr.DIRECTION_NEUTRAL;
if (bIsPara) {
eAttrDirection = moSpanAttr.paraDirection();
}
if (eAttrDirection == TextAttr.DIRECTION_NEUTRAL) {
eAttrDirection = moSpanAttr.direction();
}
if (eAttrDirection == TextAttr.DIRECTION_NEUTRAL) {
return "";
}
int eMkDirection = (eAttrDirection == TextAttr.DIRECTION_RTL) ? MarkupAttr.MARKUP_DIRECTION_RTL
: MarkupAttr.MARKUP_DIRECTION_LTR;
return getAttr (eMkDirection);
}
private void addColour (int lCol, StringBuilder sColour) {
String s = Integer.toHexString (lCol + 0x100);
sColour.append(s, 1, s.length());
}
private String getAttr (int eTag) {
return markupAttr().lookup (eTag);
}
private void addUnitSpan (UnitSpan oUnits, StringBuilder sAttrs, boolean bUseDefaultUnits) {
addUnitSpan (oUnits, sAttrs, bUseDefaultUnits, false);
}
private void addUnitSpan (UnitSpan oUnits, StringBuilder sAttrs, boolean bUseDefaultUnits, boolean bUsePTs) {
// Add the value of this unitspan to the attribute string.
int eUnits = oUnits.units();
if (bUseDefaultUnits && (eUnits != meDefaultUnits)) {
oUnits = new UnitSpan (meDefaultUnits, UnitSpan.convertUnit (meDefaultUnits, eUnits, oUnits.value()));
} else if (bUseDefaultUnits && (eUnits != UnitSpan.POINTS_1K)) {
oUnits = new UnitSpan (UnitSpan.POINTS_1K, UnitSpan.convertUnit (UnitSpan.POINTS_1K, eUnits, oUnits.value()));
}
sAttrs.append (oUnits.text (3, true, false));
}
private void addMeasurement (TextAttr oAttr, TextMeasurement oMeasurement, StringBuilder sAttrs, boolean bUseDefaultUnits, boolean bUsePTs) {
// Add the value of this unitspan to the attribute string.
if (bUseDefaultUnits || bUsePTs) {
addUnitSpan (oAttr.flattenMeasurement (oMeasurement), sAttrs, bUseDefaultUnits, bUsePTs);
} else {
sAttrs.append (oMeasurement.toString (3));
}
}
private void addMeasurement (TextAttr oAttr, TextMeasurement oMeasurement, StringBuilder sAttrs, boolean bUseDefaultUnits) {
addMeasurement (oAttr, oMeasurement, sAttrs, bUseDefaultUnits, false);
}
private void addMeasurement (TextAttr oAttr, TextMeasurement oMeasurement, StringBuilder sAttrs) {
addMeasurement (oAttr, oMeasurement, sAttrs, true);
}
private void pruneParaAttrs () {
reconcileAttrs (moAmbientAttr, moParaAttr);
}
private void startPara () {
if (! mbNewPara) {
return;
}
StringBuilder sMarkup = new StringBuilder();
sMarkup.append (gsParaOpenTag);
String sTextDirection = getTextDirection (true);
if (sTextDirection.length() > 0) {
sMarkup.append (' ');
sMarkup.append (gsTextDirectionStart);
sMarkup.append (sTextDirection);
sMarkup.append (gsTextDirectionEnd);
}
StringBuilder sStyle = new StringBuilder();
sStyle.append (getParaStyle());
sStyle.append (getSpanStyle (true));
if (sStyle.length() == 0) {
sMarkup.append (gsTagEnd);
}
else {
// add a carriage return so that our xhtml output lines don't get too long
if (mbBreakupOutput) {
sMarkup.append ('\n');
} else {
sMarkup.append (' ');
}
sMarkup.append (gsStyleAttr);
sMarkup.append (sStyle);
sMarkup.append (gsStyleEnd);
}
mpTranslation.append (sMarkup);
mbNewPara = false;
mbParaHasContent = true; // because the caller is about to add real content
}
private void endPara () {
// if the previous paragraph didn't have content, start one and add a
// single space run, to force a visual vertical space
if (! mbParaHasContent) {
startPara();
mpTranslation.append (gsSpaceRunSingle);
}
if (mbTrailingBreak) {
mpTranslation.append (gsBR);
}
mbTrailingBreak = false;
mpTranslation.append (gsParaEnd);
// inherit the previous attributes for our next paragraph.
moParaAttr.override (moSpanAttr);
moSpanAttr.copyFrom (moParaAttr);
}
private static void reconcileAttrs (TextAttr oDrop, TextAttr oAttr) {
boolean bRestoreDecoration = false;
int eUnderline = GFXDecorationInfo.DECORATE_NONE;
int eStrikeout = GFXDecorationInfo.DECORATE_NONE;
if (oDrop.underlineEnable()) {
if (oAttr.underlineEnable()) {
eUnderline = oAttr.underline();
if (eUnderline != oDrop.underline()) {
bRestoreDecoration = true;
}
} else {
eUnderline = oDrop.underline();
}
}
if (oDrop.strikeoutEnable()) {
if (oAttr.strikeoutEnable()) {
eStrikeout = oAttr.strikeout();
if (eStrikeout != oDrop.strikeout()) {
bRestoreDecoration = true;
}
} else {
eStrikeout = oDrop.strikeout();
}
}
oAttr.dropSame (oDrop);
if (bRestoreDecoration) {
oAttr.underline (eUnderline);
oAttr.strikeout (eStrikeout);
}
}
}