You are hereAria User Guide / Section III: In Depth / Printing and Export
Printing and Export
Printouts can be produced in a number of different ways, directly and indirectly through applications like Excel and OpenOffice. Aria provides some easy to use classes that can be used to print pages or framesets from an application. Custom printed pages can also be to create a sort of report generation.
Direct printing
Aria's pages can be printed directly using the built-in infrastructure. However it should be noted that this may not be appropriate in all situations. A page created for on-screen, landscape rendering may not be best suited to the high resolution, portrait format of most printers. Aria applications can also include special printout pages where the layout and aspect can be given a more suitable configuration. Using the data binding schemes promoted by Aria this should involve little additional work while at the same time adding versatility to the printout infrastructure.
The printing infrastructure
Printing in Aria uses the normal Java printing API and mechanisms, but in order to hide some of the low level details Aria provides some additional infrastructure. The printing infrastructure revolves around three classes in the org.formaria.print
package. These classes are:
|
Printout |
This class encapsulates the print job. Control of headers and footers is also provided. |
|
PrintablePage |
A decoration of the |
|
PrintableFrameSet |
A decoration of a frameset to allow printing of the frameset's pages. Again scaling is performed as needed. |
These classes act as decorations of the
Page
class so that the pages can be configured for on-screen use or for printing or they can be used for both purposes. This decoration also means that the pages can be designed as any other page with little need for special considerations.
The use of these classes are as follows:
Printing a Frameset
/** * On a mouse click print this page */ public void printPage() { // Hide the navigation buttons for the home page if ( wasMouseClicked() ) { Printout po = new Printout(); po.showPageFormat(); po.setJobName( "Survey Printout" ); po.setHeader( "Left Header", Printout.LEFT ); po.setHeader( "Right Header", Printout.RIGHT ); po.setFooter( "Footer", Printout.LEFT ); po.setPageNumbers( false, Printout.RIGHT, "Page " ); int numFrames = pageManager.getNumTargets(); PrintableFrameSet pf = new PrintableFrameSet( po ); for ( int i = 0; i < numFrames; i++ ) pf.addFrame( pageManager.getTarget( i ) ); po.addFrame( pf ); po.print(); } }
The headers and footers are set so that their height is deducted from the printable area of the `page'. Headers and footers can be position to the
LEFT
,
RIGHT
or
CENTER
of the page.
Creating print forms
By default the printer is setup to print in
Landscape
format so as to match the aspect ratio of a typical screen. The printing framework automatically scales pages to the printer resolution so it is possible to create pages with different sizes or sizes more suited to the printer aspect ratio and resolution. Therefore custom pages could be setup for printing.
When using custom pages and adding them to the PrintOut you will need to take care of the initialization the page display framework would normally provide. This initialization includes updating layouts (
doLayout
), setting sizes, and updating bound components (
updateBoundComponentValues
) and perhaps even calling the page lifecycle methods (
pageCreated
,
pageActivated
).
Printing tables
As of version 2.0 Aria does not make use of the new JSE 5.0 printing support. Therefore if you wish to print tables you will need to handle sizing and pagination issues directly.
Other printout issues
While most of the time little by way of special consideration needs too be given to printing there are a few issues worth considering when preparing content.
|
The resolution of printers is typically in thousands of dots per inch (dpi) whereas on-screen resolution is of the order of 72 dpi. An unscaled raster graphic that works well on screen will appear tiny on a printout and while Aria scales images it cannot (in most cases) increase the image resolution and so the printed quality of the image may suffer. The solution to this problem is to use `lossy' raster graphics like JPEG that can interpolate the image or to use vector graphics like SVG that are ideal for scaling. |
|
|
Tables |
Tables frequently scroll or have varying amounts of content and this is not ideal for printing. At best a printout can accommodate vertical scrolling, but horizontal scrolling can rarely be accommodated well. When using tables pay special attention to testing with a variety of data. |
|
Aspect |
As has been noted above the resolution and aspect of the printed page normally differs from on-screen resolution. Consider using special print-only pages to accommodate your printouts. The print-only page can derive from the same class as the on-screen equivalent so any support functions and data binding can be reused. |
Outputting to file
Another mechanism for printing is to use intermediate formats like HTML and Office file formats. An advantage of these formats is that the output can be of use for integration with other applications or within other documents and therefore you are not limited to the print format of your application.
Aria provides some utility classes to facilitate output to some of the more common formats. The utilities work best when some common elements of the output are grouped, thereby allowing reuse of code. The following example shows how a parts list can be generated in a number of formats. The example includes output that can be configured in several ways, exported to file or clipboard, and opened or printed:
First lets look at setting up the infrastructure. The output generation is facilitated by some exporters than do some of the most common formatting. Depending on the options chosen the appropriate exported is setup.
Setup the export infrastructure
private void createExporter( String fileName ) throws Exception { if ( textRb.getState()) exporter = new XExportHelper(); else if ( wordRb.getState()) exporter = new XExportHelper( ".doc" ); else if ( xmlRb.getState() ) exporter = new XmlExportHelper(); else if ( htmlRb.getState() ) exporter = new HtmlExportHelper(); else if ( excelRb.getState() ) exporter = new ExcelExportHelper( fileName ); }
The
createExporter
method creates the processor for each type of export format that is supported. Each export format then has specific properties that must be set::
Setup the export format details
private void exportText( Writer w ) { try { exporter.setOutputWriter( w ); exporter.setComponentFactory( pageHelper.componentFactory ); String delim = delimiterEdit.getText(); exporter.setLeftDelimiter( delim ); exporter.setRightDelimiter( delim ); delim = separatorEdit.getText(); exporter.setFieldSeparator( delim ); exporter.setFieldNameSeparator( delim ); exporter.setUseWindowsLineEnd( windowsCheck.getState()); doExport(); exporter.close(); } catch ( IOException ex ) { } } private void exportXml( Writer w ) { try { exporter.setOutputWriter( w ); exporter.setComponentFactory( pageHelper.componentFactory ); exporter.setUseWindowsLineEnd( windowsCheck.getState()); doExport(); exporter.close(); } catch ( IOException ex ) { } } private void exportHtml( Writer w ) { try { exporter.setOutputWriter( w ); exporter.setComponentFactory( pageHelper.componentFactory ); exporter.setUseWindowsLineEnd( windowsCheck.getState()); doExport(); exporter.close(); } catch ( IOException ex ) { } } private void exportExcel( String fileName ) { try { exporter = new ExcelExportHelper( fileName ); exporter.setComponentFactory( pageHelper.componentFactory ); exporter.setUseWindowsLineEnd( windowsCheck.getState() ); doExport(); exporter.close(); } catch ( IOException ex ) { ex.printStackTrace(); } }
Most of this setup code is straightforward and involves setting up the export processor. The most involved is setting up the text export as a delimited file can vary by the type of delimiter and how the text values are separated. Some formats even have different left and right delimiters.
The actual export is initiated in the
BeginExport
method:
Start the export process
public void beginExport() { String targetFile = locationEdit.getText(); int type = TEXT; try { if ( wasMouseClicked() ) { if ( !clipboardCheck.getState() && ( targetFile.length() == 0 ) ) { showMessage( "Unable to Export", "You must enter a file name or click browse to choose a file!" ); return; } root = DataModel.getInstance(); Writer w = null; createExporter( targetFile ); // Choose the output, either the clipboard or an output stream (file) if ( !clipboardCheck.getState() && !excelRb.getState() ) w = exporter.setupWriter( this, targetFile ); else w = new StringWriter(); if ( w != null ) { // Start the export process if ( textRb.getState() ) exportText( w ); else if ( xmlRb.getState() ) { exportXml( w ); type = XML; } else if ( htmlRb.getState() ) { exportHtml( w ); type = HTML; } else if ( excelRb.getState() ) { exportExcel( locationEdit.getText() ); type = EXCEL; } else if ( wordRb.getState() ) { exportText( w ); type = WORD; } if ( clipboardCheck.getState() ) { exporter.setClipboardContents( ( ( StringWriter ) w ).getBuffer().toString() ); } } if ( exportRb.getState() || exportAndOpenRb.getState()) { showMessage( "Export complete", "Your file can be found at " + targetFile ); if ( exportAndOpenRb.getState()) doOpen( targetFile, false, type ); } else if ( exportAndPrintRb.getState() ) { showMessage( "Export complete", "Your file has been printed and can be found at " + targetFile ); doOpen( locationEdit.getText(), true, type ); } } } catch ( Exception ex ) { ex.printStackTrace(); showMessage( "Export failed", "Unable to export to " + targetFile ); } }
The export process involves setting up the output stream (or the clipboard) and calling the appropriate output method to take account of the output variations. The export process then finishes by writing the output and displaying some messages where appropriate.
If opening a file for saving the document then the following support methods helps access the file.
Open the output file
/** * Open or print a file * @param fileName the file name * @param doPrint true to print */ private void doOpen( String fileName, boolean doPrint, int fileType ) { // This method is all over the palce and needs to be refactored! String command = "start"; try { File tempFile = null; String tempFileName = null; if ( doPrint ) { switch ( fileType ) { case TEXT: command = "NOTEPAD /P" + "\"" + fileName + "\""; break; case XML: case HTML: case EXCEL: case WORD: { if (( tempPrintFileName == null ) || ( fileType != lastFileType )) { tempFile = File.createTempFile( "doPrint", ".bat" ); tempPrintFileName = tempFile.getCanonicalPath(); lastFileType = fileType; tempOpenFileName = null; } tempFileName = tempPrintFileName; if ( tempFile != null ) { OutputStreamWriter writer = new OutputStreamWriter( project.getBufferedOutputStream( tempFileName, false ) ); writer.write( getOfficePath( fileType, doPrint ) + " %1 /mFilePrintDefault" ); writer.flush(); writer.close(); } command = "start \"" + tempFileName + "\" \"" + fileName + "\""; ; } break; } } else { if (( tempOpenFileName == null ) || ( lastFileType != fileType )) { tempFile = File.createTempFile( "doOpen", ".bat" ); tempOpenFileName = tempFile.getCanonicalPath(); lastFileType = fileType; tempPrintFileName = null; } tempFileName = tempOpenFileName; if ( tempFile != null ) { OutputStreamWriter writer = new OutputStreamWriter( project.getBufferedOutputStream( tempFileName, false ) ); writer.write( "start %1 %2 %3 %4 %5 %6 %7 %8 %9" ); writer.flush(); writer.close(); } } String line; String cmdLine; if ( doPrint ) cmdLine = command; else cmdLine = "start \"" + tempFileName + "\" \"" + fileName + "\""; File targetFile = new File( fileName ); File startDir = new File( targetFile.getParent() ); if ( BuildProperties.DEBUG ) DebugLogger.log( "Running: " + cmdLine ); String osName = System.getProperty( "os.name" ); String[] cmd = new String[ 3 ]; if ( osName.equals( "Windows 95" ) || osName.equals( "Windows 98" ) ) { cmd[ 0 ] = "command.com"; cmd[ 1 ] = "/C"; cmd[ 2 ] = cmdLine; } else { cmd[ 0 ] = "cmd.exe"; cmd[ 1 ] = "/C"; cmd[ 2 ] = cmdLine; } Process p = Runtime.getRuntime().exec( cmd ); BufferedReader input = new BufferedReader( new InputStreamReader( p.getInputStream() ) ); while ( ( line = input.readLine() ) != null ) { if ( BuildProperties.DEBUG ) System.out.println( line ); } input.close(); } catch ( IOException ex ) { ex.printStackTrace(); } }
An output file can be required for printing as the approach we take is to generate a temporary file and then invoke an application to print the document.
The opening of files in this way also requires that we build the command line appropriate for each application.
And when exporting to office it is necessary to find the office installation:
private String getOfficePath( int fileType, boolean doPrint ) { try { String applicationQuery = null; switch ( fileType ) { case EXCEL: applicationQuery = REGQUERY_UTIL + "\"HKCR\\Applications\\EXCEL.EXE\\shell\\edit\\command"; break; case HTML: if ( doPrint ) applicationQuery = REGQUERY_UTIL + "\"HKCR\\Applications\\WINWORD.EXE\\shell\\edit\\command"; else applicationQuery = REGQUERY_UTIL + "\"HKCR\\Applications\\iexplore.exe\\shell\\edit\\command"; break; case TEXT: case WORD: default: applicationQuery = REGQUERY_UTIL + "\"HKCR\\Applications\\WINWORD.EXE\\shell\\edit\\command"; break; } Process process = Runtime.getRuntime().exec( applicationQuery ); StreamReader reader = new StreamReader( process.getInputStream() ); reader.start(); process.waitFor(); reader.join(); String result = reader.getResult(); int p = result.indexOf( REGSTR_TOKEN ); if ( p == -1 ) return null; result = result.substring( p + REGSTR_TOKEN.length() ).trim(); result = result.substring( 0, result.indexOf( "\"", 1 ) + 1 ).trim(); return result; } catch ( Exception e ) { return null; } }
Ultimately, to generate the document we must ouput each part of the document
Run through each step of the document
private void doExport() throws IOException { exporter.startDocument(); exportPartsList(); exporter.endDocument(); }
And finally the elements within that document section must be output.
Run through each step of the document
private void exportPartsList() throws IOException { exporter.writeSectionTitle( "Components" ); exporter.setOutputFieldNames( false ); exporter.startTable(); exporter.startHeader(); exporter.writeField( "type", "" ); exporter.writeField( "description", "" ); exporter.writeField( "codeNo", "" ); exporter.writeField( "quantity", "" ); exporter.endHeader(); int itemId = 0; int numNodes = getNumNodes(); for ( int i = 0; i < numNodes; i++ ) { DataModel targetNode = getNode( i ); if ( targetNode != null ) { exporter.startGroup(); exporter.startRecord(); exporter.startElement( "component" ); exporter.writeField( "type", targetNode.getAttribValueAsString( BaseModel.VALUE_ATTRIBUTE )); exporter.writeField( "description", pageHelper.componentFactory.translate( (String)targetNode.get( "@Description" ) )); exporter.writeField( "codeNo", ( String ) selectedNode.get( "@Code_no" )); exporter.writeField( "quantity", "1" ); exporter.endRecord(); int accessories = 0; for ( int ij = 0; ij < accessoryList.length; ij++ ) { Object accessoryCode = selectedNode.get( "@" + accessoryList[ ij ] ); if ( accessoryCode != null ) { exporter.startRecord(); if ( accessories == 0 ) exporter.endElement(); exporter.startElement( "accessory" ); exporter.writeField( "type", "" ); exporter.writeField( "description", pageHelper.componentFactory.translate( Functional.accessoryDescription[ ij ] )); exporter.writeField( "codeNo", (String)accessoryCode ); exporter.writeField( "quantity", new Integer( Functional.accessoryQuantity[ ij ] ).toString() ); accessories++; exporter.closeElement(); exporter.endRecord(); } } if ( accessories == 0 ) exporter.closeElement(); else exporter.matchElement(); exporter.endGroup(); } } } } exporter.endTable(); exporter.setOutputFieldNames( true ); exporter.writeSectionEnd(); exporter.writeBlankLine(); }
This last output method is very much application specific, but the example above groups some items (the accessories) with a main item.
To create a print out with this mechanism requires a more work than printing directly but it offers more flexibility. Furthermore the content of an Excel sheet can be alot more powerful than a flat printout.
Printing via Excel
Aria includes a set of wrapper classes to ease the use of Excel sheets. While Excel worksheets have many potential uses they can be used for printing purposes with the sheet being created directly or with a pre-prepared sheet acting as a template. Aria also includes classes to allow you easily launch Excel to preview the `printout' prior to actual printing.
Using the code above we can get the following Excel sheet (displayed in OpenOffice 2.0):
Printing and interacting with OpenOffice
OpenOffice can be used in much the same way as Excel however OpenOffice provides some extra programming control and support that can be useful in some situations.
OpenOffice can be run in a server mode where the worksheet is generated or populated by a server. This opens up the possibility of doing some sorts of workflow.
Printing via HTML
HTML provides another option for printing. Aria includes support for printing things such as forms and tables to HTML. However because of the wide variety of formats that could be generated you should be prepared to manually code some of the HTML generation.
Again using the code above we can get the following HTML, shown in the Mozilla FireFox browser:
- Printer-friendly version
- Login or register to post comments