LibreOffice SDK Guide: Chapter 41. Printing
Java's Print Service (JPS) and Office's printing APIs nicely compliment each other. For example, since Java 1.4 (February 2002), JPS has included printer discovery based on document types and printer attributes. but Office's API lacks such capabilities. JPS cannot easily print much beyond text and images; binary data can be sent to a printer, but it's left to the device to interpret and render it, which is only possible in high-end printers, not ordinary inkjets and laser printers. In contrast, Office can print a very wide range of document formats, including ODF, Microsoft, and PDF files.
This chapter starts by describing JPS's PrintService, which I'll use later to select a printer for the Office API. Then there's an overview of the printing capabilities of the Office API, with examples of how to print Writer, Impress, and Calc files.
The bad news is that Office's printing API has a few bugs and missing features. By 'missing' I mean parts of Office's printing GUI with no equivalents in the API. Also, buggy features in the API, such as the printing of multiple pages/sheet and slide handouts, work flawlessly in the GUI.
I'll finish with a short discussion of command line printing in Windows. The OS has several print utilities that are a little hard to find and use. Also, Office can be started from the command line in order to print documents.
1. The Java Print Service (JPS)
Java 1.4 saw the introduction of the Java Print service (JPS) API which allows a printer to be selected based on document types and attributes such as color output, paper sizes, and double-sided printing.
JPS printing passes through four stages:
- Selection of a print service (i.e. a printer or print driver), represented by a PrintService instance. The programmer can supply document types and printer attributes to limit the search.
- Creation of a Doc object for the document that's to be printed.
- Creation of a print job combining the Doc object and print request attributes. A listener can be attached to monitor what's happening during the printing.
- The printing is started, and performed asynchronously.
Document types are encoded as DocFlavor objects which contain data source details (e.g. whether the document comes from an input stream or a URL), and a MIME type (e.g. image/gif, application/pdf). A common misconception about DocFlavor is that it means that Java can print that kind of document. A DocFlavor is used to check whether a printer supports a document type, and the answer may be "no".
The following code locates print services that support GIF documents sent from a file:
// stage 1: find print services for GIF files
DocFlavor gifFlavor = DocFlavor.INPUT_STREAM.GIF;
PrintService[] services =
PrintServiceLookup.lookupPrintServices(gifFlavor, null);
The matching print services are returned in a PrintService[] array.
lookupPrintServices()'s second argument (which is null here) could be a set of print request attributes (PRAs).
There are four kinds of print attributes. The first two are requests sent from a program to the printer, which may be accepted or rejected:
- Print request attributes (PRA) ask for particular printing features, such as two- sided output, or a paper size.
- Doc attributes (DA) supply information about a document, such as its page size, use of color, or page ranges for printing.
The two other attributes types are used by the printer service to convey information about the printer and job back to the program.
- Print service attributes (PSA) give information about the print service, such as the printer's make and model or whether it is currently accepting jobs.
- Print job attributes (PJA) give information about the status of a particular print job, such as whether it has successfully finished.
The following code fragment looks for printer services that can print on both sides of A4 paper:
// stage 1 example using attributes
AttributeSet attrs = new HashAttributeSet();
attrs.add(MediaSizeName.ISO_A4);
attrs.add(Sides.DUPLEX);
PrintService[] services =
PrintServiceLookup.lookupPrintServices(null, attrs);
AttributeSet is an interface and HashAttributeSet an implementation which supports the four kinds of attributes.
The easiest way to find out about particular attribute is via the Attribute interface documentation at https://docs.oracle.com/javase/8/docs/api/index.html?java/util/jar/Attributes.html, which lists the attribute subclasses, such as MediaSizeName and Sides used in the example above.
The next fragment creates a Doc object for a GIF file, a stage 2 task in JPS printing:
// stage 2 : create a Doc object for a GIF file
FileInputStream in = new FileInputStream(fileName);
Doc doc = new SimpleDoc(in, gifFlavor, null);
The third argument of the SimpleDoc constructor can be a set of document attributes (DAs).
JPS stage 3 involves the creation of a print job, and perhaps a listener:
// stage 3: print job creation
DocPrintJob job = service[0].createPrintJob();
job.addPrintJobListener(new PrintJobAdapter() {...} );
The listener will receive information about the print job's progress as print job attributes (PJAs).
Stage 4 starts the printing:
job.print(doc, null);
The second argument of print() can be a set of print request attributes (PRAs).
1.1. Listing the Printers
The ListPrinters.java example in this section focuses on JPS stage 1. It prints short details about all the available printers, fuller information about the default printer, and a list of printer names. The main() function:
// in ListPrinters.java
public static void main(String [] args)
{
JPrint.listServices(); // short info
// JPrint.listServices(true); // full info, but slow to generate
PrintService ps = PrintServiceLookup.lookupDefaultPrintService();
System.out.println("Default printer \"" + ps.getName() + "\":");
JPrint.listService(ps, true); // full info on default printer
String[] pNames = JPrint.getPrinterNames();
System.out.println("Printer names (" + pNames.length + "):");
for(String pName : pNames)
System.out.println(" " + pName);
} // end of main()
Most of the functionality is implemented by the JPrint.java utility class.
JPrint.listServices() on one of my test machines prints the following:
-------- Print services (16) ---------- 1. "Send To OneNote 2010" color-supported:supported printer-name:Send To OneNote 2010 queued-job-count:0 printer-is-accepting-jobs:accepting-jobs Supported doc types: image/gif image/jpeg image/png application/octet-stream 2. "Ricoh Aficio MP 2000 PCL(Black Office Room)" color-supported:not-supported printer-name:Ricoh Aficio MP 2000 PCL(Black Office Room) queued-job-count:0 printer-is-accepting-jobs:accepting-jobs Supported doc types: image/gif image/jpeg image/png application/octet-stream : : 16. "CutePDF Writer" color-supported:supported printer-name:CutePDF Writer queued-job-count:0 printer-is-accepting-jobs:accepting-jobs Supported doc types: image/gif image/jpeg image/png application/octet-stream application/postscript
The information for each printer consists of four attributes, including its name and the MIME types that it can process. For example, the Ricoh Aficio MP 2000 printer can deal with standard graphics formats and "application/octet-stream". This latter type means that the printer will accept binary printer data, but doesn't mean that it can correctly print it. For instance, it may be output as a mishmash of ASCII characters.
The service listing methods in JPrint.java:
// in the JPrint class
public static void listServices(boolean showAll)
{ PrintService[] psa =
PrintServiceLookup.lookupPrintServices(null, null);
listServices(psa, showAll);
}
public static void listServices(PrintService[] psa,
boolean showAll)
{ if (psa != null && psa.length > 0) {
System.out.println("\n-------- Print services (" +
psa.length + ") ----------\n");
for (int i = 0; i < psa.length; i++) {
System.out.println((i+1) + ". \"" + psa[i].getName() + "\"");
listService(psa[i], showAll);
}
System.out.println("------------------\n");
}
else
System.out.println("No print services found");
} // end of listServices()
public static void listService(PrintService ps, boolean showAll)
{
// print basic attributes
Attribute[] attrs = ps.getAttributes().toArray();
for(Attribute attr : attrs)
System.out.println(" " + attr.getName() + ":" + attr);
// print the supported MIME types
System.out.print(" Supported doc types: ");
DocFlavor[] flavors = ps.getSupportedDocFlavors();
for (int j = 0; j < flavors.length; j++) {
// Filter out DocFlavors that have a representation class other
// than java.io.InputStream.
String repclass = flavors[j].getRepresentationClassName();
if (!repclass.equals("java.io.InputStream")) // restrict to files
continue;
System.out.print(" " + flavors[j].getMimeType());
}
System.out.println();
// print every printer attribute
if (showAll) {
System.out.println(" ----");
ArrayList<NamedAttribute> attrList = getAttributes(ps);
Collections.sort(attrList);
for (NamedAttribute attr : attrList)
printAttr(ps, attr.getAttribute());
}
System.out.println();
} // end of listService()
The lookupPrintServices() call in listServices():
PrintServiceLookup.lookupPrintServices(null, null)
has no DocFlavor or attribute arguments, so every printer service will be returned in the array. listServices() iterates through each PrintService object, and prints its basic attributes, the supported MIME types for files, and optionally a list of every printer attribute.
The four attributes returned by PrintService.getAttributes() are the tip of a very large iceberg. Java supports a multitude of attributes as seen if you look at the Java 8 Attribute documentation at https://docs.oracle.com/javase/8/docs/api/javax/print/attribute/Attribute.html. Over 70 subclasses are listed, including PrinterName, Sides, and Chromacity. A taste of what can be printed occurs when the default printer's details are listed:
// in ListPrinters.java...
PrintService ps = PrintServiceLookup.lookupDefaultPrintService();
System.out.println("Default printer \"" + ps.getName() + "\":");
JPrint.listService(ps, true); // full info
The default printer for my test machine is an old laser printer. ListPrinter.java reports the following:
Default printer "HP LaserJet 1200 Series PCL 6": color-supported:not-supported printer-name:HP LaserJet 1200 Series PCL 6 queued-job-count:0 printer-is-accepting-jobs:accepting-jobs Supported doc types: image/gif image/jpeg image/png application/octet-stream ---- chromaticity: color copies-supported: null ipp-attribute-fidelity: false job-name: Java Printing media: iso-a4 media-printable-area: (4.233,4.233)->(197.951,288.459)mm orientation-requested: portrait page-ranges: 1-2147483647 printer-resolution: 60000x60000 dphi requesting-user-name: Ad sheet-collate: uncollated sides: one-sided spool-data-destination: file:/C:/Users/Ad/Desktop/LibreOffice%20Tests/Printing%20Tests/o ut.prn sun-alternate-media: null
The "chromacity" attribute states how color and monochrome documents are mapped to each other. Probably the attribute the user really wants to examne is "color- supported" which shows that this printer only offers grayscales.
The main drawback of printing all these attributes is the time required to collect them, sort them, and remove duplicates; each printer may take several seconds to process.
1.2. Discovering Print Services
PrintServiceLookup.lookupPrintServices() should be called with DocFlavor and/or print request attributes to narrow down the search. Some examples can be found in Discovery.java. For instance, the following fragment looks for printers that can process JPEG files:
// in Discovery.java...
DocFlavor flavor = DocFlavor.INPUT_STREAM.JPEG;
PrintService[] psa =
PrintServiceLookup.lookupPrintServices(flavor, null);
System.out.println("\nServices that support " + flavor);
String[] pNames = JPrint.getPrinterNames(psa);
if (pNames != null)
for (String pName : pNames)
System.out.println(" " + pName);
It's very likely that the printers listed by this search can also output PDF, Word, PowerPoint and other file formats printable from Office, since Office delivers pages to a printer rendered as images.
However, if a search is made for printers that can process PDF files:
DocFlavor flavor = DocFlavor.INPUT_STREAM.PDF;
PrintService[] psa =
PrintServiceLookup.lookupPrintServices(flavor, null);
then no matches are found. This means that none of the printers can process PDF by themselves.
JPrint.findPrinterNames() lets printers be selected based on partial names. The following call collects all the printers whose names include "HP":
String[] pNames = JPrint.findPrinterNames("HP");
Multiple attributes can be used in a search. The following looks for all the "HP" printer services that support color:
// in Discovery.java...
AttributeSet attrs = new HashAttributeSet();
System.out.println("\nHP Services that support color:");
PrintService[] psa;
for (String pName : pNames) { // from the "HP" search above
attrs.clear();
attrs.add(new PrinterName(pName, null)); // must be included
attrs.add(ColorSupported.SUPPORTED);
psa = PrintServiceLookup.lookupPrintServices(null, attrs);
if (psa.length > 0)
System.out.println(" " + pName);
}
It's possible to look for color printers with less code:
// in Discovery.java...
AttributeSet attrs = new HashAttributeSet();
attrs.add(ColorSupported.SUPPORTED);
PrintService[] psa =
PrintServiceLookup.lookupPrintServices(null, attrs);
Unfortunately the returned print services include non-color printers. It seems that the printer name must be part of the attribute set to get a correct match.
DocFlavors and attributes can be combined, as in the following that looks for a printer that supports JPEG and A4 paper:
// in Discovery.java...
DocFlavor flavor = DocFlavor.INPUT_STREAM.JPEG;
AttributeSet attrs = new HashAttributeSet();
attrs.add(MediaSizeName.ISO_A4);
PrintService[] psa =
PrintServiceLookup.lookupPrintServices(flavor, attrs);
A quite different printer selection approach is to display a dialog box at run time to let the user set the search criteria. Java offers ServiceUI.printDialog() for this, which I've wrapped inside JPrint.dialogSelect():
// in the JPrint class
public static PrintService dialogSelect()
{
GUI.setLookFeel();
PrintService psa[] =
PrintServiceLookup.lookupPrintServices(null, null);
PrintService defaultService =
PrintServiceLookup.lookupDefaultPrintService();
PrintRequestAttributeSet attrs =
new HashPrintRequestAttributeSet(); // none
PrintService service = ServiceUI.printDialog(null, 100, 100,
psa, defaultService, null, attrs);
if (service == null)
System.out.println("No print service selected");
return service;
} // end of dialogSelect()
dialogSelect() initializes ServiceUI.printDialog() with a list of printer services, the default printer, and an empty print request attribute set.
In Discovery.java, JPrint.dialogSelect() is called like so:
// in Discovery.java...
PrintService ps = JPrint.dialogSelect();
if (ps != null) {
System.out.println("\nYou selected " + ps.getName());
JPrint.listService(ps, true); // full info
}
The dialog looks like Figure 1.
Figure 1. The ServiceUI.printDialog() Dialog.
Pressing the "Print" button returns a PrintService reference; pressing "Cancel" returns null.
1.3. Printing Using Java
After a print service has been selected, JPS stages 2-4 involve instantiating a Doc object, creating a print job, and starting the printing. These are managed by my JPrint.printFile():
// in the JPrint class
public static void printFile(PrintService ps, String fnm)
{
if (ps == null) {
System.out.println("Print service is null");
return;
}
DocPrintJob printJob = ps.createPrintJob(); // stage 2
try {
InputStream is = new FileInputStream(fnm);
DocFlavor flavor = getFlavor(ps, fnm);
Doc doc = new SimpleDoc(is, flavor, null); // stage 3
printJob.print(doc, null); // stage 4
is.close();
}
catch(Exception e) {
System.out.println("Unable to print " + fnm);
System.out.println(e);
}
} // end of printFile()
The user supplies a PrintService and a filename. A DocFlavor for the file is obtained by JPrint.getFlavor():
// in the JPrint class
public static DocFlavor getFlavor(PrintService ps, String fnm)
{
DocFlavor flavor = getFlavorFromFnm(fnm);
System.out.println("File-based DocFlavor: " + flavor);
if (!ps.isDocFlavorSupported(flavor)) {
System.out.println("Not supported by printer;
using autosense");
flavor = DocFlavor.INPUT_STREAM.AUTOSENSE;
}
return flavor;
} // end of getFlavor()
public static DocFlavor getFlavorFromFnm(String fnm)
{
String ext = fnm.substring(fnm.lastIndexOf('.') + 1).
toLowerCase();
if (ext.equals("gif"))
return DocFlavor.INPUT_STREAM.GIF;
else if (ext.equals("jpeg"))
return DocFlavor.INPUT_STREAM.JPEG;
else if (ext.equals("jpg"))
return DocFlavor.INPUT_STREAM.JPEG;
else if (ext.equals("png"))
return DocFlavor.INPUT_STREAM.PNG;
else if (ext.equals("ps"))
return DocFlavor.INPUT_STREAM.POSTSCRIPT;
else if (ext.equals("pdf"))
return DocFlavor.INPUT_STREAM.PDF;
else if (ext.equals("txt"))
return DocFlavor.INPUT_STREAM.TEXT_PLAIN_HOST;
else // try to determine flavor from file content
return DocFlavor.INPUT_STREAM.AUTOSENSE;
} // end of getFlavorFromFnm
getFlavorFromFnm() uses the file's extension to choose a DocFlavor.INPUT_STREAM constant, which is checked by the print service in PrintService.isDocFlavorSupported().
JPrint.printFile() is utilized in my JDocPrinter.java example:
// in JDocPrinter.java
public static void main(String[] args)
{
if ((args.length < 1) || (args.length > 2)) {
System.out.println("Usage: java JDocPrinter <filename>
[<(partial)printer-name>]");
return;
}
String fnm = args[0];
String pName = null;
if (args.length == 2) {
String[] pNames = JPrint.findPrinterNames(args[1]);
if (pNames != null) {
System.out.println("Using first match: \"" + pNames[0] + "\"");
pName = pNames[0];
}
}
if (pName != null)
JPrint.print(pName, fnm);
else {
PrintService ps = JPrint.dialogSelect();
if (ps != null)
// JPrint.printMonitorFile(ps, fnm);
JPrint.printFile(ps, fnm);
}
} // end of main()
JPrint.printMonitorFile() is fancier version of JPrint.printFile() which attaches a PrintJobListener to the print job. The listener's methods are triggered as the job passes through its printing states such as its delivery to the printer, completion, cancellation, or printing error.
The lines in JPrint.printMonitorFile() that add the listener to the print job are:
// in JPrint.printMonitorFile()...
DocPrintJob printJob = ps.createPrintJob();
printJob.addPrintJobListener(new PJWatcher());
PJWatcher implements the PrintJobListener interface:
// in the JPrint class
private static class PJWatcher implements PrintJobListener
{
public void printDataTransferCompleted(PrintJobEvent pje)
{ System.out.println(" >> Data transferred to printer"); }
public void printJobCanceled(PrintJobEvent pje)
{ System.out.println(" >> Print job was cancelled"); }
public void printJobCompleted(PrintJobEvent pje)
{ System.out.println(" >> Print job completed successfully"); }
public void printJobFailed(PrintJobEvent pje)
{ System.out.println(" >> Print job failed"); }
public void printJobNoMoreEvents(PrintJobEvent pje)
{ System.out.println(" >> No more events will be delivered"); }
public void printJobRequiresAttention(PrintJobEvent pje)
{ System.out.println(" >> Print job needs attention"); }
} // end of PJWatcher class
1.4. More Information on javax.print
Good sources of information on Java printing include the tutorial at https://docs.oracle.com/javase/tutorial/2d/printing/ which covers JPS and the older Java 2D printing API, and the JPS API user guide at https://docs.oracle.com/javase/8/docs/technotes/guides/jps/spec/JPSTOC.fm.html.
Two books I've found useful:
- Core Java, Volume II – Advanced Features Cay S. Horstmann; Gary Cornell Prentice Hall, 2016, 10th ed.
https://horstmann.com/corejava.html Chapter 7 includes a section on printing, including a table of attributes.
- Pro Java 8 Programming Terrill Brett Spell Apress, 2015, 3rd ed.
https://apress.com/9781484206423/ Chapter 11 is about printing.
2. Printing in Office
In this section I'll employ the print service search methods in JPrint.java to find a printer for Office. The service name is passed to functions that use the Office API to send a document to the printer.
Only a handful of Office methods are required to print a document; the real complexity of the API is the multitude of properties which configure how the document is output. Not surprisingly, these properties are spread across many modules since different document types (i.e. Writer, Draw, Impress, and Calc) are managed by different parts of the API.
Figure 2 shows the OfficeDocument services and their print-related interfaces. The words in brackets are the modules where the services are located. The rounded rectangles refer to print property tables explained later in this chapter.
Figure 2. The OfficeDocument Interface and its Print-related Interfaces.
The XPrintable interface is used when printing Writer, Draw, Impress, or Calc documents since their services all inherit OfficeDocument. Writer documents can also employ XPagePrintable for printing. Note that it isn't possible to print databases using XPrintable.
OfficeDocument also has XPrintJobBroadcaster and XPrintableBroadcaster interfaces for attaching listeners to a print job. However, XPrintableBroadcaster is no longer supported, having been superseded by XPrintJobBroadcaster .
The tricky aspects of XPrintable, XPrintJobBroadcaster, and XPagePrintable are the properties used to configure how they work. XPrintable.getPrinter() and XPrintable.setPrinter() utilize PrintDescriptor properties, the XPrintJobListener created by XPrintJobBroadcaster uses PrintableState properties, XPrintable.print() uses PrintOptions, and XPagePrintable employs PagePrintSettings. I'll be explaining all of these in due course, with the aid of the tables referred to in Figure 2.
We're not finished with properties yet. There's also several "XXXSettings" services which configure printing properties within a document. These are summarized by Figure 3.
Figure 3. The Setting Services.
The rounded rectangles refer to property tables described later in this chapter.
3. Printing any Document
At this point you may be thinking that Office printing is rather complex. In fact, if you're happy to print a document using the default printing settings then it's fairly simple. The DocPrinter.java example illustrates this by printing any Office document supplied on its command line to the default printer or to the one named in the second command line argument. The complete code:
// in DocPrinter.java
public static void main(String args[])
{
if ((args.length < 1) || (args.length > 3)) {
System.out.println("Usage: DocPrinter fnm
[printer-name [no-of-pages]]");
return;
}
String fnm = args[0];
String pName = JPrint.getDefaultPrinterName(); // default
if (args.length > 1) { // 2 or 3 args
String[] printerNames = JPrint.findPrinterNames(args[1]);
if (printerNames == null)
System.out.println("Using default printer: \"" +
pName + "\"");
else {
pName = printerNames[0];
System.out.println("Using first matching printer: \"" +
pName + "\"");
}
}
String pagesStr = "1-"; // default is print all pages
if (args.length == 3)
pagesStr = args[2];
XComponentLoader loader = Lo.loadOffice();
XComponent doc = Lo.openDoc(fnm, loader);
if (doc == null) {
System.out.println("Could not open " + fnm);
Lo.closeOffice();
return;
}
int docType = Info.reportDocType(doc);
XPrintable xp = Lo.qi(XPrintable.class, doc);
if (xp == null)
System.out.println("Cannot print; XPrintable is null");
else if (!Print.isPrintable(docType))
System.out.println("Cannot print that document type");
else {
Print.usePrinter(xp, pName);
Print.reportPrinterProps(xp);
Print.print(xp, pagesStr);
}
Lo.closeDoc(doc);
Lo.closeOffice();
} // end of main()
An optional third command line argument is a page range, given in string form. For instance, "2-4;6" will print pages 2 to 4 and 6 (with the page numbering starting at 1).
The printer name is obtained using methods from my JPrint.java class. The Office API takes over after the document has been loaded, and utilizes support functions in my Print.java class (note: no "J" in the name).
The document type is printed and returned by Info.reportDocType() as one of: Lo.UNKNOWN, Lo.WRITER, Lo.BASE, Lo.CALC, Lo.DRAW, Lo.IMPRESS, or Lo.MATH.
The loaded document is cast to XPrintable (see Figure 1), and Print.isPrintable() tests for those document types that can be printed using XPrintable.print():
// in the Print class
public static boolean isPrintable(int docType)
{ return ((docType == Lo.WRITER) || (docType == Lo.CALC) ||
(docType == Lo.DRAW) || (docType == Lo.IMPRESS)); }
Print.usePrinter() adds the printer's name and paper format to the XPrintable
properties by calling XPrintable.setPrinter() with an array of PrintDescriptor
properties (see Figure 1):
// in the Print.java class
public static void usePrinter(XPrintable xp, String printer)
{
if (xp == null) {
System.out.println("Cannot set printer XPrintable is null");
return;
}
System.out.println("Using printer \"" + printer + "\"");
xp.setPrinter( Props.makeProps("Name", printer,
"PaperFormat", PaperFormat.A4) );
setListener(xp);
}
Table 1 lists the PrintDescriptor properties, or use my lodoc.bat script to access the class' documentation online (type lodoc PrinterDescriptor
).
Property | Description |
---|---|
Name | Name of the printer. |
PaperOrientation | Paper orientation |
(com.sun.star.view.PaperOrientation: | |
PORTRAIT, LANDSCAPE) | |
PaperFormat | Paper size formats |
(com.sun.star.view.PaperFormat: A3, A4, | |
A5, B4, B5, LETTER, LEGAL, | |
TABLOID, USER) | |
PaperSize | Paper size in 100th mm |
(com.sun.star.awt.Size). | |
IsBusy | Is the printer busy? |
CanSetPaperOrientation | Can the paper orientation be set? |
CanSetPaperFormat | Are other paper formats supported? |
CanSetPaperSize | Are other paper sizes supported? |
Table 1. Properties in com.sun.star.view.PrinterDescriptor.
Print.setListener() inside Print.usePrinter() attaches an XPrintJobListener listener to the print job by casting XPrintable to XPrintJobBroadcaster (see Figure 1).
// in the Print class
public static void setListener(XPrintable xp)
{
if (xp == null) {
System.out.println("Cannot set listener; XPrintable is null");
return;
}
XPrintJobBroadcaster pb = Lo.qi(XPrintJobBroadcaster.class, xp);
if (pb == null) {
System.out.println("Cannot obtain print job broadcaster");
return;
}
pb.addPrintJobListener( new XPrintJobListener()
{
public void printJobEvent(PrintJobEvent e)
{ System.out.println("Print Job status: " +
printableState(e.State)); }
public void disposing(com.sun.star.lang.EventObject e)
{ System.out.println("Disposing of print job: " + e); }
});
} // end of setListener()
XPrintJobListener.printJobEvent() is called at different stages during the job's execution, and Print.printableState() converts the retrieved PrintableState constant into a more readable string:
// in the Print class
public static String printableState(PrintableState val)
{
if (val == PrintableState.JOB_STARTED )
return "JOB_STARTED";
else if (val == PrintableState.JOB_COMPLETED )
return "JOB_COMPLETED";
else if (val == PrintableState.JOB_SPOOLED )
return "JOB_SPOOLED";
else if (val == PrintableState.JOB_ABORTED )
return "JOB_ABORTED";
else if (val == PrintableState.JOB_FAILED )
return "JOB_FAILED";
else if (val == PrintableState.JOB_SPOOLING_FAILED )
return "JOB_SPOOLING_FAILED";
else {
System.out.println("Unknown printable state");
return "??";
}
} // end of printableState()
The PrintableState constants are summarized in Table 2.
Enumerator Constant | Description |
---|---|
JOB_STARTED | Printing/rendering has begun. |
JOB_COMPLETED | Printing/rendering has finished; spooling |
has begun. | |
JOB_SPOOLED | Spooling has finished successfully. This |
is the "success" state for a print job. | |
JOB_ABORTED | Printing was aborted (e.g., by the user) |
during printing or spooling. | |
JOB_FAILED | The printing ran into an error. |
JOB_SPOOLING_FAILED | The document could be printed but not |
spooled. |
Table 2. Properties in com.sun.star.view.PrintableState enum.
Back in the main() function of DocPrinter.java, Print.reportPrinterProps() outputs the printer's PrintDescriptor settings (listed in Table 1). It loops through the properties array returned by XPrintable.getPrinter():
// in the Print class
public static void reportPrinterProps(XPrintable xp)
{
if (xp == null) {
System.out.println("Cannot report printer props");
return;
}
PropertyValue[] printProps = xp.getPrinter();
if (printProps == null)
System.out.println("No Printer properties found");
else {
System.out.println("Printer properties:");
String name;
for (PropertyValue prop : printProps) {
name = prop.Name;
if (name.equals("PaperOrientation"))
System.out.println(" " + name + ": " +
paperOrientation((PaperOrientation)prop.Value));
else if (name.equals("PaperFormat"))
System.out.println(" " + name + ": " +
paperFormat((PaperFormat)prop.Value));
else if (name.equals("PaperSize")) {
Size sz = (Size)prop.Value;
System.out.println(" " + name + ": (" +
sz.Width + ", " + sz.Height + ")");
}
else
System.out.println(" " + name + ": " + prop.Value);
}
System.out.println();
}
} // end of reportPrinterProps()
The constants in the PaperOrientation and PaperFormat classes are mapped to more easily understood strings.
Typical output from Print.reportPrinterProps() is:
Printer properties: Name: FinePrint PaperOrientation: PORTRAIT PaperFormat: A4 PaperSize: (11906, 16838) IsBusy: false CanSetPaperOrientation: true CanSetPaperFormat: true CanSetPaperSize: true
At last it's time to print the document. As Figure 1 indicates, the call to XPrintable.print() is configured using PrintOptions properties, which are listed in Table 3.
Property | Description |
---|---|
CopyCount | Number of copies to print. |
FileName | Send the output to a file rather than to the |
printer. | |
Collate | Collate the printed pages (set to true or |
false). | |
Sort | Sort the pages (deprecated; use Collate) |
Pages | A string that specifies pages and page |
ranges to print. For example, "1-4;10" to | |
print the pages 1 to 4 and 10. | |
Wait | Wait for the print request to be |
completed. | |
DuplexMode | Set duplex constant (using |
com.sun.star.view.DuplexMode) | |
PrinterName | The name of the printer. |
Table 3. Properties in com.sun.star.view.PrintOptions.
The configuration and printing is carried out by Print.print():
// in the Print class
public static void print(XPrintable xp)
{ print(xp, "1-"); } // print all the pages
public static void print(XPrintable xp, String pagesStr)
{
if (xp == null) {
System.out.println("Cannot print; XPrintable is null");
return;
}
System.out.println("Print range: " + pagesStr);
System.out.println("Sending document...");
PropertyValue[] props =
Props.makeProps("Pages", pagesStr, "Wait", true);
// synchronous
// see com.sun.star.view.PrintOptions
xp.print(props); // print the document
System.out.println("Delivered");
} // end of print()
Two properties are set: "Pages" and "Wait". "Pages" could be left out if all of the document is to printed, but "Wait" is necessary; it forces the print() call to wait until the job has been delivered to the driver, and so has left the Office process. If "Wait" wasn't included then DocPrinter could continue and perhaps close Office before it had fully transmitted the document to the printer. This would cause a crash.
Note that there's no need to set the "PrinterName" property since its already been supplied as one of the PrintDescriptor properties.
4. Viewing a Document's Print Properties
The previous section looked at printer-related properties stored in PrinterDescriptor (Table 1) and PrintOptions (Table 2). But the real property mother lode are those related to documents, which are stored in the many subclasses below the Settings service (see Figure 3). The ShowPrintProps.java example prints out those properties for a given input document. The relevant lines are:
// in ShowPrintProps.java...
int docType = Info.reportDocType(doc);
XProperty Set docProps = Print.getDocSettings(docType);
Props.showProps("Document Settings", docProps);
The document type returned by Info.reportDocType() is employed by Print.getDocSettings() to decide which of the subclasses of the Settings service (once again, see Figure 3) should be instantiated:
// in the Print class
public static XPropertySet getDocSettings(int docType)
{
XPropertySet props = null;
if (docType == Lo.WRITER)
props = Lo.createInstanceMSF(XPropertySet.class,
"com.sun.star.text.DocumentSettings");
else if (docType == Lo.IMPRESS)
props = Lo.createInstanceMSF(XPropertySet.class,
"com.sun.star.presentation.DocumentSettings");
else if (docType == Lo.DRAW)
props = Lo.createInstanceMSF(XPropertySet.class,
"com.sun.star.drawing.DocumentSettings");
else if (docType == Lo.CALC)
props = Lo.createInstanceMSF(XPropertySet.class,
"com.sun.star.sheet.DocumentSettings");
else if (docType == Lo.BASE)
System.out.println("No document settings for a base doc");
else if (docType == Lo.MATH)
System.out.println("No document settings for a math doc");
else
System.out.println("Unknown document type");
return props;
} // end of getDocSettings()
All these setting services support XPropertySet, which is returned as Print.getDocSettings()'s result.
The printing-related properties defined in the Settings superclass are listed in Table 4, but it contains many more properties for different aspects of a document. For a full list, use lodoc Settings document
to look at the online documentation. The Settings webpage is also a good starting point for examining its DocumentSettings subclasses.
Property | Description |
---|---|
PrinterName | Printer used by the document. |
PrinterSetup | A byte array containing driver-specific |
data. | |
IsPrintBooklet | Put two document pages on one physical |
page, such that you can fold the output | |
into a booklet. | |
IsPrintBookletFront | Only print the fronts of a booklet |
IsPrintBookletBack | Only print the backs of a booklet |
PrintQuality | Normal (0); Grayscale (1), Black and |
white (2). | |
PrinterIndependentLayout | Forbid use of printer metrics for layout. |
EmbedFonts | Whether to embed fonts used by the |
document. | |
EmbedSystemFonts | Whether to embed system fonts used by |
the document. |
Table 4. Printing-related Properties in com.sun.star.document.Settings.
Writer documents utilize the DocumentSettings service in the text module which doesn't contain any printing properties, but is far from empty. Again, use lodoc DocumentSettings text
to visit its documentation in the text module. Instead, the print properties are inherited from the PrintSettings service in the text module, which are listed in Table 5. Use lodoc PrintSettings text
to visit its documentation.
Property | Description |
---|---|
PrintGraphics | If true, print graphic objects. |
PrintTables | If true, print text tables. |
PrintDrawings | If true, print shapes. |
PrintLeftPages | If true, print left pages. |
PrintRightPages | If true, print right pages. |
PrintControls | If true, then control shapes in the |
document (usually inside forms) are | |
printed. | |
PrintReversed | If true, print the pages in reverse order, |
starting with the last page. | |
PrintPaperFromSetup | If true, use the paper tray specified for |
the system printer. If false, use the paper | |
tray specified by the page style. | |
PrintFaxName | Name of the fax machine. |
PrintAnnotationMode | Specify how notes are printed. Uses |
com.sun.star.text.NotePrintMode: NOT, | |
ONLY, DOC_END, or PAGE_END). | |
PrintProspect | If true, prospect printing is used. Related |
to brochures (I think). | |
PrintPageBackground | Determines if the background color / |
background graphic of pages is printed. | |
PrintBlackFonts | Determines if characters are printed in |
black. | |
PrintEmptyPages | Determines if automatically inserted |
empty pages are printed. |
Table 5. Properties in com.sun.star.text.PrintSettings.
Back in ShowPrintProps.java, the properties are printed with Props.showProps(). The output is quite lengthy since every setting is printed, not just the printing-related ones in my tables. Typical output when a Writer file is examined includes:
: PrintAnnotationMode == 0 PrintBlackFonts == false PrintControls == true PrintDrawings == true PrintEmptyPages == false PrintFaxName == PrintGraphics == true PrintHiddenText == false PrintLeftPages == true PrintPageBackground == true PrintPaperFromSetup == false PrintProspect == false PrintProspectRTL == false PrintReversed == false PrintRightPages == true PrintSingleJobs == false PrintTables == true PrintTextPlaceholder == false PrinterIndependentLayout == 3 PrinterName == PrinterSetup == [B@1222c14 PropLineSpacingShrinksFirstLine == true ProtectForm == false RedlineProtectionKey == [B@6d8acf Rsid == 537223 :
A Calc file uses the DocumentSettings service in the sheet module (use lodoc DocumentSettings sheet
to examine its documentation). However, printing-related properties are stored elsewhere, in the GlobalSheetSettings and PageStyle services, which need additional code to access them as I'll explain later.
An Impress file uses DocumentSettings in the presentation module (see lodoc DocumentSettings service presentation
); its printing-related properties are given in Table 6.
Property | Description |
---|---|
IsPrintDrawing | Enables or disables the printing of the |
drawing pages. | |
IsPrintNotes | Enables or disables the printing of the |
notes pages. | |
IsPrintHandout | Enables or disables the printing of the |
handout pages. | |
IsPrintOutline | Enables or disables the printing of the |
outline pages. | |
IsPrintHiddenPages | Enables or disables the printing of draw |
pages that are marked as hidden. | |
IsPrintFitPage | Enables or disables the fitting of the page |
to the printable area during printing. | |
IsPrintTilePage | If this is true and the paper size for |
printing is larger than the paper size of | |
the printer than the content is tiled over | |
multiple pages. |
Table 6. Printing-related Properties in com.sun.star.presentation.DocumentSettings.
Draw documents are similar to presentations, and this is reflected in the DocumentSettings service in the drawing module (see lodoc DocumentSettings service drawing
) which offers a subset of the slide properties. The printing-related ones are listed in Table 7.
Property | Description |
---|---|
IsPrintFitPage | Enables or disables the fitting of the page |
to the printable area during printing. | |
IsPrintTilePage | If this is true and the paper size for |
printing is larger than the paper size of | |
the printer than the content is tiled over | |
multiple pages. |
Table 7. Printing-related Properties in com.sun.star.drawing.DocumentSettings.
The drawing and presentation DocumentSettings services inherit the Settings service, and a HeaderFooterSettings service in the documents folder (see lodoc HeaderFooterSettings
), which add the properties in Table 8.
Property | Description |
---|---|
IsPrintPageName | Enables or disables the printing of the |
page name in the header or footer. | |
IsPrintDate | Enables or disables the printing of the |
date in the header or footer. | |
IsPrintTime | Enables or disables the printing of the |
current time in the header or footer. |
Table 8. Properties in com.sun.star.document.HeaderFooterSettings.
4.1. Additional Writer Print Properties
As Figure 2 indicates, Writer Documents have access to an XPagePrintable interface offered by the GenericTextDocument service (see lodoc XPagePrintable
). It has a print method, XPagePrintable.printPages() which utilizes properties stored in the PagePrintSettings service (see Table 9). These relate to the document's margins and printing multiple pages on a single sheet.
Property | Description |
---|---|
PageRows | Number of page rows on each |
printed page. | |
PageColumns | Number of page columns on each |
printed page. | |
LeftMargin | Left margin of the printed page. |
RightMargin | Right margin. |
TopMargin | Top margin. |
BottomMargin | Bottom margin. |
HoriMargin | Margin between page rows. |
VertMargin | Margin between page columns. |
IsLandscape | Print in landscape format? |
Table 9. Properties in com.sun.star.text.PagePrintSettings.
These properties are printed by casting the document to XPagePrintable:
// part of ShowPrintProps.java...
if (docType == Lo.WRITER) {
XPagePrintable xpp = Lo.qi(XPagePrintable.class, doc);
PropertyValue[] printProps = xpp.getPagePrintSettings();
Props.showProps("Page print settings", printProps);
}
Typical output is:
Properties for "Page print settings": PageRows: 1 PageColumns: 1 LeftMargin: 0 RightMargin: 0 TopMargin: 0 BottomMargin: 0 HoriMargin: 0 VertMargin: 0 IsLandscape: false
4.2. Additional Calc Print Properties
Figure 3 indicates that a Calc document has three sources of printing-related properties: DocumentSettings in the sheet module, GlobalSheetSettings also in the sheet module, and PageStyle in the style module.
In a break with usual Office conventions, the GlobalSheetService service (see lodoc GlobalSheetSettings
) doesn't store the properties; instead they're maintained by its XGlobalSheetSettings interface (see lodoc XGlobalSheetSettings
). The printing- related properties are listed in Table 10.
Property | Description |
---|---|
PrintAllSheets | Specifies whether all sheets or only |
selected sheets are printed. | |
PrintEmptyPages | Specifies whether empty pages are |
printed. | |
UsePrinterMetrics | Specifies whether printer metrics are used |
for display. |
Table 10. Printing-related Properties in com.sun.star.sheet.XGlobalSheetSettings.
This organization of the documentation may be an error, since the XGlobalSheetSettings properties are accessed in the usual way, by instantiating the GlobalSheetSettings service, and casting it to XPropertySet:
XPropertySet globalSheetProps =
Lo.createInstanceMCF(XPropertySet.class,
"com.sun.star.sheet.GlobalSheetSettings");
Props.showProps("Global Sheet Settings", globalSheetProps);
GlobalSheetSettings properties are applied to all sheets, so you may want to reset a changed property to its original value after a print. There's an example of using global sheet settings in PrintSheet.java later in this chapter.
Another source of spreadsheet properties is the PageStyle service. I explained page styles in Chapter 22, so I refer you back there for the details. A page style is associated with a sheet, and so it's necessary to first choose a sheet to examine, and then access its "PageStyle" property:
XSpreadsheetDocument ssDoc = Calc.getSSDoc(doc);
XSpreadsheet sheet = Calc.getSheet(ssDoc, 0); // first sheet
String styleName = (String) Props.getProperty(sheet, "PageStyle");
System.out.println("\nPageStyle of first sheet: " + styleName);
The page style name is used to lookup the properties for that particular style:
XPropertySet props = Info.getStyleProps(doc, "PageStyles",
styleName);
Props.showProps(styleName + " PageStyles", props);
Props.showProps() prints an extensive list of properties, drawn form several services in the PageStyle hierarchy. A simplified version of that hierarchy is shown in Figure 4.
Figure 4. Part of the PageStyle Service Hierarchy.
I'm only interested in printing-related properties in this chapter. Some come from the PageProperties service in the style module (see lodoc PageProperties style
), as listed in Table 11.
Property | Description |
---|---|
Lots of border props… | |
Lots of margin props… | |
IsLandscape | Determines if the page format is |
landscape. | |
PrinterPaperTray | Contains the name of a paper |
tray of the selected printer. | |
Lots of header props… | |
Lots of footer props… | |
Lots of grid props… | |
GridPrint | Determines whether text grid |
lines are printed. |
Table 11. Printing-related Properties in com.sun.star.style.PageProperties.
Many more print properties come from TablePageStyle in the sheet module (see lodoc TablePageStyle
, which are given in Table 12.
Property | Description |
---|---|
PrintAnnotations | Enables printing of cell annotations. |
PrintGrid | Enables printing of the cell grid. |
PrintHeaders | Enables printing of column and row |
headers. | |
PrintCharts | Enables printing of charts. |
PrintObjects | Enables printing of embedded objects. |
PrintDrawing | Enables printing of drawing objects. |
PrintFormulas | Enables printing of formulas instead of their |
results. | |
PrintZeroValues | Enables printing of zero-values. |
PrintDownFirst | Specifies the print order for the pages within |
each sheet. | |
PageScale | Contains the scaling factor (in percent) for |
printing the sheet. | |
ScaleToPages | Contains the number of pages the sheet will |
use for printing. | |
ScaleToPagesX | Contains the number of horizontal pages the |
sheet will be printed on. | |
ScaleToPagesY | Contains the number of vertical pages the |
sheet will be printed on. | |
LeftPageHeaderContent | Contains the content of the header for the |
left pages | |
(com.sun.star.sheet.XHeaderFooterContent). | |
RightPageHeaderContent | Contains the content of the header for the |
right pages | |
(com.sun.star.sheet.XHeaderFooterContent). | |
LeftPageFooterContent | Contains the content of the footer for the left |
pages | |
(com.sun.star.sheet.XHeaderFooterContent). | |
RightPageFooterContent | Contains the content of the footer for the |
right pages | |
(com.sun.star.sheet.XHeaderFooterContent). |
Table 12. Printing-related Properties in com.sun.star.sheet.TablePageStyle.
5. Specialized Printing
In this section I'll use some of the document properties just described to control how Writer, Impress, and Calc documents are printed. The example code is in TextPrinter.java, ImpressPrinter.java, SheetPrinter.java, and PrintSheet.java.
The sad news is that although TextPrinter.java and ImpressPrinter.java successfully print their documents, the API ignores most of the changes to the properties. Perhaps even sadder (from a programming perspective) is the fact that these same documents are correctly printed if the properties are set via Office's GUI.
Aside from this, the print GUI in Office offers more functionality than the API. For example, it's possible to output multiple slides and sheets on a single page through the print dialog but not with API calls.
5.1. Printing a Text Document
TextPrinter.java sends a text document to a specified printer, with two pages printed on each sheet.
After the document has been loaded, the printing is carried out by:
// part of TextPrinter.java...
XPrintable xp = Lo.qi(XPrintable.class, doc);
Print.usePrinter(xp, pName);
XPagePrintable xpp = Lo.qi(XPagePrintable.class, doc);
PropertyValue[] props = xpp.getPagePrintSettings();
Props.setProp(props, "IsLandscape", true);
Props.setProp(props, "PageColumns", (short)2);
xpp.setPagePrintSettings(props);
xpp.printPages(new PropertyValue[1]); // empty array of props
To help understand what the code is doing, refer back to Figure 2. The document is cast to XPrintable so the printer settings can be initialized by Print.usePrinter(). Then the document is cast to XPagePrintable so its PagePrintSettings properties can be configured. That's done via XPagePrintable.setPagePrintSettings() and finally XPagePrintable.printPages() is called with an empty array.
The document is printed but unfortunately the two pages/sheet and landscape settings are ignored.
This bug has been known for many years, and some people have suggested an alternative implementation where the properties are passed to XPagePrintable.printPages() rather than to XPagePrintable.setPagePrintSettings().
The last two lines from above would be replaced by:
xpp.printPages(props);
Unfortunately this produces the same output in my tests.
If the Writer document is printed through the Office GUI, then the print dialog looks something like Figure 5.
Figure 5. The Writer application's Print Dialog.
The pages/sheet settings are on the "Page Layout" tab, as in Figure 6.
Figure 6. The "Page Layout" Tabbed Window in the Print Dialog.
When "Ok" is pressed, the document is printed correctly.
5.2. Printing an Impress Document
ImpressPrinter.java prints an Impress document to the specified printer in handout format, six slides per sheet, in black and white. A4 paper in landscape mode is utilized.
After the document has been loaded, the printing is carried out by the following code:
// part of ImpressPrinter.java...
// set document props
XPropertySet props = Lo.createInstanceMSF(XPropertySet.class,
"com.sun.star.presentation.DocumentSettings");
Props.setProperties(props,
new String[] {"IsPrintHandout","SlidesPerHandout","IsPrintFitPage",
"IsPrintDate", "PrintQuality", "PrinterName" },
new Object[] { true, (short)6, true, true, 2, pName} // 2 == B&W
);
Props.showProps("Document Settings", props);
// set printer props
XPrintable xp = Lo.qi(XPrintable.class, doc);
Print.usePrinter(xp, pName);
xp.setPrinter( Props.makeProps(
"PaperOrientation", PaperOrientation.LANDSCAPE,
"PaperFormat", PaperFormat.A4) );
Print.reportPrinterProps(xp);
Print.print(xp);
To help understand what the code is doing, refer back to Figure 2. A presentation DocumentSettings service is created, and its printing properties set. The "PrintQuality" and "PrinterName" properties are from the Settings service (see Table 4), "IsPrintHandout" and "IsPrintFitPage" are from DocumentSettings (see table 6), and "IsPrintDate" from HeaderFooterSettings (see Table 8). This leaves the "SlidesPerHandout" property, which isn't documented anywhere. I discovered it by looking at the print-out of that service's properties with:
Props.showProps("Document Settings", props);
The printer properties are set as in earlier examples, but with the addition of "PaperOrientation" and "PaperFormat" which are PrintDescriptor properties (see table 1).
The document is printed but as one color slide per sheet; the handout and black and white settings are ignored.
If the same Impress document is printed through the Office GUI, then the print dialog looks like Figure 7.
Figure 7. The Impress application's Print Dialog.
This is much closer to what I want to output, although I couldn't print the handouts in landscape mode.
5.3. Printing a Calc Document
SheetPrinter.java prints the first sheet of a Calc document, scaled so that two pages are used in the vertical direction. Also, the paper is set to landscape mode and A4 size, and the header's central field is modified. In addition, commented-out code restricts the printed area of the sheet to be a single column.
The good news is that all of these setting are correctly processed at print-time.
After the document has been loaded, the printing is carried out by:
// part of SheetPrinter.java...
XSpreadsheet sheet = Calc.getSheet(doc, 0); // first sheet
String styleName = (String) Props.getProperty(sheet, "PageStyle");
// get the properties set for the sheet's page style
XPropertySet props =
Info.getStyleProps(doc, "PageStyles", styleName);
Props.setProperty(props, "ScaleToPagesY", (short)2);
// use a max of 2 pages on the y-axis
Props.showProps(styleName, props);
showTotalsHeader(props);
/*
// print only the "E" column
XPrintAreas printAreas = Lo.qi(XPrintAreas.class, sheet);
printAreas.setPrintAreas(new CellRangeAddress[] {});
// reset print areas
CellRangeAddress addr = Calc.getAddress(sheet, "E1:E111");
printAreas.setPrintAreas(new CellRangeAddress[]{ addr });
// set area
*/
// set printer settings
XPrintable xp = Lo.qi(XPrintable.class, doc);
Print.usePrinter(xp, pName);
PropertyValue[] printProps =
Props.makeProps("PaperOrientation", PaperOrientation.LANDSCAPE,
"PageFormat", PaperFormat.A4);
xp.setPrinter(printProps);
Print.reportPrinterProps(xp);
Print.print(xp);
The scaling is done by accessing the sheet's page style properties, and setting the "ScaleToPagesY" value. This is one of the TablePageStyle properties listed in Table 12.
The headers and footers are manipulated by showTotalsHeader():
// in SheetPrinter.java
private static void showTotalsHeader(XPropertySet props)
// change the header of the sheet
{
// get the right-hand header and footer
XHeaderFooterContent header =
Calc.getHeadFoot(props, "RightPageHeaderContent");
XHeaderFooterContent footer =
Calc.getHeadFoot(props, "RightPageFooterContent");
// print details about them
Calc.printHeadFoot("Right Header", header);
Calc.printHeadFoot("Right Footer", footer);
// modify the header center text to be "Totals"
Calc.setHeadFoot(header, Calc.HF_CENTER, "Totals");
// turn on headers and make left and right page headers the same
Props.setProperty(props, "HeaderIsOn", true);
Props.setProperty(props, "HeaderIsShared", true);
// from style.PageProperties
Props.setProperty(props, "RightPageHeaderContent", header);
} // end of showTotalsHeader()
A sheet may have two headers (one for the left hand page, one for the right) and two footers, which are accessed as XHeaderFooterContent objects (see lodoc XHeaderFooterContent
). XHeaderFooterContent is a collection of three XText instances representing the left, center, and right of the content area.
The headers and footers are accessed via the sheet's page style properties, stored as the properties "LeftPageHeaderContent", "RightPageHeaderContent", "LeftPageFooterContent", and "RightPageFooterContent" in the TablePageStyle service (see Table 12).
Calc.getHeadFoot() retrieves the page style properties, and returns the requested XHeaderFooterContent:
// in the Calc class
public static XHeaderFooterContent getHeadFoot(XPropertySet props,
String content)
{ return Lo.qi( XHeaderFooterContent.class,
Props.getProperty(props, content)); }
Calc.printHeadFoot() prints the three text fields inside the XHeaderFooterContent object:
// in the Calc class
public static void printHeadFoot(String title,
XHeaderFooterContent hfc)
{ XText left = hfc.getLeftText();
XText center = hfc.getCenterText();
XText right = hfc.getRightText();
System.out.println(title + ": \"" + left.getString() + "\" : \"" +
center.getString() + "\" : \"" +
right.getString() + "\"");
} // end of showHeadFoot()
Calc.setHeadFoot() can change the text in the left, center, or right of a XHeaderFooterContent instance:
// in the Calc class
public static void setHeadFoot(XHeaderFooterContent hfc,
int region, String text)
{ XText xText = getRegion(hfc, region);
if (xText == null) {
System.out.println("Could not set text");
return;
}
XTextCursor headerCursor = xText.createTextCursor();
headerCursor.gotoStart(false);
headerCursor.gotoEnd(true);
headerCursor.setString(text);
} // end of setHeadFoot()
public static XText getRegion(XHeaderFooterContent hfc, int region)
{
if (hfc == null) {
System.out.println("Header/footer content is null");
return null;
}
if (region == HF_LEFT)
return hfc.getLeftText();
else if (region == HF_CENTER)
return hfc.getCenterText();
else if (region == HF_RIGHT)
return hfc.getRightText();
else {
System.out.println("Unknown header/footer region");
return null;
}
} // end of getRegion()
Back in SheetPrinter.java, showTotalsHeader() changes the central text field of the right header to "Totals". It then switches on the display of headers, makes the left and right headers the same, and inserts the modified XHeaderFooterContent instance back into the page styles:
// part of showTotalsHeader() in SheetPrinter.java...
Props.setProperty(props, "HeaderIsOn", true);
Props.setProperty(props, "HeaderIsShared", true);
// from style.PageProperties
Props.setProperty(props, "RightPageHeaderContent", header);
Print Areas
A sheet is usually manipulated via the XSpreadsheet interface in the Spreadsheet service. However, Spreadsheet supports a number of other interfaces, including XPrintAreas for specifying printable areas within a sheet. This interface is illustrated in Figure 8.
Figure 8. The Spreadsheet XPrintAreas Interface.
Aside from defining printable areas, XPrintAreas also has methods for setting which rows and/or columns are repeated if a sheet spans several pages.
The commented out code in SheetPrinter.java does two things: it cancels any existing print areas, and specifies that only the "E" column will be printed:
// part of SheetPrinter.java...
XPrintAreas printAreas = Lo.qi(XPrintAreas.class, sheet);
printAreas.setPrintAreas(new CellRangeAddress[] {});
// cancel print areas
CellRangeAddress addr = Calc.getAddress(sheet, "E1:E111");
printAreas.setPrintAreas(new CellRangeAddress[]{ addr });
// set area to be the "E" column
5.4. Printing a Single Sheet
My thanks to user "villeroy" on the OpenOffice forums for suggesting this example: how is a single sheet of a multi-sheet spreadsheet printed?
One part of the solution is to set the "PrintAllSheets" property to false in the GlobalSheetSettings service (see Table 10). The other is to make the current sheet explicitly active in the XSpreadsheetView interface, which is equivalent to bringing the sheet to the foreground in the Calc application.
The PrintSheet.java example shows how to do this for a spreadsheet of three sheets, where only "Sheet2" is printed:
// in PrintSheet.java
public static void main(String args[])
{
// hardwired spreadsheet and printer choice
String fnm = "tables.ods";
String pName = "FinePrint";
// load the spreadsheet
XComponentLoader loader = Lo.loadOffice();
XComponent cDoc = Lo.openReadOnlyDoc(fnm, loader);
XSpreadsheetDocument doc = Calc.getSSDoc(cDoc);
if (doc == null) {
System.out.println("Could not open " + fnm);
Lo.closeOffice();
return;
}
// what are the sheets called?
String[] sheetNms = Calc.getSheetNames(doc);
System.out.println("Names of sheets (" + sheetNms.length + "):");
for(String sheetNm : sheetNms)
System.out.println(" " + sheetNm);
// make "Sheet2" active
XSpreadsheet sheet = Calc.getSheet(doc, "Sheet2");
Calc.setActiveSheet(doc, sheet);
// set Global Sheet settings
// changes are remembered
XPropertySet gsProps =
Lo.createInstanceMCF(XPropertySet.class,
"com.sun.star.sheet.GlobalSheetSettings");
Props.setProperty(gsProps, "PrintAllSheets", false);
System.out.println();
Props.showProps("Global Sheet Settings", gsProps);
// set printer settings
XPrintable xp = Lo.qi(XPrintable.class, doc);
Print.usePrinter(xp, pName);
PropertyValue[] printProps =
Props.makeProps("PaperOrientation", PaperOrientation.LANDSCAPE,
"PageFormat", PaperFormat.A4);
xp.setPrinter(printProps);
Print.print(xp);
// reset global settings
Props.setProperty(gsProps, "PrintAllSheets", true);
Lo.closeDoc(doc);
Lo.closeOffice();
} // end of main()
One issue with changing GlobalSheet settings is that they are remembered after Office has closed, and will be used by default during future prints. In that case, it's a good idea to reset the "PrintAllSheets" to true after the Print.print() call.
6. A Bad Idea: Hacking the GUI
A recurring observation of the last few sections is that printer dialogs inside Office are less buggy and more feature-rich than the API. In previous chapters, I've sometimes resorted to using Office's GUI by sending dispatch messages, and key presses to the window. I decided not to follow that approach here since it's too unreliable.
Getting to the print dialog is straightforward. The document window is made visible, and an ".uno:Print" dispatch opens the print dialog:
GUI.setVisible(doc, true); Lo.delay(1000); // give the Office window time to appear Lo.dispatchCmd("Print"); Lo.delay(500); // give the Print dialog time to appear
The first problem is that the print dialog uses tabbed windows, and the window that's foremost can vary. Usually it's the "General" window (e.g. see Figure 5), but if the user has previously clicked the "Ok" button in another tabbed window (such as the "Page Layout" window in Figure 6) then that window will be foremost when the print dialog is next invoked. This means that my code cannot be sure which tabbed window is active after the "Print" dispatch.
There's also the issue of moving around the tabbed windows and their fields. This can be implemented using Java's Robot class to send TAB and CTRL-TAB characters, as in:
private static void ctrlTab()
// send a CTRL-TAB character combination
{ try {
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_CONTROL);
robot.keyPress(KeyEvent.VK_TAB);
robot.delay(100);
robot.keyRelease(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_CONTROL);
}
catch(AWTException e)
{ System.out.println(e); }
} // end of ctrlTab()
The difficulty is knowing how many TABs and CTRL-TABs are needed, which depends on the tab ordering of each window.
Even if my code somehow managed to get to the correct field in the correct tabbed window, there's still the problem of entering data and 'pressing' the "Ok" button afterwards.
On balance, I decided not to bother implementing this 'solution'.
7. Command Prompt Printing
Another approach to printing documents, which avoids the use of Java, is to utilize command line utilities.
There's no shortage of command line printing tools in Linux and the Mac OS, based around the CUPS printing system ( https://cups.org/), and plenty of sites explaining how to use lp, lpq, lpstat and others (e.g. https://computerhope.com/unix/ulp.htm, https://eecs.utk.edu/resources/it/kb/printing/linux-command-line/).
Perhaps less well known are the printing utilities in Windows, listed in Table 13.
VBScript | Purpose |
---|---|
Prnmngr.vbs | Printer management. Adds, deletes, and |
lists printers or printer connections, in | |
addition to setting and displaying the | |
default printer. | |
Used by printersList.bat | |
Prncnfg.vbs | Configures or displays configuration |
information about a printer. | |
Used by printerStatus.bat | |
Prnjobs.vbs | Pauses, resumes, cancels, and lists print |
jobs. | |
Used by printerJobs.bat | |
Prnqctl.vbs | Printer queue management. Prints a test |
page, pauses or resumes a printer, and | |
clears a printer queue. | |
Used by printerClean.bat | |
Prndrvr.vbs | Adds, deletes, and lists printer drivers. |
Prnport.vbs | Printer port management. |
Pubprn.vbs | Publish a printer to Active Directory. |
Table 13. Windows 7's VBScript Printing Utilities.
The utilities are Visual Basic scripts, located in the C:_Admin_Scripts-US folder which isn't a standard part of Window's PATH environment variable.
One way of finding documentation on these tools is by starting "Windows Help" and then following the links to the commands reference section. This will eventually take you to Microsoft's TechNet website. A slightly easier alternative is the TechRepublic article "How to take advantage of the hidden VBScript print utilities in Windows 10" by Greg Shultz at https://techrepublic.com/article/how-to-take-advantage-of-the-hidden-vbscript-print-utilities-in-windows-10/.
In my "Printing Tests" examples folder, there are four Batch files (printersList.bat, printerStatus.bat, printerJobs.bat, and printerClean.bat) which use these VBScripts in simple ways.
printersList.bat lists the names of all the print services visible to Windows, and also the name of the default printer. For example:
> printersList Printer name Send To OneNote 2010 Printer name Ricoh Aficio MP 2000 PCL(Black Office Room) Printer name PrinterShare Printer name Print to Evernote Printer name Microsoft XPS Document Writer Printer name HPLJM806 (HP LaserJet M806) Printer name HP LaserJet9040DN PCL 6(Office) Printer name HP LaserJet M806 PCL 6 Printer name HP LaserJet 1200 Series PCL 6 Printer name Foxit PhantomPDF Printer Printer name FinePrint Printer name Fax The default printer is FinePrint
These names can be used as printer name arguments in my earlier Java programs.
printerStatus.bat reports the status of a printer:
> printerStatus "HP LaserJet 1200 Series PCL 6" Printer status Idle Extended printer status Unknown
printerJobs.bat lists all the print jobs currently on a printer's queue:
> printerJobs "HP LaserJet 1200 Series PCL 6" Number of print jobs enumerated 0
printerClean.bat deletes all the jobs on a printer queue:
> printerClean "HP LaserJet 1200 Series PCL 6" Microsoft (R) Windows Script Host Version 5.8 Copyright (C) Microsoft Corporation. All rights reserved.
Success Purge Printer HP LaserJet 1200 Series PCL 6
Notably missing from the VBScripts is a way to print a document, perhaps because Windows already has an awesome "print" command:
> print /? Prints a text file. PRINT [/D:device] [[drive:][path]filename[...]] /D:device Specifies a print device.
An alternative is "printui" which starts Window's printui.dll (https://technet.microsoft.com/en-us/library/ee624057(v=ws.11).aspx).
However, I'd not recommend "print" or "printui" since Office is a much better command line printing tool.
If you open a command prompt in the Office directory (e.g. in C: Files), you can get a list of its command line options by typing:
> soffice.exe –h
Figure 9 shows the top half of a long window.
Figure 9. Some of Office Command Line Arguments.
Another information source is the "Starting the LibreOffice Software With Parameters" webpage at https://help.libreoffice.org/Common/Starting_the_Software_With_Parameters.
The relevant parameters for printing are "-p" and "–pt", which can be seen near the bottom of Figure 9. "-p" prints a file to the default printer, while "–pt" (note the two '- 's) sends the document to the named printer. A suitable name can be obtained from the printersList.bat script.
I've utilized these parameters in a loprint.bat script in the "Printing Tests" folder. If loprint is called with no arguments, then Figure 9's help window is displayed. If a filename argument is supplied then the document is sent to the default printer using the '-p' parameter. Two arguments are assumed to be a printer name and filename, and are processed by "–pt". For example:
> loprint "HP LaserJet 1200 Series PCL 6" cover.odg
prints the cover.odg Draw file to the HP laserjet.
Topics: The Java Print Service (JPS); Printing in Office Writer, Impress, Calc; Viewing a Document's Print Properties; Specialized Printing: Writer, Impress, Calc; Hacking the GUI; Command Prompt Printing
Example folders: printing
and Utils