For a recent client project, we were having an issue with using superscript to display the number next to a disclaimer information. In HTML this is accomplished by simply wrapping a <sup> tag around the text. For example: "<sup>1</sup> The client wants a disclaimer here." looks like:
1 The client wants a disclaimer here.
However, Flash does not support superscripts or subscripts in HTML text out of box, so a "hack" of some sort needed to be applied
Looking around I found two solutions. The first is fairly old (2007/2008) and uses a combination of some regular expression magic to convert <sup> and <sub> tags into HTML <font> tags. The solution next used an extra set of embedded fonts that were created for superscirpt and subscript. Here is the blog post for reference.
This solution is a reasonable but requires the process of embedding two extra fonts into the application to get the desired result.
The second approach was to use the Text Layout Framework (TLF) in Flex 4 and take advantage of the baselineShift property to create subscript and superscript text. See this Adobe blog for reference.
My personal desire was to use the TLF approach so we would not have to embed extra fonts into the application. Plus, our application reads in text dynamically that is formatted as simply HTML and is then converted into TLF via the TextConverter.importFlow() method. Here is a quick example of the conversion.
var richText:RichText = new RichText();
richText.textFlow = TextConverter.importToFlow(ourContentAsString,
TextConverter.TEXT_FIELD_HTML_FORMAT);
Since we are using the TextConvert to convert HTML into TLF, we don't have a chance to modify any <sup> or <sub> tags that might be within the HTML string. The challenge is that the converter will actually strip out those tags (and any other unsupported tags), during conversion.
So what to do...
We found a way to combine both methods. We first use the regular expression magic outlined in the first blog, to convert all <sup> and <sub> into tags that TextConverter will not remove.
// create regular expressions
var supStartExpression:RegExp = new RegExp("<sup>", "g");
var supEndExpression:RegExp = new RegExp("</sup>", "g");
var subStartExpression:RegExp = new RegExp("<sub>", "g");
var subEndExpression:RegExp = new RegExp("</sub>", "g");
// replace all tags
htmlContentAsString = htmlContentAsString.replace(supStartExpression,
"<span class="superscript">");
htmlContentAsString = htmlContentAsString.replace(supEndExpression, "</span>");
htmlContentAsString = htmlContentAsString.replace(subStartExpression,
"<span class="subscript">");
htmlContentAsString = htmlContentAsString.replace(subEndExpression, "</span>");
// convert HTML into a TextFlow element var textFlow:TextFlow = TextConverter.importToFlow(htmlContentAsString,format, config); // format superscript and subscripts in TextFlow formatCustomTextFlow(textFlow);
You will notice, instead of converting each <sup>/<sub> into <font> tags like in the first blog post suggests, we instead use <span> tags and add a class attribute based on what tag (<sup> or <sub>) we are converting. We do this for one main reason, when we convert HTML into TLF each <span> found will be converted into a SpanElement object and we need a way to mark each SpanElement with what they were prior in HTML. This is so that we can act upon it in the next step when we call our method formatCustomTextFlow(). The TextConverter takes the 'class' attribute we define on the <span> and during the conversion, assigns the value to the 'styleName' property on the SpanElement.
The output from this part will be a single TextFlow element, which will have children for each HTML element found in the text. We will next traverse the entire TextFlow hierarchy of children searching for FlowElements (SpanElement is a decedent of FlowElement). We start this by calling a custom method formatCustomTLF(). In this method we examine each mxmlChild of the TextFlow element. We first look to see if the element is of type FlowElementGroup. If so, we know it might have children, so we loop over all it's children calling formatCustomTLF(). If the element is not a FlowElementGroup and has a styleName attribute value of "superscript" or "subscript", we process it. We process the FlowElement by setting the baselineShift property based on the styleName. So if the stylename is "superscript" we set the baselineShift equal to BaselineShift.SUPERSCRIPT and then remove the styleName value. Here's a look at the code.
private function formatCustomTLF(element:FlowElement):void {
// if FlowElement is a group loop over it's children and format them
if(element is FlowGroupElement) {
var parentAsGroupElement:FlowGroupElement = FlowGroupElement(element);
for each(var childElement:FlowElement in parentAsGroupElement.mxmlChildren) {
formatCustomTLF(childElement);
}
}
// set baselineShift based on stylename
else if(element.styleName == "superscript") {
element.baselineShift = BaselineShift.SUPERSCRIPT;
element.styleName = "";
}
else if(element.styleName == "subscript") {
element.baselineShift = BaselineShift.SUBSCRIPT;
element.styleName = "";
}
}
After that, we have our superscript and subscript support in our html content!
Here's a sample Flex application you can download and import into Flash Builder 4, that demonstrates the entire technic.

Post new comment