The next big step for NeoOffice/J is implementing printing and I have been thinking about how this could be done without having to reimplement any of the drawing code in the VCLGraphics Java class. After all, the cool thing about Java is that the same class - java.awt.Graphics2D - is used for drawing to a window, an off-screen buffer, and a printer.
So, here is the approach I was thinking of using to implement printing in NeoJ. If anyone has any ideas for simplifying the effort, please post a reply.
To implement printing in NeoJ, all of the SalPrinter methods in the neojava/vcl/java/source/app and neojava/vcl/java/source/gdi directories need to be implemented. Right now, these methods do nothing or return dummy values.
It appears to me that the SalPrinter methods fall into 3 groups: querying printer options, selecting printer options, and drawing with the selected printer options. My proposed approach for each of these 3 groups is described below:
Querying printer options - These methods are used to construct a list of printers and a list of valid choices for a printer's options (e.g. paper size, orientation, paper tray, etc.). The easiest way to implement these query methods would be to use Java 1.4.1's new javax.print.* classes. However, I don't recommend this approach primary because many people can't or won't upgrade from Java 1.3.1 to Java 1.4.1 as Java 1.4.1 causes some of their Java programs to break. So, I propose that we implement these query methods using the NSPrinter and NSPrintInfo Java classes. These Java classes are thin wrappers around the Cocoa APIs and are supported by both Java versions on Mac OS X. Selecting printer options - These methods set the current printer and the options that will be used for the current print job. I propose that we implement these methods by feeding these settings to java.awt.JobAttributes and java.awt.PageAttributes instances and then creating a java.awt.PrintJob instance by passing these job and page attributes as parameters of the java.awt.Toolkit.getPrintJob() method. Interestingly, the JobAttributes instance has methods to not only set the printer and its options, but it also has a method to suppress the display of the annoying print dialog that normally appears when Toolkit.getPrintJob() is invoked. Drawing with the selected printer options - These methods are used to obtain a graphics object that can be drawn to for the current page. These methods also include printer control functions such as forcing the printer to move to the next page and ending the print job. Surprisingly, this should be the simplest piece to implement. This is because the java.awt.PrintJob instance that we create has a method to retrieve a java.awt.Graphics2D object for the current page. In theory, we should be able to get the Graphics2D object and construct a SalGraphics instance with it just like we currently do for windows and off-screen buffers. The only change to the SalGraphics class is that we need to disable XOR operations and disable any methods that require copying of pixels already drawn when the SalGraphics is a printer. These same methods are disabled in the X11 implementation of VCL.
Sorry that the approach is a bit light on detail, but hopefully it helps to get things started.
I want to add one additional reason why I don't recommend that we use the Java 1.4.1 javax.print.* classes to implement querying of the printers and their options. I found that the javax.print.* classes don't work on Mac OS X. Here's a link to Apple's Java 1.4.1 release note:
Joined: May 25, 2003 Posts: 4752 Location: Santa Barbara, CA
Posted: Thu Jul 03, 2003 9:01 pm Post subject:
This sounds like a pretty reasonable approach. The similarity between Cocoa and this style of approach is interesting...similar to Java 2D the standard Print mechanism for a Cocoa app is to issue print requests to an NSView.
I think the split of using the CocoaJava stuff for printer feature querying and Java2D for rendering will be needed...I think the Cocoa printing from Java would only work if the CocoaJava drawing primitives were used. Still, the overlap between the two could be very helpful in exchanging general approaches and concepts
I did some testing of my proposed approach this weekend and found some problems. Specifically, the com.apple.cocoa.* Java classes have the following problems:
The NSPrinter class does not return the correct list of printers. It returns the printers in your NetInfo database, not the printers in PrintCenter.
[list]You cannot use the NSAutoreleasePool class as invoking its push() method causes and UnsatisfiedLinkError to be thrown[/link]
From the above, my sense is that the com.apple.cocoa.* Java classes may not be that reliable.
The reason that I am proposing to use these APIs is because these APIs are in their own subframework called PrintCore. The advantage of the PrintCore framework is that it is not linked to either the Cocoa or Carbon frameworks.
For those not familiar with this problem, linking OOo against any frameworks that link to Cocoa or Carbon will cause the NeoOffice/J Java code to have severe focus problems (e.g. clicking on a window will not bring it into the foreground and some mouse events never get delivered to a window).
There is one glitch to using the PrintCore framework: you cannot link directly against the PrintCore framework since it is a subframework of the ApplicationServices framework so we cannot using "-framework PrintCore" to link against it. Instead, we will need to use the more tedious approach of dynamically loading the framework file (like OOo does in stoc/source/javavm).
Does anyone have any thoughts on this new approach?
Joined: May 25, 2003 Posts: 4752 Location: Santa Barbara, CA
Posted: Mon Jul 21, 2003 9:59 pm Post subject:
Fuck. That honks. I'm miffed that the CocoaJava shite doesn't seem to work. I'd file a bug report with apple. For non-paying ADC members, it may get answered in about 6 months
Is this error in Java 1.4.1 DP102? I can test if given instructions.
If not, well, Carbon printing for querying printer abilities is fine by me...I'm a Carbon expert meself I can help flesh out details NP. Neo FY's printing is built around Carbon printing. I haven't figured out the Cocoa framework yet, which is one reason why Neo IG doesn't have printing.
If we have to dynamically load the library, the only other thing that comes to mind is that we might (this is a longshot...mmm, dos equis) be able to recycle some of the CUPS stuff from OS X 1.0.3 or some of the gnomecups printer stuff from 1.0.3 Ximian Desktop edition. The basic problem is printer location and feature querying, correct? From what I can gather browsing java2d style code the actual semantics of the printing itself once we can get the appropriate printer name, settings, margins, etc., should be a no brainer, yes?
The error is in not specific to any Java version as far as I can tell. It is really a bug in the implementation of the custom Java-to-Cocoa bridge classes in /System/Library/Java directory.
I thought of using CUPS, but CUPS is only available on 10.2 and higher. I hate to admit it, but I thought that I'd try to see how far I can go with 10.1 and Java 1.3.1. My thinking here is that if I use these, other platforms have a good chance at porting this stuff if such an interest arises as many platforms tend to lag behind Sun and Apple in their JVM versions.
The good news is that the use of Carbon printing is only necessary for querying the printer names and their capabilities. Creation of the actual printer graphics contexts and drawing to those graphics contexts will still be in Java using java.awt.Toolkit.getDefaultToolkit().getPrintJob() and java.awt.PrintJob.getGraphics(). We just need all of the Carbon stuff to populate the Properties object that is passed to the Toolkit.getDefaultToolkit().getPrintJob() method. After that, we should be able to construct a SalGraphics with the java.awt.Graphics instance that PrintJob.getGraphics() returns since it is a valid graphics context just like a window or off-screen graphics context. In fact, as far as the SalGraphics class is concerned, a printer Graphics instance is identical to an off-screen Graphics instance.
The only changes that may be needed to SalGraphics is that when the Graphics is applicable to a printer, we would not do any XORing, inverting, or other operations that depend require access to the pixels already painted. OOo seems to disable these behaviors for X11 and it makes sense as a printer Graphics is unbuffered.
Maybe later this week I can put in a new struct in neojava/vcl/java/inc/salprn.h and an initialization function in neojava/vcl/java/source/app/salinst.cxx that loads the PrintCore library, mallocs the new struct, puts a few of the PrintCore function pointers in the struct, and stores the malloc'd struct in a data member in neojava/vcl/java/inc/saldata.hxx that all of the printing methods in neojava/vcl/java/source/app/salinst.cxx and neojava/vcl/java/source/gdi/salprn.cxx can access.
Of course, I will use #ifdef MACOSX for this code since it is not portable to other platforms.
First the bad news. Before I started putting all that PrintCore code in, I thought that I would write a simple Java problem to verified that the java.awt.* printing APIs work as expected.
Unfortunately, the java.awt.Toolkit.getPrintJob() method has some problems. Even worse, the problems are different on Java 1.3.1 and 1.4.1! Here are the most obvious problems that I found:
1. Java 1.3.1 - Toolkit.getPrintJob() ignores any java.awt.JobAttributes or java.awt.PageAttributes that you pass in. I decompiled the JVM's classes and found that the code is hardcoded to ignore these two parameters and to always display the native print dialog.
2. Java 1.4.1 - Toolkit.getPrintJob() works, but you cannot cast the java.awt.Graphics that the PrintJob.getGraphics() returns to java.awt.Graphics2D. Without this casting, you cannot using of the Java2D text and glyph layout methods.
OK, so what now? The whole point of using the Java printing APIs is to be able to use all of the Java-based SalGraphics code. Without the Java printing APIs, the whole point of using Java is moot.
Here's the good news. Fortunately, there is still one option left. Basically, if we suppress the OOo print dialog windows and allow Java to display its native print dialogs, we can use the java.awt.print.PrinterJob class to print. I verified that using the java.awt.print.PrinterJob class works on both Java 1.3.1 and 1.4.1. A sample Java class that uses the java.awt.print.PrinterJob class is below:
public static void main(String[] args) {
PrinterJob job = PrinterJob.getPrinterJob();
if (job != null) {
// Ask the user to select the page format
job.setPrintable(new Test(), job.pageDialog(job.defaultPage()));
// Ask the user to select a printer or file to print to
if (job.printDialog()) {
try {
// Iterate through each page. This call sends a Graphics2D
// instance to the Test.print() method until that method
// returns Printable.NO_SUCH_PAGE or throws a
// PrinterException
job.print();
}
catch (PrinterException pe) {
pe.printStackTrace();
}
}
}
System.exit(0);
}
public int print(Graphics graphics, PageFormat p, int i) {
// Only print one page
if (i > 0)
return Printable.NO_SUCH_PAGE;
// Print some sample text. Note that we don't implement any of the
// page formatting yet. What we really need to do here is to
// construct a SalGraphics C++ object with this Graphics2D instance
// and invoke Object.wait() to block this method so that the VCL
// C++ code can access the SalGraphics instance when it invokes the
// SalPrinter::StartPage() method. Then, when the VCL C++ code
// invokes SalPrinter::EndPage(), we can invoke Object.notifyAll()
// to unblock this method and return.
Graphics2D g = (Graphics2D)graphics;
Font font = g.getFont();
FontRenderContext pfrc = g.getFontRenderContext();
TextLayout playout = new TextLayout("This is a test string", font, pfrc);
playout.draw(g, 100, 100);
return Printable.PAGE_EXISTS;
}
}
This approach has the advantage that we can create a SalGraphics instance for a printer page like I had originally planned (i.e. construct a VCLGraphics Java object like we do for window and off-screen graphics contexts but just disable the methods that access the graphic contexts buffer since there is no buffer in a printer graphics).
Another advantage with this approach is that it will require very little, if any, porting to any other platform with a 1.2 or higher JVM since we no longer need to access native printing functions.
The big disadvantage is that we have to make many of the SalPrinter methods in neojava/vcl/java return dummy values since selection of the printer done as part of the print job creation which is much later than the existing VCL code expects. Also, we will have to suppress the display of the OOo printer dialog in the svtools module.
Since I have already had to rework the approach a couple of times already due to the Java limitations that I have found, I think the safest approach is for me to hack in enough dummy values in the SalPrinter class so that I can get NeoOffice/J to execute the SalPrinter StartPage() and EndPage() methods. Only when I can get some simple Java printing to work in these two methods will I feel confortable that this approach is feasible.
I have been thinking about how to implement the latest printing approach that I outlined in my last post. Since I have been busy doing paid work and working on complex text layout, I haven't started on printing.
As such, I thought it might help if I list out the steps that I think would get us from our current state (no printing) to something that actually prints using Java's printing dialogs.
Here are the steps I envisioned, if anyone is interested in starting on the first couple, please post here so that we don't duplicate work:
1. Source neojava/build/MacosxEnvJava.Set and rebuild neojava/vcl in debug mode by executing
Code:
build -u debug=true
and copy the neojava/vcl/unxmacxp.pro/lib/libvcl641mxp.dylib file into an existing NeoJ installation.
, select the File -> Print menu item, and close NeoJ. You should see debug statements like "SalInstance::GetDefaultPrinter not implemented". Make a note of these as you will need them for the next step.
3. Replace the debug statements with dummy implementations for each method that prints out in step 2. When I say "dummy implementations", I mean return something generic. The purpose here is to have these methods fool the rest of OOo into thinking that there is only 1 printer with only one set of settings in NeoJ. If a method returns a list of options, only return 1 option.
4. Repeat steps 1 to 3 until you are able to get the OOo print dialog to appear with all of your dummy values. Note that after you have done step 1 once, you can rebuild with build debug=true (no -u) to speed up rebuilds.
5. Once we get to this step, check in the code. I know it will seem incomplete, but it wil be needed to get to the next step.
6. Make the SalPrinter::Job() method create a java.awt.print.PrinterJob instance and invoke the PrinterJob.printDialog() and PrinterJob.pageDialog() methods.
7. Make the SalPrinter::StartPage() method invoke the PrinterJob.print() method. This step requires that we write a Java class that implements the java.awt.print.Printable interface. Our Printable implementation should not do any printing in its print() method. Instead, it should take the print() method's Graphics2D and PageFormat parameters, construct a com.sun.star.vcl.VCLGraphics instance and wrap that VCLGraphics, and then do an Object.wait() call so that the print() method blocks. Then, we create a startPage() method in our Printable that returns the VCLGraphics instance. SalPrinter::StartPage() then wraps the VCLGraphics instance in a SalGraphics instance. Lastly, make the SalPrinter::EndPage() method invoke an endPage() method in our Printable. The endPage() method in our Printable invokes Object.notifyAll() to unblock the Printable.print() method so that the printer prints what has been drawn to the Graphics2D.
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You cannot download files in this forum