Return-Path: Delivered-To: apmail-xml-batik-users-archive@xml.apache.org Received: (qmail 22687 invoked by uid 500); 3 Mar 2003 12:13:38 -0000 Mailing-List: contact batik-users-help@xml.apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: list-post: Reply-To: "Batik Users" Delivered-To: mailing list batik-users@xml.apache.org Received: (qmail 22676 invoked from network); 3 Mar 2003 12:13:37 -0000 Message-ID: <3E634505.E6B43148@oracle.com> Date: Mon, 03 Mar 2003 13:05:25 +0100 From: Jakob Ilves Organization: Oracle Corporation X-Mailer: Mozilla 4.75 [en] (Windows NT 5.0; U) X-Accept-Language: en MIME-Version: 1.0 To: Batik Users Subject: Hype, issues and questions regarding Squiggle printing SVG graphics. Content-Type: multipart/mixed; boundary="------------E28253806CDF91F8E0CC4C75" X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N --------------E28253806CDF91F8E0CC4C75 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Hello! Ok, this is a long mail, It's a bad habit I have :-). MY BACKGROUND For several reasons, SVG caught my interest as the basis for describing graphics as they were printed on paper. If stuff looked nice on screen, I would course appreciate it, but it was accurate, high resolution printouts I primarly had in mind. As the I read the SVG 1.0 spec, I realized that the standard provideded excellend control over distances and metrics on printed media as well as printing various graphic primitives at the *printer's* resolution. And also, it supported ECMAscript (which I figured out to pretty much be the same as JavaScript) which provided a lot of intresting possibilities (such as describing the image by providing code which is executed and arranges the SVG graphics upon display upon displaying the document in the Batik browser). So, when looking for an SVG agent I had two features I found important: ECMAscript support and good printouts at the printers resolution. (Features like code stability, managebility etc were of course also important ;-). As SVG is a pretty new standard, I was prepared to find varying support for it, to say the least. HYPE After trying around with different agents (and being dissappointed), the Batik Squiggle was a pleasant surprise. When it comes to printing, it's "almost there" and even better, the pieces missing are pretty easily fixed. "Hard to implement stuff" like printing vectored graphics is already in place and works! On a printout done at 600dpi tiny features appears clear and distinct even if those would not be visible on the computer screen. It doesn't first raster the images on screen and then dump out that rastered bitmap (such as ImageMagick or the Adobe SVG plugin do in MSIE), it sends vectors to the printer directly. Scripting seem to work as well (even if I've not "torture tested" that feature :-). (And yes, I've not yet tried out to use system fonts which some messages in the archive hint can have issues in printouts, but that isn't critical to me). The only issue was the scale, but after some (ugly) hardcoding in the Squiggly source I managed to get SVG docs with a viewport expressed in pixels to print out at a scale of 96 screen pixels per printed media inch (which is exactly what I wanted). All in all, it's VERY promising! So, to you people who develops batik and Squiggle I just want to say this... ...KEEP UP THE EXCELLENT WORK!!! Ok, that was the well deserved hype. Now something different... ISSUES and QUESTIONS: Squiggle currently always autoscales graphics to fit the printer page. I would highly appreciate if that was made optional, so users like me who want's unscaled printouts can get what we want. When adding that feature, it can be worth providing an option where the user can specify how big a "pixel" should be on the printer in the case graphics are expressed in "pixels" as the length unit. Perhaps one could have the option to express this as either "dpi" or as "mm per pixel". The question is, from a GUI perspective, where to put those options in Squiggle? Of course, when graphics are unscaled, an inch in the SVG code should still map to an inch on the printout (and equally for cm, mm etc). I know that Squiggle is mainly a demonstration of how to write an application using the Batik framework but as it excels in printout quality compared to the rest "out there" and it is sooo close to do *exactly* what I need an SVG user agent to do I would highly appreciate the above feature. BTW, how big is the focus on printout quality? After searching the archives, I get a feeling that on screen display is top priority and that printing quality is of lower importance. Is this correct? Note, I'm not questioning that priority, if it would happen to be the case because I find it reasonable and also, even if you don't prioritize print quality, you've managed to excel in that area anyway ;-). But in the case you wonders if there's anyone on the planet who views SVG primarly as a tool for conveying descriptions of printed documents, there's at least one of us (me :-). HACKING THE CODE... And now comes the part of the mail which would better be discussed on the developers list: It contains what I've done to the poor source code to tweak around with scaling. First said, the code is neat and nicely organized and easy to navigate in (I consider the fact that I didn't (?) get lost in it as a proof of that statement). Another point worth noting: the discussion below let dpi (dots per inch) denote the resolution on the printer while "pixel per inch" means how many screen pixels an inch on the printed media corresponds to. Example, if I print an image at a 96 pixels per inch resolution on a 600 dpi printer that means that a square on screen being 96 pixels long and 96 pixels high will be 1 by 1 inch on the printed media and it will be made up by 600 by 600 printer dots. As a test, I did a quick and really dirty hard coded hack in the Squiggle code, where I added a few lines in the method PrintAction.actionPerformed() (found in the file JSVGViewerFrame.java) where I added yet another "hint" to the PrintTranscoder: "KEY_SCALE_TO_PAGE" where set to Boolean.FALSE. This was a fun test. When printing out my graphics, they were indeed not scaled, they were instead small and tiny but still crisp (600 dpi lasers and Squiggle... unbeatable combo...:-). There was only one issue: even if smaller as expected, the graphics were still approximately 33% larger than stated in the SVG document. A 100mm square in the document became 133mm on print and half inch squares became approximately 1/3 larger than intended. When using the "pixel" as the length unit, the graphics were not printed as 96 pixels per inch but instead at 72 pixels per inch. After chasing stuff around in the source code, I found out that batik hardcodes a pixel to be approx 0.2645833333333... mm (in the method UserAgentAdapter.getPixelUnitToMillimeter() in UserAgentAdapter.java) and according to a comment on the same line the intent is to get a resolution of... 96 pixels per inch! (Worth noting, I only use length units in the size attribute in the outermost SVG tag. All coordinates for graphical primitives are in the Viewport coordinate space, without any length units. This all follows the recommendations found in the SVG 1.0 spec. ) Has anyone else replicated this issue with 33% too large printouts? I use JDK 1.4.0_02 on Windows 2000 (service pack 3) and the beta release 1_5beta4 of Batik (downloaded 2003-Feb-26). So, after some thinking I came up with a wild guess of what's happening: When printing, Batik initially wants stuff to be printed at 96 pixels per inch and lays out the graphics accordingly. Somewhere in the path of transforming the SVG document into a printout, Batik accidently "switches" to a 72 pixels per inch resolution (perhaps just before the laid out SVG graphics are sent to the printer) and therefore the graphics become enlarged by a factor 96/72=4/3=1.333333... !! Maybe the java.awt libraries makes some unfortunate guess regarding how many pixels per inch should be used and therefore things break. (Ok, I admit, I just speculate here...) Ok, it's a guess. But to investigate, I went into the source again and added the following hint: "KEY_PIXEL_UNIT_TO_MILLIMETER" being set to 0.1984375 to "scale down" a pixel to 3/4 of it's usual size (0.2645833333.... * 3/4 = 0.1984375). I also set the deprecated "KEY_PIXEL_TO_MM" to that values just to be sure ;-). This caused no effect because "KEY_SCALE_TO_PAGE" were false which prevents ANY scaling (I read the code and realized that a a false value of "KEY_SCALE_TO_PAGE" makes the PrintTranscoder completely ignore "KEY_PIXEL_UNIT_TO_MILLIMETER" and "KEY_PIXEL_TO_MM"). So, I did some more fearless hacking... in PrintTranscoder.print() I edited the code which set scale=1 if "KEY_SCALE_TO_PAGE" had a false value. The change I did was that the code also checked if either "KEY_PIXEL_UNIT_TO_MILLIMETER" or "KEY_PIXEL_TO_MM" were set and if so, use them to calculate the scale. If they weren't set, scale were set to 1. The result were that when I specified a document to use "pixels" as it's size, the graphics are printed at 96 pixels per inch (success!!) but as soon as I supplied another length unit such as mm, cm, in etc to the SVG documents size, the graphics were again 33% too large. At that stage I decided to stop hacking. This for two reasons: 1.) I finally got the kind of printouts I wanted (SVG docs using "pixels" as length unit appears on print at a 96 pixels per inch resolution) so I hade a OK workaround and 2.) I realized that I didn't really know what I were doing with the code as I haven't understood it fully enough and therefore, other people who really know how it works should have a look at it instead. I suspect that to fix the issue of printed out docs being 33% larger than expected one have to make nothing but a tiny change but one have to do it at the right place. So, to make Squiggle amazingly useful for printing paper docs defined in SVG only the following is needed: 1.) An option to disable autoscaling and instead print graphics unscaled (in Squiggle). 2.) A proper bugfix to the problem of graphics being 33% too big when unscaled (my hack is not a proper fix ;-). Would require some fix somewhere in the Batik framework code itself. I provide "context patches" below of my hacked versions of PrintTranscoder.java and JSVGViewerFrame.java for those who happen to be intrested. I put qoutes around "context patches" because I create these by hand (I have no good "diff" utility on my Windows box) by simply including a fairly large part of the code before and after my changes (actually, the entire method definition for the altered methods). One alternative would have been to the entire files as attachments but I've found out the hard way that won't work... (the resulting mail becomes larger than 100k and is therefore rejected...) DISCLAIMER (or, I know I do the following wrong here...) I am aware that I in this email break against a number of holy rules of how to ask for help on maillists: 1.) I write a very long email 2.) I submit hand made context patches which the "patch" program most likely would not accept (sorry, I sit on a windows box, I have no good diff program :-< ) 3.) I use the deprecated KEY_PIXEL_TO_MM, thus provoking the java compiler to emit unneeded noise. 4.) I've not produced a usable patch which can be included directly into the product. 5.) In spite of having several flavors of OSes (WinME, Linux Mandrake 8.1, HP-UX 11.00) and several versions of JDK/JRE and different printers as well at my disposal I haven't explored these combinations to track down if this issue might be related with the a.) OS, b.) Java Version, c.) printer or d.) a combination of these. 6.) I make speculations about what can be the cause of the problem instead of sticking to just document the symptoms and leave diagnostics to those who actually knows the product. 7.) I mix content suitable for batik-users and batik-dev in the same mail! (But I tried to search the list archives and I did read the FAQ. Also, I've expressed my candid opinion about the quality of the product and it's superb printing abilities!!) In spite of all virtues of nettiquette I've ignored I hope you folks don't mind me sending this email to the list. Best regards and a lot of thanks in advance /IlvJa Hand made diff context for PrintTranscoder.java ----8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<---- public class PrintAction extends AbstractAction { public PrintAction() {} public void actionPerformed(ActionEvent e) { if (svgDocument != null) { final SVGDocument doc = svgDocument; new Thread() { public void run(){ String uri = doc.getURL(); String fragment = svgCanvas.getFragmentIdentifier(); if (fragment != null) { uri += "#"+fragment; } // // Build a PrintTranscoder to handle printing // of the svgDocument object // PrintTranscoder pt = new PrintTranscoder(); // // Set transcoding hints // pt.addTranscodingHint(pt.KEY_XML_PARSER_CLASSNAME, application.getXMLParserClassName()); pt.addTranscodingHint(pt.KEY_SHOW_PAGE_DIALOG, Boolean.TRUE); pt.addTranscodingHint(pt.KEY_SHOW_PRINTER_DIALOG, Boolean.TRUE); // // Jakob Ilves hardcoded (and therefore ugly) hack. // // As I want to print UNSCALED graphics I want the // transcoder hint KEY_SCALE_TO_PAGE to be set to // false. How to best do that I have to discuss with // people who develop batik. // // Also, it appears that the graphics on screen is // assumed to be 96dpi while the printer is assumed // to be 72dpi and somewhere a mistake occurs so the // printed graphics becomes 4/3 times larger than // wanted. So, I also (ab-)use the deprecated // KEY_PIXEL_TO_MM to adjust things. // pt.addTranscodingHint(pt.KEY_SCALE_TO_PAGE, Boolean.FALSE); pt.addTranscodingHint(pt.KEY_PIXEL_UNIT_TO_MILLIMETER, new Float(0.1984375)); pt.addTranscodingHint(pt.KEY_PIXEL_TO_MM, new Float(0.1984375)); // // Do transcoding now // pt.transcode(new TranscoderInput(uri), null); // // Print // try { pt.print(); } catch (PrinterException ex) { userAgent.displayError(ex); } } }.start(); } } } ----8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<---- Hand made diff context for JSVGViewerFrame.java ----8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<---- /** * Printable implementation */ public int print(Graphics _g, PageFormat pageFormat, int pageIndex){ // // On the first page, take a snapshot of the vector of // TranscodeInputs. // if(pageIndex == 0){ printedInputs = (Vector)inputs.clone(); } // // If we have already printed each page, return // if(pageIndex >= printedInputs.size()){ curIndex = -1; System.out.println("Done"); return NO_SUCH_PAGE; } // // Load a new document now if we are printing a new page // if(curIndex != pageIndex){ // The following call will invoke this class' transcode // method which takes a document as an input. That method // builds the GVT root tree.{ try{ super.transcode((TranscoderInput)printedInputs.elementAt(pageIndex), null); curIndex = pageIndex; }catch(TranscoderException e){ drawError(_g, e); return PAGE_EXISTS; } } // Cast to Graphics2D to access Java 2D features Graphics2D g = (Graphics2D)_g; g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.setRenderingHint(RenderingHintsKeyExt.KEY_TRANSCODING, RenderingHintsKeyExt.VALUE_TRANSCODING_PRINTING); // // Compute transform so that the SVG document fits on one page // AffineTransform t = g.getTransform(); Shape clip = g.getClip(); Rectangle2D bounds = curAOI; double scaleX = pageFormat.getImageableWidth() / bounds.getWidth(); double scaleY = pageFormat.getImageableHeight() / bounds.getHeight(); double scale = scaleX < scaleY ? scaleX : scaleY; // Check hint to know if scaling is really needed Boolean scaleToPage = (Boolean)hints.get(KEY_SCALE_TO_PAGE); if(scaleToPage != null && !scaleToPage.booleanValue()){ // // Jakob Ilves ugly addition: // If KEY_SCALE_TO_PAGE is set to FALSE but still // KEY_PIXEL_UNIT_TO_MILLIMETER or the deprecated // KEY_PIXEL_TO_MM is set honor that value. Note, the default // appears to be 72dpi (for some reason), so if you set to // the value to 0.264583333... (72dpi), the scale should be 1. // If you set it to 0.1984375 (96dpi) the scaling becomes 0.75. // // This hack should be brought up with the Batik developers. // Float px2mm; px2mm = (Float)hints.get(KEY_PIXEL_UNIT_TO_MILLIMETER); if(px2mm == null) { px2mm = (Float)hints.get(KEY_PIXEL_TO_MM); } if(px2mm != null) { scale = px2mm.floatValue()/0.2645833333333333333333333333333f; } else { scale = 1; } } double xMargin = (pageFormat.getImageableWidth() - bounds.getWidth()*scale)/2; double yMargin = (pageFormat.getImageableHeight() - bounds.getHeight()*scale)/2; g.translate(pageFormat.getImageableX() + xMargin, pageFormat.getImageableY() + yMargin); g.scale(scale, scale); // // Append transform to selected area // g.transform(curTxf); g.clip(curAOI); // // Delegate rendering to painter // try{ root.paint(g); }catch(Exception e){ g.setTransform(t); g.setClip(clip); drawError(_g, e); } // // Restore transform and clip // g.setTransform(t); g.setClip(clip); // g.setPaint(Color.black); // g.drawString(uris[pageIndex], 30, 30); // // Return status indicated that we did paint a page // return PAGE_EXISTS; } ----8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<---- --------------E28253806CDF91F8E0CC4C75 Content-Type: text/x-vcard; charset=us-ascii; name="jakob.ilves.vcf" Content-Transfer-Encoding: 7bit Content-Description: Card for Jakob Ilves Content-Disposition: attachment; filename="jakob.ilves.vcf" begin:vcard n:;Jakob x-mozilla-html:FALSE adr:;;;;;; version:2.1 email;internet:jakob.ilves@oracle.com fn:Jakob Ilves end:vcard --------------E28253806CDF91F8E0CC4C75 Content-Type: text/plain; charset=us-ascii --------------------------------------------------------------------- To unsubscribe, e-mail: batik-users-unsubscribe@xml.apache.org For additional commands, e-mail: batik-users-help@xml.apache.org --------------E28253806CDF91F8E0CC4C75--