package com.inet.html.samples.paged;
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.WindowConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.View;
import com.inet.html.InetHtmlDocument;
import com.inet.html.InetHtmlEditorKit;
import com.inet.html.InetHtmlFactory;
import com.inet.html.ViewPainter;
/**
* This sample shows how to render an HTML page into several page images. This is especially useful for printing
* or splitting large pages to avoid memory issues.
* The sample uses a custom {@link ViewPainter} to control the clipping of all content elements (which are
* text, images and horizontal rules). Any box element is drawn and clipped like normal.
*/
public class PagedRenderer {
/**
* This method renders the HTML referenced by an URL into several images without clipping text or image
* elements.
* @param url the page to be rendered
* @param width the width of a page
* @param height the height of a page
* @return the rendered pages, never null
* @throws IOException for a null or invalid URL specification
*/
public static List renderPaged( String url, int width, int height ) throws IOException {
// prepare the EditorKit with the custom PainterFactory
final PagedPainterFactory painterFactory = new PagedPainterFactory();
final InetHtmlFactory viewFactory = new InetHtmlFactory( painterFactory, new InetHtmlFactory.DefaultFontFactory() );
InetHtmlEditorKit kit = new InetHtmlEditorKit(){
@Override
public javax.swing.text.ViewFactory getViewFactory() {
return viewFactory;
};
@Override
public InetHtmlDocument createDefaultDocument() {
InetHtmlDocument doc = (InetHtmlDocument)super.createDefaultDocument();
// disable async to make the code shorter; if you require async loading, simply
// put everything after the setPage-Call into a PropertyChangeListener which listens
// for a change of "page"
doc.setAsynchronousLoadPriority(-1);
// set media to print to cause the renderer to wait for the images to be loaded
doc.setDocumentProperty( InetHtmlDocument.PROPERTY_MEDIA, InetHtmlDocument.MEDIA_PRINT );
return doc;
}
};
// Create the EditorPane to load and render the content
JEditorPane editor = new JEditorPane();
editor.setEditorKit( kit );
editor.setPage( url );
editor.setSize( width, 1 );
editor.setOpaque( false ); // to avoid getting a look&feel dependent background
editor.setBorder( new EmptyBorder( 0, 0, 0, 0 ) ); // required to get rid of the 3px borders
// To avoid the overhead of the EditorPane, use only the root render the content
View root = editor.getUI().getRootView( editor );
root.setSize( width, 1 ); // this forces the layouter to layout with 'width' and the minimum height possible for the current content
int requiredHeight = (int)root.getPreferredSpan( View.Y_AXIS );
editor.setSize( width, requiredHeight );
List images = new ArrayList();
int yOffset = 0;
while(true){
// create page image
BufferedImage img = new BufferedImage( width, height, BufferedImage.TYPE_INT_ARGB );
images.add( img );
// create sub-graphics to translate the clip into the image area
Graphics g2d = img.getGraphics().create( 0, -yOffset, img.getWidth(), yOffset + img.getHeight() );
g2d.setClip( 0, yOffset, img.getWidth(), img.getHeight() );
// reset latest y-offset of the painter factory and render the page
painterFactory.reset();
editor.paint( g2d );
yOffset = painterFactory.getNextYOffset();
if( yOffset == Integer.MAX_VALUE ){
// NOTE: this yOffset can mean two different states: either this was the last page so nothing
// was clipped OR no elements hit the lowe bound of the clip(in that case this was NOT the last page)
int maxY = (int)g2d.getClipBounds().getMaxY();
if( maxY >= requiredHeight ){
// no clipping and the clip covered the bottom area of the document -> this was the last page
break;
} else {
// the clip did only cover an area somewhere within the document, continue
yOffset = maxY;
}
}
}
return images;
}
/**
* Illustration of the CSS2.1 spec main page rendered into 800x600 images
*/
public static void main( String[] args ) throws IOException {
final List images = renderPaged( "http://www.w3.org/TR/CSS2/", 800, 600 );
JFrame f = new JFrame( "Pages" );
f.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE );
f.setLayout( new BorderLayout() );
final JLabel label = new JLabel( new ImageIcon( images.get( 0 ) ) );
f.add( label, BorderLayout.CENTER );
final SpinnerNumberModel model = new SpinnerNumberModel( 0, 0, images.size()-1, 1 );
final JSpinner spinner = new JSpinner( model );
f.add( spinner, BorderLayout.NORTH );
spinner.addChangeListener( new ChangeListener() {
@Override
public void stateChanged( ChangeEvent e ) {
label.setIcon( new ImageIcon( images.get( model.getNumber().intValue() ) ) );
}
});
f.pack();
f.setVisible( true );
}
}