I'm using AlivePDF to create a PDF file, then save it to the desktop. I can then use an HTMLLoader to display my lovely PDF file.
Now, the print button in Adobe Reader works fine. However, there will be young children using the app, so I'd like to have a big "Print" button right above it.
I figured I could just start up a print job and feed it my HTMLLoader. This won't work because the HTML loader rasterizes the content.
Any suggestions?
One answer I have found in order to solve this problem is called Cross-scripting PDF content. The idea is that a PDF can have embedded JavaScript, which can be called from the JavaScript within the HTML page "housing" said PDF (object tag only, no embed).
This site was of particular help. I had to simplify the JavaScript from that page down quite a bit. I kept getting syntax errors.
I also need my program to generate the PDF and the HTML content. I cannot ship a single PDF with embedded JS and an HTML file pointing to it. They need to be dynamically generated by the user. Here is a basic rundown:
private function printText(text:String):void
{
var p:PDF=new PDF(Orientation.PORTRAIT, Unit.MM, Size.LETTER);
p.addPage();
p.addText(text, 100, 100);
p.addJavaScript(this.getJavascript());
var f:FileStream=new FileStream();
var html:File=File.desktopDirectory.resolvePath("exported.html");
f.open(html, FileMode.WRITE);
f.writeUTF(this.getHtml());
f.close();
var file:File=File.desktopDirectory.resolvePath("exported.pdf");
f.open(file, FileMode.WRITE);
var bytes:ByteArray=p.save(Method.LOCAL);
f.writeBytes(bytes);
f.close();
Now that we have our two files, HTML and PDF, we can view the PDF, and create a giant purple print button for our younger / sight-impared users.
if (HTMLLoader.pdfCapability == HTMLPDFCapability.STATUS_OK)
{
var win:PrintTitleWindow; //the window w/giant button
var htmlLoader:HTMLLoader=new HTMLLoader();
var url:URLRequest=new URLRequest(html.url);
htmlLoader.width=880;
htmlLoader.height=(appHeight - 150); //i figure out the height elsewhere
htmlLoader.load(url);
var holder:UIComponent=new UIComponent();
holder.addChild(htmlLoader);
win=PrintTitleWindow(PopUpManager.createPopUp(mainWindow, PrintTitleWindow, true));
win.width=900;
win.height=(appHeight - 50);
win.addChild(holder);
win.addContent(htmlLoader);
PopUpManager.centerPopUp(win);
}
}
Here is the JS and HTML I used. I'm adding these in here for laughs. I'm sure there is a better way to do this, but I'm tired and it is late.
private function getJavascript():String
{
return 'function myOnMessage(aMessage) { print({ bUI: true, bSilent: false, bShrinkToFit: true }); } function myOnDisclose(cURL,cDocumentURL) { return true; } function myOnError(error, aMessage) { app.alert(error); } var msgHandlerObject = new Object(); msgHandlerObject.onMessage = myOnMessage; msgHandlerObject.onError = myOnError; msgHandlerObject.onDisclose = myOnDisclose; this.hostContainer.messageHandler = msgHandlerObject;';
}
private function getHtml():String
{
return '<html><head><script>function callPdfFunctionFromJavascript(arg) { pdfObject = document.getElementById("PDFObj");pdfObject.postMessage([arg]);}</script></head><body><object id="PDFObj" data="exported.pdf" type="application/pdf" width="100%" height="100%"></object></body></html>';
}
The PrintTitleWindow is a simple title window with the print button on it. The code to print is simple.
myHtmlLoader.window.callPdfFunctionFromJavascript('Print');
Eh Voila! I have a gigantor-print-button like so:
(source: cetola.net)
Hitting that big purple print button is the same as hitting the print icon in the PDF toolbar. The difference for me is that my users, who could be elementary or middle-school kids, won't have to look around for the stupid button.
So, it's a long way around the block. Still, if you need to print and can't rely on that adobe toolbar button, here's your answer :) .
Related
I have a form in my Xamarin project that its result is something like that:
Basically there is a header (1) and a body of this form (2). The header is quite simple build with AbsoluteLayout.
For creating the body (2) I've created my component to show a tab control and then for each tab a specific grid with images and text. For each section, I'm checking in the database how many records there are for it and change the text. This activity is very long and I'm trying to understand why and how I can improve speed.
Then I should cut the corner to add later in my page the tab control so the user can see immediately the header and after few second all page. The code is like the following:
public class MyPage : WaitingPage
{
public MyPage(Card card)
{
LoadingMessage = "Loading...";
ShowLoadingFrame = true;
ShowLoadingMessage = true;
ShadeBackground = true;
WaitingOrientation = StackOrientation.Vertical;
IsWaiting = true;
StackLayout stackPage = new StackLayout() {
BackgroundColor = Color.FromHex("#fff"),
Children = {
ShowHeader(card),
}
};
Content = stackPage;
Task.Yield();
Task.Run(async () => {
Content = await ShowDetails(card);
});
IsWaiting = false;
}
}
I tried different ways to add the content from await ShowDetails(card); but nothing happens. Basically the content doesn't change unless await ShowDetails(card); is executed. I discovered Task.Yield(); (you can wait the page is rendered and showed and then continue) but in this case doesn't work. And also WaitingPage doesn't show the message.
Any suggestions or help? Thank you in advance
I am working with a digital book application. I make use of swf loader to load swf pages created from pdf. I use TextSnapsot to draw inline text highlight on the pages. The highlight is thoroughly retained on the respective pages throughout the session and later it can be updated/deleted without any problem. Everything was working great till I made the following changes in the swf loading approach to enable page caching:
I am now loading swf loader object into application memory and while doing jumping from one page to other page I am just copying the content of the next page to the current swf loader which is on the display to the user. There are two sets of swf loaders - one for displaying the page and other to cache the next/previous page(s). On the caching side, I load the swf into application memory and after getting it loaded I pick all the contents of the loaded swf page (the children of it's movie clip) into an array collection. While changing the page I copy the cached content into the swf loader's movie clip which is displaying the page.
Now when I highlight on the page on display and navigate back/forth from the page and comeback again to the page where I did the highlighting: It shows the highlight I did. But as soon as I try to draw another highlight on that page, the previous highlight is instantly disappears from the page.
I suspect that the Textsnapshot object which draws highlight while navigating (to the target display page) is different from the one which redraws/update the highlight on the same page next time. Although the Textsnapshot object id for both the objects is same.
Here are some code snippet:
For copying the content from the swf loader object cached in application memory:
private function copyPageContent():void
{
var contentCollection:ArrayCollection = new ArrayCollection();
_pageContentVO = new PageContentVO();
_pageContentVO.contentHeight = MovieClip(_swfPageLoader.content).height;
_pageContentVO.contentWidth = MovieClip(_swfPageLoader.content).width;
var count:int = MovieClip(_swfPageLoader.content).numChildren;
for(var i:int=0;i<count;i++)
{
var dispObject:DisplayObject = MovieClip(_swfPageLoader.content).removeChildAt(0);
contentCollection.addItem(dispObject);
}
_pageContentVO.pageContentCollection = contentCollection;
_swfPageLoader = null;
}
For copying the content to the swf loader which is displaying the page:
private function copyContent(pageContentVo:PageContentVO):void
{
for(var i:int = 0;i<pageContentVo.pageContentCollection.length;i++)
{
var dispObject:DisplayObject = pageContentVo.pageContentCollection.getItemAt(i) as DisplayObject;
MovieClip(this.content).addChild(dispObject);
}
this.content.height = this.height;
this.content.width = this.width;
}
after this I dispatch swf loader's complete manually and in the handler of that event I take the text snap shot object.(highlightManager.as)
Code I use to draw highlight manually(using mouse drag on the page).
public function setHighlight():void
{
removeAll();
if(_textSnapShot!=null && _textSnapShot.getText(0,_textSnapShot.charCount)!="")
{
if(_isCoveredTextSelectedAtAnyInstance)
{
_textSnapShot.setSelected(_beginIndex,_endIndex+1,false); //this is the global variable to the class
}
else
{
_textSnapShot.setSelectColor(0xfff100);
_textSnapShot.setSelected(_beginIndex,_endIndex+1,true);
}
if(saveHighlight)
{
countHighlightedSegments();
}
}
}
Code I use to redraw previously drawn highlight when I return to the page:
public function showHighlights(textSnapShot:TextSnapshot,currentPageNum:int):void
{
if(currentPageNum >= 0)
{
textSnapShot.setSelected(0,textSnapShot.charCount,false);
var pageVO:PageVO = _model.eBookVO.eBookPagesVO.getItemAt(currentPageNum) as PageVO;
var objColl:ArrayCollection = new ArrayCollection();
objColl.source = pageVO.highLightSelection;
for(var i:int=0;i<objColl.length;i++)
{
var highlightVO:HighlightVO = new HighlightVO();
highlightVO.beginIndex = objColl.getItemAt(i).beginIndex;
highlightVO.endIndex = objColl.getItemAt(i).endIndex;
setHighlightedSegment(textSnapShot,highlightVO.beginIndex,highlightVO.endIndex);
}
}
}
private function setHighlightedSegment(textSnapShot:TextSnapshot,beginIndex:int,endIndex:int):void
{
textSnapShot.setSelectColor(0xfff100);
textSnapShot.setSelected(beginIndex,endIndex,true);
}
Looking forward to your support to resolve this issue.
Regards,
JS
What you're doing is not 'caching', it's preloading previous/next pages. Also, what you're doing is really bad practice. I'm not even sure why you're casting these things into MovieClips unless the SWFs are that; if they're Flex SWFs, they'll be UIComponents. I would recommend you rethink your approach. I wouldn't even bother copying the children or anything over. Once the browser loads a SWF, it is now part of the browser cache, meaning the next time it's requested, it won't actually download it.
If you want to 'cache' your SWFs for a quicker next/previous page flipping, I would recommend you use something like SWFLoader to just load the other SWFs without actually adding it to the display, then removing it from memory. That will cache the SWFs for you in the browser. Then when the user click previous/next, just change the url of the main swfloader of the currently displayed page and it will load it up really quickly. No downloading since it's already cached, it will just need to instantiate.
i'm pulling my hair out. need to print html generated invoices from a distant server, using
the print class, event if bitmap is set as false, will render the invoice a bitmap. at least the text is blurry and not usable. alivepdf could be a solution but i need to print straight, not save the pdf locally. i don't even understand, giving the fact this print class sucks badly, flex won't allow a simple javascript print function from the remote page.
i beg for some help here !
thank you
Why don't you use the browser to print?
Here's an example:
Put this in your index.html template:
<script language="JavaScript">
function printPage(htmlPage)
{
var w = window.open("about:blank");
w.document.write(htmlPage);
w.print();
w.close();
}
</script>
Put this in your Flex Project. What you're doing is checking to see if you have access to ExternalInterface to have access to the browser. Then you're going to use the ExternalInterface static method of "call" to call the javascript:
import mx.controls.Alert;
import flash.external.ExternalInterface;
public static function PrintHtmlPage(pHtmlPage:String):void
{
if (ExternalInterface.available)
{
try
{
ExternalInterface.call("printPage",pHtmlPage);
}
catch (error:SecurityError) { Alert.show("Security Error"); }
catch (error:Error) { Alert.show("Error");}
}
else { Alert.show("ExternalInterface not avalible");}
}
Now the user can print clean html from their browser!
http://cookbooks.adobe.com/post_How_to_print_in_Flex_using_browser_capabilities-11468.html
EDIT:
If you are using AIR and need to do this, you can try using AlivePDF and following this tutorial:
use alivepdf (http://alivepdf.bytearray.org/), and look at this tutorial for printing from AIR (http://murrayhopkins.wordpress.com/2011/01/07/using-alivepdf-to-print-from-air-javascript-via-actionscript3-part-1/)
parse the HTML into Spark components, and then add them as a Sprite, then use printAsBitmap = true in your option, and FlexPrintJob
I have a flex Air program that loads external content with the HTMLLoader. Now for some reason whenever I load a page that has any flash content a blank system window pops up outside of my program. It's completely blank, all white with min, max and close buttons. If I close it any flash content I loaded stops working. For the life of my I can't figure out what's happening and there's no messages in the console and no title for the window.
Does anyone have any ideas? I appreciate any help you can give. Here's the code I'm using:
private var webPage:HTMLLoader;
private function registerEvents():void
{
this.addEventListener(gameLoadEvent.GAME_LOAD, gameLoad);
//webPage = new HTMLLoader();
}
//function called back from Game Command to load correct game
private function gameLoad(event:Event):void
{
var gameEvent:gameLoadEvent = event as gameLoadEvent;
loadgame(gameEvent.url, gameEvent.variables);
}
private function loadgame(url:String, variableString:String):void
{
DesktopModelLocator.getInstance().scaleX = 1;
DesktopModelLocator.getInstance().scaleY = 1;
//var url:String = "http://pro-us.sbt-corp.com/aspx/member/LaunchGame.aspx";
var request:URLRequest = new URLRequest(url);
//var variables:URLVariables = new URLVariables("gameNum=17&as=as1&t=demo&package=a&btnQuit=0");
if(variableString != null && variableString != ""){
var variables:URLVariables = new URLVariables(variableString);
variables.exampleSessionId = new Date().getTime();
variables.exampleUserLabel = "guest";
request.data = variables;
}
webPage = HTMLLoader.createRootWindow(true, null, true, null);
webPage.height = systemManager.stage.nativeWindow.height - 66;
webPage.width = systemManager.stage.nativeWindow.width;
webPage.load(request);
webPage.navigateInSystemBrowser = false;
flexBrowser.addChild(webPage);
}
]]>
</mx:Script>
<mx:HTML id="flexBrowser" width="1366" height="658" backgroundAlpha="0.45" creationComplete="registerEvents();" x="0" y="0">
</mx:HTML>
you're not using any of the capabilities of your html component. As is, it may as well be a canvas since all you're doing is addChild to flexBrowser, a DisplayObjectContainer. Though I wouldn't do it this way, you can pretty simply set the flexBrowser.htmlLoader.load(request); and get rid of all that webPage stuff.
Is your application using a transparent window? air won't display flash content in the HTMLLoader in that case, see http://bugs.adobe.com/jira/browse/SDK-15033
One workaround is to use http://code.google.com/p/adobe-air-util/source/browse/trunk/src/net/tw/util/air/HTMLOverlay.as.
I had to do some changes to get it to work well with our app. I sent an email to the project owner to contribute the changes, if you are still on it I can send you the patch. The most important change, is that the html overlay window does go behind other apps when switching i.e. alt-tab or opening another app.
Update: I committed the changes to the overlay above, check it out as it should work for you as well. I know it seems like an awful workaround, but there doesn't seem to be anything better until adobe fixes the issue. If you do see something better, make sure to post the update :)
This problem has been fixed in AIR 1.5.2:
Before AIR 1.5.2, SWF content embedded in and HTML container in a transparent window could not be displayed. With AIR 1.5.2, SWF content can be displayed with certain wmode settings.
I can display a PDF file from byte[] in asp.net.
The problem is that it contains hyperlinks and I want to disable or remove these hyperlink events.
Docotic.Pdf, the library I am involved with, can be used to find hyperlinks in PDFs and remove them.
Here is the sample code that does exactly this:
public static void RemoveHyperlinks(string inputFile, string outputFile)
{
using (PdfDocument doc = new PdfDocument(inputFile))
{
foreach (PdfPage page in doc.Pages)
{
for (int i = 0; i < page.Widgets.Count; i++)
{
PdfWidget widget = page.Widgets[i];
PdfActionArea actionArea = widget as PdfActionArea;
if (actionArea != null)
{
PdfUriAction linkAction = actionArea.Action as PdfUriAction;
if (linkAction != null)
{
page.Widgets.RemoveAt(i);
i--;
}
}
}
}
doc.Save(outputFile);
System.Diagnostics.Process.Start(outputFile);
}
}
Please note that some viewers can detect hyperlinks from text and still present them as clickable areas even though there is not links defined in PDF itself. For example, Adobe Reader with certain settings can do just that.
P.S. I know this question is old, but maybe my answer will benefit new visitors.
You can libraries like this one to open and modify the PDF file and convert every hyperlink object to simple text.