12 Replies Latest reply on Feb 10, 2011 8:38 AM by macromedical

    Save mx:HTML full content to image

    Ben Smeets Level 1

      After searching my pants off for the correct approach for this, I need some help regarding this. I want to save the contents of an html component to an image (png/jpg/whatever) in the filesystem. The added bonus, is that I want to render the entire HTML page, not just the visible part in the component.

       

      A few approaches I tried:

      - Trying to get some reference to the canvas that is drawn on in the html component, no luck (this seemed to me to be the easiest solution)

      - Trying out a way of splitting the entire html page in multiple squares and creating seperate bitmaps for each viewport's content, after that combining them into 1.

       

      Some problems I have:

      - No way to get an easy reference to the entire html-canvas in the html component (that I could find).

      - Lot's of other stuff that went wrong which made me think there has to be an easier way.

      - Pages can become 10000px or more high easy, how do we work with bitmaps this size taking the bitmap size limit in the player into account?

       

      I know this should be possible, since I've seen Air apps do it already (Webkut e.g.).

       

      Any help appreciated,

       

      Ben

        • 1. Re: Save mx:HTML full content to image
          Saumitra Bhave Level 3

          You can use BitmapData's draw method to get snapshot of any IBitmapDrawable control.

          You can use its extra parameters to clip the rectangle or to apply transformations.

           

          Sample Code:

           

          protected function h_completeHandler(event:Event):void
             {
             var b:BitmapData = new BitmapData(h.width, h.height);
              b.draw(h);
              var c:Bitmap = new Bitmap(b);
              this.stage.addChild(c);
              c.x = 101;
              c.y = 0;
             }

            ]]>
          </mx:Script>
          <mx:HTML id="h" location="http://google.com" x="0" y="0" width="100" height="100" complete="h_completeHandler(event)" />

          • 2. Re: Save mx:HTML full content to image
            Ben Smeets Level 1

            Tnx Saumitra, I know. The source of the problem with this is that it (what I could find out) only allows me to get the "visible" part of the html page. Depending on the size of the HTML component. What I was looking for was a way to get the "entire" html page, independant of the viewport. If I'm right, this won't let me do that? If it does, please let me know

            • 3. Re: Save mx:HTML full content to image
              Saumitra Bhave Level 3

              Is it ok for you application if it resizes HTML control as per the actual HTML Content's width and height? and come back to it original dimension?

               

              I am asking this because of the second solution you tried (Having Multiple snapshots and combining them)

              • 4. Re: Save mx:HTML full content to image
                Saumitra Bhave Level 3

                May be this code will help you.

                 

                 

                protected function windowedapplication1_applicationCompleteHandler(event:FlexEvent):void
                {
                     var h2:HTMLLoader = new HTMLLoader();
                     var r:URLRequest = new URLRequest("http://yahoo.com");
                     var s:Stage = this.stage;
                     h2.addEventListener(Event.COMPLETE,function handle(e:Event):void{
                          h2.width = h2.contentWidth;
                          h2.height = h2.contentHeight;
                
                          h2.addEventListener(Event.HTML_RENDER,function (e:Event):void{
                               var b:BitmapData = new BitmapData(h2.contentWidth, h2.contentHeight);
                
                               b.draw(h2);
                
                               var c:Bitmap = new Bitmap(b);
                               s.addChild(c);
                               c.x =0;
                               c.y = 0;
                          });
                     });
                     h2.load(r);
                }
                
                

                 

                This uses the core HTMLLoader(Which gives all the features that you may want from mx:HTML) class given in flash.html package. You can any time add it to stage by saying this.addChild(h2); currently this code only shows the Image and not the actual HTML.

                 

                You can also try above example by using mx:HTML controls htmlLoader property.

                 

                You can also use this example as it is. To display the page in mx:HTML and whenever you need bitmap internally call this code to get BitmapData without showing anything extra to the user.

                 

                Hope this helps.

                1 person found this helpful
                • 5. Re: Save mx:HTML full content to image
                  Ben Smeets Level 1

                  Tnx for the help Saumitra, the problem I come across with this approach (it's kind of linked to the splitting in multiple bitmaps I mentioned) is that the HTML control (HTMLLoader as well as far as I can tell) has a size limit. So when trying to set the height to 10000px or more (approx.) it throws an error

                   

                  Does help though! Cheers,

                   

                  Ben

                  • 6. Re: Save mx:HTML full content to image
                    Saumitra Bhave Level 3

                    You can add some extra code at line 6 n 7 to see if conentHeight is more than Limit. And then use the extra arguments of draw to clip accordingly. Anyways if the page Height is greater than LIMIT(8191) you cant even hold it in single bitmapData object.

                    • 7. Re: Save mx:HTML full content to image
                      Abram Adams Level 1

                      Ben, I'm in the same boat.  I've tried the approaches you mentioned with the same results.  I also tried auto scrolling the html content (instead of resizing the html component), no go.  My last attempt was with a BitmapDataUnlimited class (http://blog.formatlos.de/2008/05/28/bitmapdataunlimited/).  This allows drawing a bitmap of unlimited sizes.  So I resized the HTML component, passed it to the BitmapDataUnlimited.draw() method and... same problem.  Turns out HTMLLoader has a 2,880px limitation itself, so resizing it to something like 1000x15000 would cause the image to crop anyway.

                       

                      I guess the only way to do it within AS3 is to find out how to scroll the HTML component one page at a time, create a bitmap of each page, then merge them all at the end.  I had attempted this but the scroll feature of HTML doesn't seem to work.

                       

                      Frustrating.  If I get any further, I'll post it here.

                       

                      --Abram

                      • 8. Re: Save mx:HTML full content to image
                        Abram Adams Level 1

                        I was finally able to get a working solution.  I ended up using the scrolling method, in which I dispatch an event if there are more pages to process.  The handler for that event then simply called the function again.  This allowed the HTML component to scroll incrementally.  Sample below (this is bits and pieces of my PrintManager class, which does a lot more. I haven't tested the sample below) :

                         

                        private var _html:HTMLLoader;     
                        private var page:int = 0;
                        
                        public function snapshot(html:HTMLLoader):void{
                          var PAGE_HEIGHT:int = html.height;
                             var pages:int = Math.ceil(html.contentHeight/PAGE_HEIGHT);
                             var b:BitmapData;
                             
                             // make global reference to html object
                             _html = html;
                             //global counter of pages;
                             page++;
                             
                             // Register Listners     
                             FlexGlobals.topLevelApplication.addEventListener('NEXT_PAGE_PLEASE',nextPageHandler);
                             FlexGlobals.topLevelApplication.addEventListener('PRINT_JOB_COMPLETE',pringJobCompleteHandler);
                             
                             
                             // Without the timeout the window doesn't refresh in time to take a snapshot
                             setTimeout(function():void{
                                  b = new BitmapData(PAGE_WIDTH+50,PAGE_HEIGHT);                         
                                  b.draw(html);
                                  
                                  /* Code here to write the image to disk or whatever */
                                  
                                  // Ok, here's the magic.  If there are more pages to process we need to dispatch an event
                                  // telling our printer to execute this method agian.  If it is the last page we need to 
                                  // create the pdf and present it to the user.  Then reset the global vars used.
                                  if(page < pages){                                   
                                       FlexGlobals.topLevelApplication.dispatchEvent(new Event('NEXT_PAGE_PLEASE'));
                                  }else{          
                                       FlexGlobals.topLevelApplication.dispatchEvent(new Event('PRINT_JOB_COMPLETE'));                                   
                                  }     
                             
                             },100);
                        
                        }
                        
                        public function nextPageHandler(e:Event):void{     
                             FlexGlobals.topLevelApplication.removeEventListener('NEXT_PAGE_PLEASE',nextPageHandler);     
                             _html.scrollV = _html.scrollV+_html.height;
                             // This will snapshot the next screen.
                             snapshot(_html);
                             
                        }
                        
                        public function pringJobCompleteHandler(e:Event):void{
                             
                             FlexGlobals.topLevelApplication.removeEventListener('PRINT_JOB_COMPLETE',pringJobCompleteHandler);     
                             trace('print job complete');          
                             page = 0;
                             
                        }
                        

                         

                        Like I said, I copy/pasted bits and pieces from my class to post here.  You probably need to tweak it to get it to work.  One of the things I left out here was logic to calculate and clip the last page, since the last page likely contains the previous page you need to determine the height of the last page and use something to clip it.  I actually used BitmapData.copyPixels and copied a rect in the correct position and size from a temporary snapshot of the page.

                         

                        --Abram

                        1 person found this helpful
                        • 9. Re: Save mx:HTML full content to image
                          macromedical

                          I adapted the adobe PngEncoder to a new version 'PNGStackEncoder' that

                          is able to save a Vector of bitmaps into one large PNG, either

                          horizontal or vertical.

                          And so I refactored the HtmlToPng class. Now you can save 1 png from a

                          webpage that is a gazillion pixels high ( if your cpu holds it ).

                          Hope somebody will enjoy sooner or later.

                          Cheers,

                          Latcho

                           

                          Both classes pasted below.

                          Or grab source here:

                          http://greencollective.nl/blog/wp-content/uploads/2011/HtmlToPng_AIR.zip

                          • 10. Re: Save mx:HTML full content to image
                            Ben Smeets Level 1

                            Hi Abram,

                             

                            Between posts, I had the same solution (and limts you ran into) as you found out as well. I started off with a HTML component and after converting it to a HTMLLoader, i got the problem the scrollV didn't do anything... so if I manage to get that finally working (it should, not much different from your code) I'll post it here as well.

                             

                            Cheers, Ben

                            • 11. Re: Save mx:HTML full content to image
                              Ben Smeets Level 1

                              Nice one, the principles of the code I have now are near identical as the HTML2Png class you have here, I only have the issue (like mentioned above) that

                              for some reason the scrollV isn't taking. Thanks for sharing the code though, will help a lot!


                              • 12. Re: Save mx:HTML full content to image
                                macromedical Level 1

                                all issues mentioned above where my issues to

                                source updated and demo included. Can do multiple html captures on 1 instance now.


                                source and info :

                                http://greencollective.nl/blog/?p=34

                                example:

                                 

                                 

                                import com.latcho.air.transform.HtmlToPng;
                                
                                var htmlToPng:HtmlToPng = new HtmlToPng();      
                                htmlToPng.capture(    
                                                                'http://news.google.nl/news/section?pz=1&cf=all&l&topic=t&ict=ln'
                                                                , File.desktopDirectory.resolvePath('AirHtmlCapture/check1_VericalLimitedContentWidth560.png')
                                                                ,HtmlToPng.VERTICAL
                                                              );
                                htmlToPng.capture(    
                                                                'http://news.google.nl/news/section?pz=1&cf=all&l&topic=t&ict=ln'
                                                                , File.desktopDirectory.resolvePath('AirHtmlCapture/check1_HorizontalLimitedContentWidth560.png')
                                                                ,HtmlToPng.HORIZONTAL
                                                                ,0
                                                                ,800
                                                              );
                                htmlToPng.addEventListener(Event.COMPLETE, onAllImagesSaved);      
                                
                                


                                enjoy !
                                Latcho