0
votes

I need to find a way to "upsample" text from 72dpi (screen) to 300dpi (print) for rendered client generated text. This is a true WYSIWYG application and we're expecting a ton of traffic so client side rendering is a requirement. Our application has several fonts, font sizes, colors, alignments the user can modify in a textarea. The question is how to convert 72dpi to 300dpi. We have the editior complete, we just need to make 300dpi versions of the textarea.

MY IDEA

1) Get textarea and increase the height, width, and font size by 300/72. (if ints are needed on font size I may need to increase the font then down-sample to the height/width)

2) use BitmapUtil.getSnapshot on the textarea to get a rendered version of the text

THE QUESTION

How can I render text inside of a textarea without the component lifecycle? Imagine:

var textArea:TextArea = new TextArea();
textArea.text = "This is a test";    
var bmd:BitmapData = textArea.render();
3
Not my area of expertise; but I'm pretty sure changing the height / width will not do what you want to do. Height and width have very little to do w/ DPI. If you don't have 300DPI sources for what you're printing; I don't think you'll be able to print succesfully at 300DPI. - JeffryHouser
Thank you for the thoughtful reply. My question is not about the math of DPI but how to capture the BitmapData of a TextArea that is "offscreen". Being "offscreen" it should not be shown thus will not go through a component lifecycle like creationComplete. To ask a question another way, it is possible to create a TextArea in memory which I can use the BitmapUtil.getSnapshot() function to generate a BitmapData object. - Gary
Why wouldn't off screen elements go through their own component lifecycle? If they were added to a container, they will renderer. - JeffryHouser

3 Answers

0
votes

Like Flextras said, width/height has nothing to do with DPI, unless you actually zoom into the application by 4.16X. If your application all has vector based graphics, it shouldn't be a problem. Plus, the concept of DPI is lost in any web application until you're trying to save/print a bitmap.

It's definitely possible, but you'll have to figure it on your own.

0
votes

To ask a question another way, it is possible to create a TextArea in memory which I can use the BitmapUtil.getSnapshot() function to generate a BitmapData object

Technically, all components are in memory. What you want to do, I believe, is render a component without adding it to a container.

We do exactly this for the watermark on Flextras components. Conceptually we created a method to render the instance; like this:

public function render(argInheritingStyles : Object):void{
 this.createChildren();
 this.childrenCreated();
 this.initializationComplete();
 this.inheritingStyles = argInheritingStyles;
 this.commitProperties();
 this.measure(); 
 this.height = this.measuredHeight;
 this.width = this.measuredWidth;
 this.updateDisplayList(this.unscaledWidth,this.unscaledHeight);
}

The method must be explicitly called. Then you can use the 'standard' procedure for turning the component into a bitmap. I think we use a Label; but the same approach should work on any given component.

0
votes

Here is the final method I used to solve the problem of creating a printable version of the text and style of a Spark TextArea component. I ended up placing the custom component TextAreaRenderer (see below) in the MXML and setting the visibility to false. Then using the reference to this component to process any text field (renderObject) and get back a BitmapData object.

public class TextAreaRenderer extends TextArea implements IAssetRenderer
{

    public function render(renderObject:Object, dpi:int = 300):BitmapData{

        // CAST THE OBJECT
        //.................
        var userTextArea:TextArea = TextArea(renderObject);

        // SCALE IS THE DIVISION OF THE NEW DPI OVER THE SCREEN DPI 72
        //............................................................
        var scale:Number = dpi / 72;

        // COPY THE USER'S TEXT AREA INTO THE OFFSCREEN TEXT AREA
        //.......................................................
        this.text = userTextArea.text;                          // the actual text
        this.height = Math.floor(userTextArea.height * scale);  // scaled height
        this.width = Math.floor(userTextArea.width * scale);    // scaled width


        // GET THE LAYOUT FORMATS AND COPY TO OFFSCREEN
        // - the user's format =  userTextAreaLayoutFormat
        // - the hidden format = thisLayoutFormat
        //...............................................
        var editableLayoutProperties:Array = ['fontSize', 'fontFamily', 'fontWeight', 'fontStyle', 'textAlign', 'textDecoration', 'color']
        userTextArea.selectAll();
        var userTextAreaLayoutFormat:TextLayoutFormat = userTextArea.getFormatOfRange();
        this.selectAll();
        var thisLayoutFormat:TextLayoutFormat = this.getFormatOfRange();
        for each(var prop:String in editableLayoutProperties){
            thisLayoutFormat[prop] = userTextAreaLayoutFormat[prop];
        }

        // SCALE THE FONT SIZE
        //....................
        thisLayoutFormat.fontSize = thisLayoutFormat.fontSize * scale;

        // SET THE FORMAT BACK IN THE TEXT BOX
        //...................................
        this.setFormatOfRange(thisLayoutFormat);

        // REDRAW THE OFFSCREEN
        // RETURN THE BITMAP DATA
        //.......................
        this.validateNow();
        return BitmapUtil.getSnapshot(this);
    }
}

Then calling the TextAreaRenderer after the text area is changed to get a scaled up bitmap.

// COPY THE DATA INTO THE OFFSCREEN COMPONENT
//............................................
var renderableComponent:IAssetRenderer = view.offScreenTextArea;
return renderableComponent.render(userTextArea, 300);

Thanks to the advice from www.Flextras.com for working through the issue with me.