I have a RichEditiableText component in a Flex 4.5 project and I want to print its contents. The problem I'm having is that component is holding a TextFlow and the copy is being clipped at the beginning/ending of pages. I can't seem to get it to respect the page breaks consistently.
I have this piece of code that adjusts the scroll position of the the RichEditableText component in a loop. Is there a better/smarter way to do this so that I ensure the text breaks to a new page appropriately?
if(richEditTextComponent.contentHeight <= printJob.pageHeight) {
printJob.addObject(richEditTextComponent, FlexPrintJobScaleType.NONE);
} else {
var max:Number = richEditTextComponent.contentHeight;
var i:Number = 0;
while(i<max) {
printJob.addObject(richEditTextComponent, FlexPrintJobScaleType.NONE);
richEditTextComponent.verticalScrollPosition += printJob.pageHeight;
i+=printJob.pageHeight;
}
}
Just for anyone else that has this problem...I ended up simply passing the copy to Javascript to handle printing. Turns out TLF is pretty cumbersome for type of thing.
Related
I'm using code mirror to display, highlight and edit xml in a web page but I am having a problem with the cursor position being offset from the insert position so that if you delete a character from where the blinking cursor is, a character before the one you would expect gets deleted instead. I am assuming its a css clash with my current page because it works well outside my page, but cant find the clash anywhere. Has anyone had similar issues or know what to do?
Further investigation shows that the page had padding set on all divs embedded in fieldsets which was the cause of the problem.
The following lines fixed the issue for me:
.CodeMirror pre {
white-space: pre-wrap;
word-break: break-all;
word-wrap: break-word;
}
I'm using lineWrapping: true in my CodeMirror configuration. Setting that to false works as well.
Be careful using zoom in your CSS with CodeMirror.
I used zoom in body and removing that worked for me.
This issue often happened to me after resizing a parent container. What helped was:
editor.setSize("100%", "100%");
editor.focus();
in the end of a resize event handler.
Sometimes, also, below italic or bold -containing lines (depending on your OS and browser), cursor has a wrong vertical position up to 90% of a line. It can be easily fixed by setting, e.g.
.CodeMirror pre {
height:15px;
}
anywhere in your CSS stylesheets. This also provides you with a way to control line height, if you find lines of code showing too close to each other.
For some reason the white spaces when indention is enabled were not treated correctly when calculating the line size. Replacing measureLine function with the following in codemirror.js did the trick for me:
function measureLine(cm, line) {
// First look in the cache
var cached = findCachedMeasurement(cm, line);
if (cached) return cached.measure;
// Failing that, recompute and store result in cache
var measure = measureLineInner(cm, line);
var origL;
var origR;
var lastR ="";
for (var mes in measure) {
origL = measure[mes].left;
origR = measure[mes].right;
if (lastR != "") {
measure[mes].left = lastR;
measure[mes].right = lastR + (origR - origL);
}
if (origL == origR) {
measure[mes].right = measure[mes].right + 8;
}
lastR = measure[mes].right;
}
var cache = cm.display.measureLineCache;
var memo = {
text: line.text,
width: cm.display.scroller.clientWidth,
markedSpans: line.markedSpans,
measure: measure,
classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass
};
if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
else cache.push(memo);
return measure;
}
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();
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.
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.
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.
I have a several chart components that I have created in Flex. Basically I have set up a special UI that allows the user to select which of these charts they want to print. When they press the print button each of the selected charts is created dynamically then added to a container. Then I send this container off to FlexPrintJob.
i.e.
private function prePrint():void
{
var printSelection:Box = new Box();
printSelection.percentHeight = 100;
printSelection.percentWidth = 100;
printSelection.visible = true;
if (this.chkMyChart1.selected)
{
var rptMyChart1:Chart1Panel = new Chart1Panel();
rptMyChart1.percentHeight = 100;
rptMyChart1.percentWidth = 100;
rptMyChart1.visible = true;
printSelection.addChild(rptMyChart1);
}
print(printSelection);
}
private function print(container:Box):void
{
var job:FlexPrintJob;
job = new FlexPrintJob();
if (job.start()) {
job.addObject(container, FlexPrintJobScaleType.MATCH_WIDTH);
job.send();
}
}
This code works fine if the chart is actually displayed somewhere on the page but adding it dynamically as shown above does not. The print dialog will appear but nothing happens when I press OK.
So I really have two questions:
Is it possible to print flex components/charts when they are not visible on the screen?
If so, how do I do it / what am I doing wrong?
UPDATE:
Well, at least one thing wrong is my use of the percentages in the width and height. Using percentages doesn't really make sense when the Box is not contained in another object. Changing the height and width to fixed values actually allows the printing to progress and solves my initial problem.
printSelection.height = 100;
printSelection.width = 100;
But a new problem arises in that instead of seeing my chart, I see a black box instead. I have previously resolved this issue by setting the background colour of the chart to #FFFFFF but this doesn't seem to be working this time.
UPDATE 2:
I have seen some examples on the adobe site that add the container to the application but don't include it in the layout. This looks like the way to go.
i.e.
printSelection.includeInLayout = false;
addChild(printSelection);
Your component has to be on the stage in order to draw its contents, so you should try something like this:
printSelection.visible = false;
application.addChild(printSelection);
printSelection.width = ..;
printSelection.height = ...;
...
then do the printing
i'm not completely sure, but in one of my application I have to print out a complete Tab Navigator and the only method i have found to print it is to automatically scroll the tabnavigator tab in order to show the component on screen when i add them to the printjob.
In this way they are all printed. Before i created the tabnaviagotr scrolling the print (to PDF) result was a file with all the pages of the tab but only the one visible on screen really printed.
I've been playing around with different methods of determining at runtime the width of a "label" so that I can resize the "label" because I don't want it to truncate. I've finally found an easy solution through UITextField which allows me to set the .autoSize which is great! However, now I'm trying to "style" (simply adjust font and font size) of the UITextField but it seems that I have to do it manually with '.htmlText' (which I'll gladly accept if that is the ONLY way).
I'm using the .text to set the value of the label.
My test case involves a HBox (I'm actually using a Grid but they should be the same and I've done testing on both):
I style the HBox and the style carries through to the UITextField. I don't believe this will work for me because I have other components inside that I need to style differently.
I've tried: UITextFormat and TextFormat (I see that the .htmlText being updated accordingly but the output doesn't update. Then I noticed that whenever I called hbox.addChild(myUITextField) it would override the .htmlText
I've tried setting the style with myUITextField.setStyle("fontSize", 20) before and/or after the call to addChild neither of which made an impact on the display as per what I noted above.
Changes are being made but they seem to be overrided when I add it to the display.
So what do I need to do in order to style the UITextField aside from manually setting it along with my contents in .htmlText? Solutions not using UITextField is fine as long as there is some easy way of not truncating the text.
EDIT: I want to just do textField.setStyle('fontSize', 20) and expect that every time I change the text, I wouldn't need to use HTML to go with it (so I can just do textField.text = 'something else' and expect that it will still have a font size of 20). This is what I meant by not using .htmlText (sorry if I wasn't clear before).
2nd EDIT: I guess I should present the whole issue and maybe that'll clarify what I did wrong or couldn't achieve.
My intent is to have a Grid and add text into it. I do not want it to wrap or scroll so I add it to the next row in the Grid when the current row's children total width exceeds some number. In order to add it to the next row, I need to be able to calculate the width of the text. I would like to be able to style that text individually based on cases and there might be other components (like a TextInput). Essentially what I'm trying to accomplish is "Fill in the Blank".
I've included code to show what I'm currently doing and it works somewhat. It might be un-related to the original issue of styling but I can't figure out how to adjust the distance between each UITextField but aside from that this fits what I would like to accomplish. Relevant to the question is: I would like to change the way I style each UITextField (currently setting .htmlText) into something a bit straightforward though like I previously mentioned I'll gladly accept using .htmlText if that's the only solution.
So I have a Grid with x Rows in it and in each row, I have exactly one GridItem. Based on the input, I add UITextField and TextInput into the GridItem going on to the next GridItem when necessary. If you have a better way of doing so then that would be better but I guess what I really want is to find a different way of styling.
Also another problem, I'm not sure of the exact way to add a TextField into the display. I tried:
var t : TextField = new TextField();
t.text = "I'm a TextField";
hBox.addChild(t); // doesn't work
//this.addChild(t); // doesn't work either
But I get the following error:
TypeError: Error #1034: Type Coercion failed: cannot convert flash.text::TextField#172c8f9 to mx.core.IUIComponent.
Here's what I have that's working.
private function styleQuestionString(str : String) : String {
return '<FONT leading="1" face="verdana" size="20">' + str + '</FONT>';
}
private function loadQuestion(str : String) : void {
/* Split the string */
var tmp : Array = str.split("_");
/* Track the current width of the GridItem */
var curWidth : int = 0;
/* Display components that we will add */
var txtField : UITextField = null;
var txtInput : TextInput = null;
/* Track the current GridItem */
var curGridItem : GridItem = null;
/* Track the GridItem we can use */
var gridItemAC : ArrayCollection = new ArrayCollection();
var i : int = 0;
/* Grab the first GridItem from each GridRow of Grid */
var tmpChildArray : Array = questionGrid.getChildren();
for (i = 0; i < tmpChildArray.length; i++) {
gridItemAC.addItem((tmpChildArray[i] as GridRow).getChildAt(0));
}
curGridItem = gridItemAC[0];
gridItemAC.removeItemAt(0);
/* Used to set the tab index of the TextInput */
var txtInputCounter : int = 1;
var txtFieldFormat : UITextFormat = new UITextFormat(this.systemManager);
txtFieldFormat.leading = "1";
//var txtFieldFormat : TextFormat = new TextFormat();
//txtFieldFormat.size = 20;
/* Proper Order
txtField = new UITextField();
txtField.text = tmp[curItem];
txtField.autoSize = TextFieldAutoSize.LEFT;
txtField.setTextFormat(txtFieldFormat);
*/
var txtLineMetrics : TextLineMetrics = null;
var tmpArray : Array = null;
curGridItem.setStyle("leading", "1");
var displayObj : DisplayObject = null;
for (var curItem : int= 0; curItem < tmp.length; curItem++) {
/* Using UITextField because it can be auto-sized! */
/** CORRECT BLOCK (ver 1)
txtField = new UITextField();
txtField.text = tmp[curItem];
txtField.autoSize = TextFieldAutoSize.LEFT;
txtField.setTextFormat(txtFieldFormat);
***/
tmpArray = (tmp[curItem] as String).split(" ");
for (i = 0; i < tmpArray.length; i++) {
if (tmpArray[i] as String != "") {
txtField = new UITextField();
txtField.htmlText = styleQuestionString(tmpArray[i] as String);
//txtField.setTextFormat(txtFieldFormat); // No impact on output
txtLineMetrics = curGridItem.measureHTMLText(txtField.htmlText);
curWidth += txtLineMetrics.width + 2;
if (curWidth >= 670) {
curGridItem = gridItemAC[0];
curGridItem.setStyle("leading", "1");
if (gridItemAC.length != 1) {
gridItemAC.removeItemAt(0);
}
// TODO Configure the proper gap distance
curWidth = txtLineMetrics.width + 2;
}
displayObj = curGridItem.addChild(txtField);
}
}
//txtField.setColor(0xFF0000); // WORKS
if (curItem != tmp.length - 1) {
txtInput = new TextInput();
txtInput.tabIndex = txtInputCounter;
txtInput.setStyle("fontSize", 12);
txtInputCounter++;
txtInput.setStyle("textAlign", "center");
txtInput.width = TEXT_INPUT_WIDTH;
curWidth += TEXT_INPUT_WIDTH;
if (curWidth >= 670) {
curGridItem = gridItemAC[0];
if (gridItemAC.length != 1) {
gridItemAC.removeItemAt(0);
}
// TODO Decide if we need to add a buffer
curWidth = TEXT_INPUT_WIDTH + 2;
}
curGridItem.addChild(txtInput);
txtInputAC.addItem(txtInput);
/* Adds event listener so that we can perform dragging into the TextInput */
txtInput.addEventListener(DragEvent.DRAG_ENTER, dragEnterHandler);
txtInput.addEventListener(DragEvent.DRAG_DROP, dragDropHandler);
txtInput.addEventListener(DragEvent.DRAG_EXIT, dragExitHandler);
}
/* Add event so that this label can be dragged */
//txtField.addEventListener(MouseEvent.MOUSE_MOVE, dragThisLabel(event, txtField.text));
}
}
After about 8 hours of searching for a solution to what would seem to be such a simple issue I FINALLY stumbled on your posts here... Thankyou!!!
I have been stumbling around trying to get TextField to work and had no Joy, Label was fine, but limited formatting, and I need to be able to use embedded fonts and rotate. After reading the above this finally worked for me:
var myFormat:TextFormat = new TextFormat();
myFormat.align = "center";
myFormat.font = "myFont";
myFormat.size = 14;
myFormat.color = 0xFFFFFF;
var newTxt:UITextField = new UITextField();
newTxt.text = "HELLO";
addChild(newTxt);
newTxt.validateNow();
newTxt.setTextFormat(myFormat);
The order of addChild before the final 2 steps was critical! (myFont is an embedded font I am using).
One again... a thousand thankyou's...
John
EDIT BASED ON THE ASKERS FEEDBACK:
I didn't realize you wanted to just apply one style to the whole textfield, I thought you wanted to style individual parts. This is even simpler for you, won't give you any trouble at all :)
var textFormat: TextFormat = new TextFormat("Arial", 12, 0xFF0000);
myText.setTextFormat(textFormat);
Be aware that this sets the style to the text that is in the TextField, not necessarily future text you put in. So have your text in the field before you call setTextFormat, and set it again every time you change it just to be sure it stays.
It's probably best if you use a normal TextField as opposed to the component. If you still want the component you may need to call textArea.validateNow() to get it to update with the new style (not 100% sure on that one though) Adobe components are notoriously bad, and should be avoided. :(
To see all available options on the TextFormat object see here
END EDIT ---------
This is easy enough to just do with CSS in a normal old TextField.
var myCSS: String = "Have some CSS here, probably from a loaded file";
var myHTML: String = "Have your HTML text here, and have it use the CSS styles";
// assuming your textfield's name is myText
var styleSheet: StyleSheet = new StyleSheet();
styleSheet.parseCSS(myCSS);
myText.autoSize = TextFieldAutoSize.LEFT;
myText.styleSheet = styleSheet;
myText.htmlText = myHTML;
Supported HTML tags can be found here
Supported CSS can be found here
The reason you have a problem adding Textfield to containers is that it doesn't implement the IUIComponent interface. You need to use UITextField if you want to add it. However, that's presenting me with my own styling issues that brought me to this question.
A few things I know:
TextField is styled using the TextFormat definition, and applying it to the textfield. As Bryan said, order matters.
setStyle does nothing on IUITextField, and the TextFormat method doesn't seem to work the same as in normal TextFields. (Edit #2: Ahah. You need to override the "validateNow" function on UITextFields to use the setTextFormat function)
To autosize a TextArea, you need to do something like this (inheriting from TextArea):
import mx.core.mx_internal;
use namespace mx_internal;
...
super.mx_internal::getTextField().autoSize = TextFieldAutoSize.LEFT;
this.height = super.mx_internal::getTextField().height;
Found this code on, I think, on StackOverflow a while back. Apologies to the original author. But the idea is that you need to access the "mx_internal" raw textfield.
Text and TextArea have wrapping options. (Label does not). So if you set the explicit width of a Text object, you might be able to size using the measuredHeight option and avoid truncation.
(edit: That was #4, but stackoverflow parsed it into a 1...)
I have a MultiLine asp:Textbox (a standard html textarea for those non-asp people) that I want to be auto-sized to fit all it's content only through css. The reason for this is that I want it to be of a specified height in the web browser, with scrolling enabled.
I have implemented a print style sheet however and want all text located in the textarea to be displayed when printed with no overflow hidden.
I can manually specify the height of the textarea in the print.css file problem with this being that the fields are optional and a 350px blank box is not optimal and there is always the possibility of a larger amount of text than this...
I have tried using :
height: auto;
height: 100%;
In IE and Firefox respectively yet these seem to be overridden by the presence of a specified number of rows in the html mark-up for the form which must be generated by .NET when you do not specify a height on the asp:Textbox tag, this seems to only accept numercial measurements such as px em etc...
Any ideas?
What you are asking for (a css solution) is not possible.
The content of the textarea is not html elements, so it can not be used by css to calculate the size of the textarea.
The only thing that could work would be Javascript, e.g. reading the scrollHeight property and use that to set the height of the element. Still the scrollHeight property is non-standard, so it might not work in all browsers.
jQuery or a javascript function to find and make textboxes bigger might be the best solution - at least thats what i found
we use this set of functions and call clean form after the page is loaded (i know this isnt the best solution right here and am working to transfer to a jQuery solution that is more elegant) - one note make sure your textareas have rows and cols specified or it doesnt work right.
function countLines(strtocount, cols)
{
var hard_lines = 1;
var last = 0;
while (true)
{
last = strtocount.indexOf("\n", last + 1);
hard_lines++;
if (last == -1) break;
}
var soft_lines = Math.round(strtocount.length / (cols - 1));
var hard = eval("hard_lines " + unescape("%3e") + "soft_lines;");
if (hard) soft_lines = hard_lines; return soft_lines;
}
function cleanForm()
{
var the_form = document.forms[0];
for (var i = 0, il = the_form.length; i < il; i++)
{
if (!the_form[i]) continue;
if (typeof the_form[i].rows != "number") continue;
the_form[i].rows = countLines(the_form[i].value, the_form[i].cols) + 1;
}
setTimeout("cleanForm();", 3000);
}
If you set rows to be a ridiculously high number the CSS height rule should override it on the screen.
Then in the print stylesheet just set height to auto. This might result in some big blank space where all the available rows haven't been filled up, but at least all text will be visible.
give jQuery's autogrow() a go #
http://plugins.jquery.com/project/autogrow