History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/print/PSPrinterJob.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/print/PSPrinterJob.java+2213−0
@@ -0,0 +1,2213 @@
1+/*
2+ * Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package sun.print;
27+
28+import java.awt.Color;
29+import java.awt.Component;
30+import java.awt.Font;
31+import java.awt.FontMetrics;
32+import java.awt.GraphicsEnvironment;
33+import java.awt.Graphics;
34+import java.awt.Graphics2D;
35+import java.awt.HeadlessException;
36+import java.awt.Rectangle;
37+import java.awt.Shape;
38+
39+import java.awt.image.BufferedImage;
40+
41+import java.awt.font.FontRenderContext;
42+
43+import java.awt.geom.AffineTransform;
44+import java.awt.geom.PathIterator;
45+import java.awt.geom.Rectangle2D;
46+
47+import java.awt.image.BufferedImage;
48+
49+import java.awt.print.Pageable;
50+import java.awt.print.PageFormat;
51+import java.awt.print.Paper;
52+import java.awt.print.Printable;
53+import java.awt.print.PrinterException;
54+import java.awt.print.PrinterIOException;
55+import java.awt.print.PrinterJob;
56+
57+import javax.print.DocFlavor;
58+import javax.print.PrintService;
59+import javax.print.StreamPrintService;
60+import javax.print.attribute.HashPrintRequestAttributeSet;
61+import javax.print.attribute.PrintRequestAttributeSet;
62+import javax.print.attribute.standard.Chromaticity;
63+import javax.print.attribute.standard.Copies;
64+import javax.print.attribute.standard.Destination;
65+import javax.print.attribute.standard.DialogTypeSelection;
66+import javax.print.attribute.standard.JobName;
67+import javax.print.attribute.standard.Sides;
68+
69+import java.io.BufferedInputStream;
70+import java.io.BufferedOutputStream;
71+import java.io.CharConversionException;
72+import java.io.File;
73+import java.io.InputStream;
74+import java.io.IOException;
75+import java.io.FileInputStream;
76+import java.io.FileOutputStream;
77+import java.io.OutputStream;
78+import java.io.PrintStream;
79+
80+import java.util.ArrayList;
81+import java.util.Enumeration;
82+import java.util.Locale;
83+import java.util.Properties;
84+
85+import sun.awt.CharsetString;
86+import sun.awt.FontConfiguration;
87+import sun.awt.FontDescriptor;
88+import sun.awt.PlatformFont;
89+import sun.awt.SunToolkit;
90+import sun.font.FontManagerFactory;
91+import sun.font.FontUtilities;
92+
93+import java.nio.charset.*;
94+import java.nio.CharBuffer;
95+import java.nio.ByteBuffer;
96+
97+//REMIND: Remove use of this class when IPPPrintService is moved to share directory.
98+import java.lang.reflect.Method;
99+
100+/**
101+ * A class which initiates and executes a PostScript printer job.
102+ *
103+ * @author Richard Blanchard
104+ */
105+public class PSPrinterJob extends RasterPrinterJob {
106+
107+ /* Class Constants */
108+
109+ /**
110+ * Passed to the <code>setFillMode</code>
111+ * method this value forces fills to be
112+ * done using the even-odd fill rule.
113+ */
114+ protected static final int FILL_EVEN_ODD = 1;
115+
116+ /**
117+ * Passed to the <code>setFillMode</code>
118+ * method this value forces fills to be
119+ * done using the non-zero winding rule.
120+ */
121+ protected static final int FILL_WINDING = 2;
122+
123+ /* PostScript has a 64K maximum on its strings.
124+ */
125+ private static final int MAX_PSSTR = (1024 * 64 - 1);
126+
127+ private static final int RED_MASK = 0x00ff0000;
128+ private static final int GREEN_MASK = 0x0000ff00;
129+ private static final int BLUE_MASK = 0x000000ff;
130+
131+ private static final int RED_SHIFT = 16;
132+ private static final int GREEN_SHIFT = 8;
133+ private static final int BLUE_SHIFT = 0;
134+
135+ private static final int LOWNIBBLE_MASK = 0x0000000f;
136+ private static final int HINIBBLE_MASK = 0x000000f0;
137+ private static final int HINIBBLE_SHIFT = 4;
138+ private static final byte hexDigits[] = {
139+ (byte)'0', (byte)'1', (byte)'2', (byte)'3',
140+ (byte)'4', (byte)'5', (byte)'6', (byte)'7',
141+ (byte)'8', (byte)'9', (byte)'A', (byte)'B',
142+ (byte)'C', (byte)'D', (byte)'E', (byte)'F'
143+ };
144+
145+ private static final int PS_XRES = 300;
146+ private static final int PS_YRES = 300;
147+
148+ private static final String ADOBE_PS_STR = "%!PS-Adobe-3.0";
149+ private static final String EOF_COMMENT = "%%EOF";
150+ private static final String PAGE_COMMENT = "%%Page: ";
151+
152+ private static final String READIMAGEPROC = "/imStr 0 def /imageSrc " +
153+ "{currentfile /ASCII85Decode filter /RunLengthDecode filter " +
154+ " imStr readstring pop } def";
155+
156+ private static final String COPIES = "/#copies exch def";
157+ private static final String PAGE_SAVE = "/pgSave save def";
158+ private static final String PAGE_RESTORE = "pgSave restore";
159+ private static final String SHOWPAGE = "showpage";
160+ private static final String IMAGE_SAVE = "/imSave save def";
161+ private static final String IMAGE_STR = " string /imStr exch def";
162+ private static final String IMAGE_RESTORE = "imSave restore";
163+
164+ private static final String COORD_PREP = " 0 exch translate "
165+ + "1 -1 scale"
166+ + "[72 " + PS_XRES + " div "
167+ + "0 0 "
168+ + "72 " + PS_YRES + " div "
169+ + "0 0]concat";
170+
171+ private static final String SetFontName = "F";
172+
173+ private static final String DrawStringName = "S";
174+
175+ /**
176+ * The PostScript invocation to fill a path using the
177+ * even-odd rule. (eofill)
178+ */
179+ private static final String EVEN_ODD_FILL_STR = "EF";
180+
181+ /**
182+ * The PostScript invocation to fill a path using the
183+ * non-zero winding rule. (fill)
184+ */
185+ private static final String WINDING_FILL_STR = "WF";
186+
187+ /**
188+ * The PostScript to set the clip to be the current path
189+ * using the even odd rule. (eoclip)
190+ */
191+ private static final String EVEN_ODD_CLIP_STR = "EC";
192+
193+ /**
194+ * The PostScript to set the clip to be the current path
195+ * using the non-zero winding rule. (clip)
196+ */
197+ private static final String WINDING_CLIP_STR = "WC";
198+
199+ /**
200+ * Expecting two numbers on the PostScript stack, this
201+ * invocation moves the current pen position. (moveto)
202+ */
203+ private static final String MOVETO_STR = " M";
204+ /**
205+ * Expecting two numbers on the PostScript stack, this
206+ * invocation draws a PS line from the current pen
207+ * position to the point on the stack. (lineto)
208+ */
209+ private static final String LINETO_STR = " L";
210+
211+ /**
212+ * This PostScript operator takes two control points
213+ * and an ending point and using the current pen
214+ * position as a starting point adds a bezier
215+ * curve to the current path. (curveto)
216+ */
217+ private static final String CURVETO_STR = " C";
218+
219+ /**
220+ * The PostScript to pop a state off of the printer's
221+ * gstate stack. (grestore)
222+ */
223+ private static final String GRESTORE_STR = "R";
224+ /**
225+ * The PostScript to push a state on to the printer's
226+ * gstate stack. (gsave)
227+ */
228+ private static final String GSAVE_STR = "G";
229+
230+ /**
231+ * Make the current PostScript path an empty path. (newpath)
232+ */
233+ private static final String NEWPATH_STR = "N";
234+
235+ /**
236+ * Close the current subpath by generating a line segment
237+ * from the current position to the start of the subpath. (closepath)
238+ */
239+ private static final String CLOSEPATH_STR = "P";
240+
241+ /**
242+ * Use the three numbers on top of the PS operator
243+ * stack to set the rgb color. (setrgbcolor)
244+ */
245+ private static final String SETRGBCOLOR_STR = " SC";
246+
247+ /**
248+ * Use the top number on the stack to set the printer's
249+ * current gray value. (setgray)
250+ */
251+ private static final String SETGRAY_STR = " SG";
252+
253+ /* Instance Variables */
254+
255+ private int mDestType;
256+
257+ private String mDestination = "lp";
258+
259+ private boolean mNoJobSheet = false;
260+
261+ private String mOptions;
262+
263+ private Font mLastFont;
264+
265+ private Color mLastColor;
266+
267+ private Shape mLastClip;
268+
269+ private AffineTransform mLastTransform;
270+
271+ /* non-null if printing EPS for Java Plugin */
272+ private EPSPrinter epsPrinter = null;
273+
274+ /**
275+ * The metrics for the font currently set.
276+ */
277+ FontMetrics mCurMetrics;
278+
279+ /**
280+ * The output stream to which the generated PostScript
281+ * is written.
282+ */
283+ PrintStream mPSStream;
284+
285+ /* The temporary file to which we spool before sending to the printer */
286+
287+ File spoolFile;
288+
289+ /**
290+ * This string holds the PostScript operator to
291+ * be used to fill a path. It can be changed
292+ * by the <code>setFillMode</code> method.
293+ */
294+ private String mFillOpStr = WINDING_FILL_STR;
295+
296+ /**
297+ * This string holds the PostScript operator to
298+ * be used to clip to a path. It can be changed
299+ * by the <code>setFillMode</code> method.
300+ */
301+ private String mClipOpStr = WINDING_CLIP_STR;
302+
303+ /**
304+ * A stack that represents the PostScript gstate stack.
305+ */
306+ ArrayList mGStateStack = new ArrayList();
307+
308+ /**
309+ * The x coordinate of the current pen position.
310+ */
311+ private float mPenX;
312+
313+ /**
314+ * The y coordinate of the current pen position.
315+ */
316+ private float mPenY;
317+
318+ /**
319+ * The x coordinate of the starting point of
320+ * the current subpath.
321+ */
322+ private float mStartPathX;
323+
324+ /**
325+ * The y coordinate of the starting point of
326+ * the current subpath.
327+ */
328+ private float mStartPathY;
329+
330+ /**
331+ * An optional mapping of fonts to PostScript names.
332+ */
333+ private static Properties mFontProps = null;
334+
335+ /* Class static initialiser block */
336+ static {
337+ //enable priviledges so initProps can access system properties,
338+ // open the property file, etc.
339+ java.security.AccessController.doPrivileged(
340+ new java.security.PrivilegedAction() {
341+ public Object run() {
342+ mFontProps = initProps();
343+ return null;
344+ }
345+ });
346+ }
347+
348+ /*
349+ * Initialize PostScript font properties.
350+ * Copied from PSPrintStream
351+ */
352+ private static Properties initProps() {
353+ // search psfont.properties for fonts
354+ // and create and initialize fontProps if it exist.
355+
356+ String jhome = System.getProperty("java.home");
357+
358+ if (jhome != null){
359+ String ulocale = SunToolkit.getStartupLocale().getLanguage();
360+ try {
361+
362+ File f = new File(jhome + File.separator +
363+ "lib" + File.separator +
364+ "psfontj2d.properties." + ulocale);
365+
366+ if (!f.canRead()){
367+
368+ f = new File(jhome + File.separator +
369+ "lib" + File.separator +
370+ "psfont.properties." + ulocale);
371+ if (!f.canRead()){
372+
373+ f = new File(jhome + File.separator + "lib" +
374+ File.separator + "psfontj2d.properties");
375+
376+ if (!f.canRead()){
377+
378+ f = new File(jhome + File.separator + "lib" +
379+ File.separator + "psfont.properties");
380+
381+ if (!f.canRead()){
382+ return (Properties)null;
383+ }
384+ }
385+ }
386+ }
387+
388+ // Load property file
389+ InputStream in =
390+ new BufferedInputStream(new FileInputStream(f.getPath()));
391+ Properties props = new Properties();
392+ props.load(in);
393+ in.close();
394+ return props;
395+ } catch (Exception e){
396+ return (Properties)null;
397+ }
398+ }
399+ return (Properties)null;
400+ }
401+
402+ /* Constructors */
403+
404+ public PSPrinterJob()
405+ {
406+ }
407+
408+ /* Instance Methods */
409+
410+ /**
411+ * Presents the user a dialog for changing properties of the
412+ * print job interactively.
413+ * @returns false if the user cancels the dialog and
414+ * true otherwise.
415+ * @exception HeadlessException if GraphicsEnvironment.isHeadless()
416+ * returns true.
417+ * @see java.awt.GraphicsEnvironment#isHeadless
418+ */
419+ public boolean printDialog() throws HeadlessException {
420+
421+ if (GraphicsEnvironment.isHeadless()) {
422+ throw new HeadlessException();
423+ }
424+
425+ if (attributes == null) {
426+ attributes = new HashPrintRequestAttributeSet();
427+ }
428+ attributes.add(new Copies(getCopies()));
429+ attributes.add(new JobName(getJobName(), null));
430+
431+ boolean doPrint = false;
432+ DialogTypeSelection dts =
433+ (DialogTypeSelection)attributes.get(DialogTypeSelection.class);
434+ if (dts == DialogTypeSelection.NATIVE) {
435+ // Remove DialogTypeSelection.NATIVE to prevent infinite loop in
436+ // RasterPrinterJob.
437+ attributes.remove(DialogTypeSelection.class);
438+ doPrint = printDialog(attributes);
439+ // restore attribute
440+ attributes.add(DialogTypeSelection.NATIVE);
441+ } else {
442+ doPrint = printDialog(attributes);
443+ }
444+
445+ if (doPrint) {
446+ JobName jobName = (JobName)attributes.get(JobName.class);
447+ if (jobName != null) {
448+ setJobName(jobName.getValue());
449+ }
450+ Copies copies = (Copies)attributes.get(Copies.class);
451+ if (copies != null) {
452+ setCopies(copies.getValue());
453+ }
454+
455+ Destination dest = (Destination)attributes.get(Destination.class);
456+
457+ if (dest != null) {
458+ try {
459+ mDestType = RasterPrinterJob.FILE;
460+ mDestination = (new File(dest.getURI())).getPath();
461+ } catch (Exception e) {
462+ mDestination = "out.ps";
463+ }
464+ } else {
465+ mDestType = RasterPrinterJob.PRINTER;
466+ PrintService pServ = getPrintService();
467+ if (pServ != null) {
468+ mDestination = pServ.getName();
469+ }
470+ }
471+ }
472+
473+ return doPrint;
474+ }
475+
476+ /**
477+ * Invoked by the RasterPrinterJob super class
478+ * this method is called to mark the start of a
479+ * document.
480+ */
481+ protected void startDoc() throws PrinterException {
482+
483+ // A security check has been performed in the
484+ // java.awt.print.printerJob.getPrinterJob method.
485+ // We use an inner class to execute the privilged open operations.
486+ // Note that we only open a file if it has been nominated by
487+ // the end-user in a dialog that we ouselves put up.
488+
489+ OutputStream output;
490+
491+ if (epsPrinter == null) {
492+ if (getPrintService() instanceof PSStreamPrintService) {
493+ StreamPrintService sps = (StreamPrintService)getPrintService();
494+ mDestType = RasterPrinterJob.STREAM;
495+ if (sps.isDisposed()) {
496+ throw new PrinterException("service is disposed");
497+ }
498+ output = sps.getOutputStream();
499+ if (output == null) {
500+ throw new PrinterException("Null output stream");
501+ }
502+ } else {
503+ /* REMIND: This needs to be more maintainable */
504+ mNoJobSheet = super.noJobSheet;
505+ if (super.destinationAttr != null) {
506+ mDestType = RasterPrinterJob.FILE;
507+ mDestination = super.destinationAttr;
508+ }
509+ if (mDestType == RasterPrinterJob.FILE) {
510+ try {
511+ spoolFile = new File(mDestination);
512+ output = new FileOutputStream(spoolFile);
513+ } catch (IOException ex) {
514+ throw new PrinterIOException(ex);
515+ }
516+ } else {
517+ PrinterOpener po = new PrinterOpener();
518+ java.security.AccessController.doPrivileged(po);
519+ if (po.pex != null) {
520+ throw po.pex;
521+ }
522+ output = po.result;
523+ }
524+ }
525+
526+ mPSStream = new PrintStream(new BufferedOutputStream(output));
527+ mPSStream.println(ADOBE_PS_STR);
528+ }
529+
530+ mPSStream.println("%%BeginProlog");
531+ mPSStream.println(READIMAGEPROC);
532+ mPSStream.println("/BD {bind def} bind def");
533+ mPSStream.println("/D {def} BD");
534+ mPSStream.println("/C {curveto} BD");
535+ mPSStream.println("/L {lineto} BD");
536+ mPSStream.println("/M {moveto} BD");
537+ mPSStream.println("/R {grestore} BD");
538+ mPSStream.println("/G {gsave} BD");
539+ mPSStream.println("/N {newpath} BD");
540+ mPSStream.println("/P {closepath} BD");
541+ mPSStream.println("/EC {eoclip} BD");
542+ mPSStream.println("/WC {clip} BD");
543+ mPSStream.println("/EF {eofill} BD");
544+ mPSStream.println("/WF {fill} BD");
545+ mPSStream.println("/SG {setgray} BD");
546+ mPSStream.println("/SC {setrgbcolor} BD");
547+ mPSStream.println("/ISOF {");
548+ mPSStream.println(" dup findfont dup length 1 add dict begin {");
549+ mPSStream.println(" 1 index /FID eq {pop pop} {D} ifelse");
550+ mPSStream.println(" } forall /Encoding ISOLatin1Encoding D");
551+ mPSStream.println(" currentdict end definefont");
552+ mPSStream.println("} BD");
553+ mPSStream.println("/NZ {dup 1 lt {pop 1} if} BD");
554+ /* The following procedure takes args: string, x, y, desiredWidth.
555+ * It calculates using stringwidth the width of the string in the
556+ * current font and subtracts it from the desiredWidth and divides
557+ * this by stringLen-1. This gives us a per-glyph adjustment in
558+ * the spacing needed (either +ve or -ve) to make the string
559+ * print at the desiredWidth. The ashow procedure call takes this
560+ * per-glyph adjustment as an argument. This is necessary for WYSIWYG
561+ */
562+ mPSStream.println("/"+DrawStringName +" {");
563+ mPSStream.println(" moveto 1 index stringwidth pop NZ sub");
564+ mPSStream.println(" 1 index length 1 sub NZ div 0");
565+ mPSStream.println(" 3 2 roll ashow newpath} BD");
566+ mPSStream.println("/FL [");
567+ if (mFontProps == null){
568+ mPSStream.println(" /Helvetica ISOF");
569+ mPSStream.println(" /Helvetica-Bold ISOF");
570+ mPSStream.println(" /Helvetica-Oblique ISOF");
571+ mPSStream.println(" /Helvetica-BoldOblique ISOF");
572+ mPSStream.println(" /Times-Roman ISOF");
573+ mPSStream.println(" /Times-Bold ISOF");
574+ mPSStream.println(" /Times-Italic ISOF");
575+ mPSStream.println(" /Times-BoldItalic ISOF");
576+ mPSStream.println(" /Courier ISOF");
577+ mPSStream.println(" /Courier-Bold ISOF");
578+ mPSStream.println(" /Courier-Oblique ISOF");
579+ mPSStream.println(" /Courier-BoldOblique ISOF");
580+ } else {
581+ int cnt = Integer.parseInt(mFontProps.getProperty("font.num", "9"));
582+ for (int i = 0; i < cnt; i++){
583+ mPSStream.println(" /" + mFontProps.getProperty
584+ ("font." + String.valueOf(i), "Courier ISOF"));
585+ }
586+ }
587+ mPSStream.println("] D");
588+
589+ mPSStream.println("/"+SetFontName +" {");
590+ mPSStream.println(" FL exch get exch scalefont");
591+ mPSStream.println(" [1 0 0 -1 0 0] makefont setfont} BD");
592+
593+ mPSStream.println("%%EndProlog");
594+
595+ mPSStream.println("%%BeginSetup");
596+ if (epsPrinter == null) {
597+ // Set Page Size using first page's format.
598+ PageFormat pageFormat = getPageable().getPageFormat(0);
599+ double paperHeight = pageFormat.getPaper().getHeight();
600+ double paperWidth = pageFormat.getPaper().getWidth();
601+
602+ /* PostScript printers can always generate uncollated copies.
603+ */
604+ mPSStream.print("<< /PageSize [" +
605+ paperWidth + " "+ paperHeight+"]");
606+
607+ final PrintService pservice = getPrintService();
608+ Boolean isPS = (Boolean)java.security.AccessController.doPrivileged(
609+ new java.security.PrivilegedAction() {
610+ public Object run() {
611+ try {
612+ Class psClass = Class.forName("sun.print.IPPPrintService");
613+ if (psClass.isInstance(pservice)) {
614+ Method isPSMethod = psClass.getMethod("isPostscript",
615+ (Class[])null);
616+ return (Boolean)isPSMethod.invoke(pservice, (Object[])null);
617+ }
618+ } catch (Throwable t) {
619+ }
620+ return Boolean.TRUE;
621+ }
622+ }
623+ );
624+ if (isPS) {
625+ mPSStream.print(" /DeferredMediaSelection true");
626+ }
627+
628+ mPSStream.print(" /ImagingBBox null /ManualFeed false");
629+ mPSStream.print(isCollated() ? " /Collate true":"");
630+ mPSStream.print(" /NumCopies " +getCopiesInt());
631+
632+ if (sidesAttr != Sides.ONE_SIDED) {
633+ if (sidesAttr == Sides.TWO_SIDED_LONG_EDGE) {
634+ mPSStream.print(" /Duplex true ");
635+ } else if (sidesAttr == Sides.TWO_SIDED_SHORT_EDGE) {
636+ mPSStream.print(" /Duplex true /Tumble true ");
637+ }
638+ }
639+ mPSStream.println(" >> setpagedevice ");
640+ }
641+ mPSStream.println("%%EndSetup");
642+ }
643+
644+ // Inner class to run "privileged" to open the printer output stream.
645+
646+ private class PrinterOpener implements java.security.PrivilegedAction {
647+ PrinterException pex;
648+ OutputStream result;
649+
650+ public Object run() {
651+ try {
652+
653+ /* Write to a temporary file which will be spooled to
654+ * the printer then deleted. In the case that the file
655+ * is not removed for some reason, request that it is
656+ * removed when the VM exits.
657+ */
658+ spoolFile = File.createTempFile("javaprint", ".ps", null);
659+ spoolFile.deleteOnExit();
660+
661+ result = new FileOutputStream(spoolFile);
662+ return result;
663+ } catch (IOException ex) {
664+ // If there is an IOError we subvert it to a PrinterException.
665+ pex = new PrinterIOException(ex);
666+ }
667+ return null;
668+ }
669+ }
670+
671+ // Inner class to run "privileged" to invoke the system print command
672+
673+ private class PrinterSpooler implements java.security.PrivilegedAction {
674+ PrinterException pex;
675+
676+ public Object run() {
677+ try {
678+ /**
679+ * Spool to the printer.
680+ */
681+ if (spoolFile == null || !spoolFile.exists()) {
682+ pex = new PrinterException("No spool file");
683+ return null;
684+ }
685+ String fileName = spoolFile.getAbsolutePath();
686+ String execCmd[] = printExecCmd(mDestination, mOptions,
687+ mNoJobSheet, getJobNameInt(),
688+ 1, fileName);
689+
690+ Process process = Runtime.getRuntime().exec(execCmd);
691+ process.waitFor();
692+ spoolFile.delete();
693+
694+ } catch (IOException ex) {
695+ pex = new PrinterIOException(ex);
696+ } catch (InterruptedException ie) {
697+ pex = new PrinterException(ie.toString());
698+ }
699+ return null;
700+ }
701+ }
702+
703+
704+ /**
705+ * Invoked if the application cancelled the printjob.
706+ */
707+ protected void abortDoc() {
708+ if (mPSStream != null && mDestType != RasterPrinterJob.STREAM) {
709+ mPSStream.close();
710+ }
711+ java.security.AccessController.doPrivileged(
712+ new java.security.PrivilegedAction() {
713+
714+ public Object run() {
715+ if (spoolFile != null && spoolFile.exists()) {
716+ spoolFile.delete();
717+ }
718+ return null;
719+ }
720+ });
721+ }
722+
723+ /**
724+ * Invoked by the RasterPrintJob super class
725+ * this method is called after that last page
726+ * has been imaged.
727+ */
728+ protected void endDoc() throws PrinterException {
729+ if (mPSStream != null) {
730+ mPSStream.println(EOF_COMMENT);
731+ mPSStream.flush();
732+ if (mDestType != RasterPrinterJob.STREAM) {
733+ mPSStream.close();
734+ }
735+ }
736+ if (mDestType == RasterPrinterJob.PRINTER) {
737+ if (getPrintService() != null) {
738+ mDestination = getPrintService().getName();
739+ }
740+ PrinterSpooler spooler = new PrinterSpooler();
741+ java.security.AccessController.doPrivileged(spooler);
742+ if (spooler.pex != null) {
743+ throw spooler.pex;
744+ }
745+ }
746+ }
747+
748+ /**
749+ * The RasterPrintJob super class calls this method
750+ * at the start of each page.
751+ */
752+ protected void startPage(PageFormat pageFormat, Printable painter,
753+ int index, boolean paperChanged)
754+ throws PrinterException
755+ {
756+ double paperHeight = pageFormat.getPaper().getHeight();
757+ double paperWidth = pageFormat.getPaper().getWidth();
758+ int pageNumber = index + 1;
759+
760+ /* Place an initial gstate on to our gstate stack.
761+ * It will have the default PostScript gstate
762+ * attributes.
763+ */
764+ mGStateStack = new ArrayList();
765+ mGStateStack.add(new GState());
766+
767+ mPSStream.println(PAGE_COMMENT + pageNumber + " " + pageNumber);
768+
769+ /* Check current page's pageFormat against the previous pageFormat,
770+ */
771+ if (index > 0 && paperChanged) {
772+
773+ mPSStream.print("<< /PageSize [" +
774+ paperWidth + " " + paperHeight + "]");
775+
776+ final PrintService pservice = getPrintService();
777+ Boolean isPS =
778+ (Boolean)java.security.AccessController.doPrivileged(
779+
780+ new java.security.PrivilegedAction() {
781+ public Object run() {
782+ try {
783+ Class psClass =
784+ Class.forName("sun.print.IPPPrintService");
785+ if (psClass.isInstance(pservice)) {
786+ Method isPSMethod =
787+ psClass.getMethod("isPostscript",
788+ (Class[])null);
789+ return (Boolean)
790+ isPSMethod.invoke(pservice,
791+ (Object[])null);
792+ }
793+ } catch (Throwable t) {
794+ }
795+ return Boolean.TRUE;
796+ }
797+ }
798+ );
799+
800+ if (isPS) {
801+ mPSStream.print(" /DeferredMediaSelection true");
802+ }
803+ mPSStream.println(" >> setpagedevice");
804+ }
805+ mPSStream.println(PAGE_SAVE);
806+ mPSStream.println(paperHeight + COORD_PREP);
807+ }
808+
809+ /**
810+ * The RastePrintJob super class calls this method
811+ * at the end of each page.
812+ */
813+ protected void endPage(PageFormat format, Printable painter,
814+ int index)
815+ throws PrinterException
816+ {
817+ mPSStream.println(PAGE_RESTORE);
818+ mPSStream.println(SHOWPAGE);
819+ }
820+
821+ /**
822+ * Convert the 24 bit BGR image buffer represented by
823+ * <code>image</code> to PostScript. The image is drawn at
824+ * <code>(destX, destY)</code> in device coordinates.
825+ * The image is scaled into a square of size
826+ * specified by <code>destWidth</code> and
827+ * <code>destHeight</code>. The portion of the
828+ * source image copied into that square is specified
829+ * by <code>srcX</code>, <code>srcY</code>,
830+ * <code>srcWidth</code>, and srcHeight.
831+ */
832+ protected void drawImageBGR(byte[] bgrData,
833+ float destX, float destY,
834+ float destWidth, float destHeight,
835+ float srcX, float srcY,
836+ float srcWidth, float srcHeight,
837+ int srcBitMapWidth, int srcBitMapHeight) {
838+
839+ /* We draw images at device resolution so we probably need
840+ * to change the current PostScript transform.
841+ */
842+ setTransform(new AffineTransform());
843+ prepDrawing();
844+
845+ int intSrcWidth = (int) srcWidth;
846+ int intSrcHeight = (int) srcHeight;
847+
848+ mPSStream.println(IMAGE_SAVE);
849+
850+ /* Create a PS string big enough to hold a row of pixels.
851+ */
852+ int psBytesPerRow = 3 * (int) intSrcWidth;
853+ while (psBytesPerRow > MAX_PSSTR) {
854+ psBytesPerRow /= 2;
855+ }
856+
857+ mPSStream.println(psBytesPerRow + IMAGE_STR);
858+
859+ /* Scale and translate the unit image.
860+ */
861+ mPSStream.println("[" + destWidth + " 0 "
862+ + "0 " + destHeight
863+ + " " + destX + " " + destY
864+ +"]concat");
865+
866+ /* Color Image invocation.
867+ */
868+ mPSStream.println(intSrcWidth + " " + intSrcHeight + " " + 8 + "["
869+ + intSrcWidth + " 0 "
870+ + "0 " + intSrcHeight
871+ + " 0 " + 0 + "]"
872+ + "/imageSrc load false 3 colorimage");
873+
874+ /* Image data.
875+ */
876+ int index = 0;
877+ byte[] rgbData = new byte[intSrcWidth * 3];
878+
879+ try {
880+ /* Skip the parts of the image that are not part
881+ * of the source rectangle.
882+ */
883+ index = (int) srcY * srcBitMapWidth;
884+
885+ for(int i = 0; i < intSrcHeight; i++) {
886+
887+ /* Skip the left part of the image that is not
888+ * part of the source rectangle.
889+ */
890+ index += (int) srcX;
891+
892+ index = swapBGRtoRGB(bgrData, index, rgbData);
893+ byte[] encodedData = rlEncode(rgbData);
894+ byte[] asciiData = ascii85Encode(encodedData);
895+ mPSStream.write(asciiData);
896+ mPSStream.println("");
897+ }
898+
899+ /*
900+ * If there is an IOError we subvert it to a PrinterException.
901+ * Fix: There has got to be a better way, maybe define
902+ * a PrinterIOException and then throw that?
903+ */
904+ } catch (IOException e) {
905+ //throw new PrinterException(e.toString());
906+ }
907+
908+ mPSStream.println(IMAGE_RESTORE);
909+ }
910+
911+ /**
912+ * Prints the contents of the array of ints, 'data'
913+ * to the current page. The band is placed at the
914+ * location (x, y) in device coordinates on the
915+ * page. The width and height of the band is
916+ * specified by the caller. Currently the data
917+ * is 24 bits per pixel in BGR format.
918+ */
919+ protected void printBand(byte[] bgrData, int x, int y,
920+ int width, int height)
921+ throws PrinterException
922+ {
923+
924+ mPSStream.println(IMAGE_SAVE);
925+
926+ /* Create a PS string big enough to hold a row of pixels.
927+ */
928+ int psBytesPerRow = 3 * width;
929+ while (psBytesPerRow > MAX_PSSTR) {
930+ psBytesPerRow /= 2;
931+ }
932+
933+ mPSStream.println(psBytesPerRow + IMAGE_STR);
934+
935+ /* Scale and translate the unit image.
936+ */
937+ mPSStream.println("[" + width + " 0 "
938+ + "0 " + height
939+ + " " + x + " " + y
940+ +"]concat");
941+
942+ /* Color Image invocation.
943+ */
944+ mPSStream.println(width + " " + height + " " + 8 + "["
945+ + width + " 0 "
946+ + "0 " + -height
947+ + " 0 " + height + "]"
948+ + "/imageSrc load false 3 colorimage");
949+
950+ /* Image data.
951+ */
952+ int index = 0;
953+ byte[] rgbData = new byte[width*3];
954+
955+ try {
956+ for(int i = 0; i < height; i++) {
957+ index = swapBGRtoRGB(bgrData, index, rgbData);
958+ byte[] encodedData = rlEncode(rgbData);
959+ byte[] asciiData = ascii85Encode(encodedData);
960+ mPSStream.write(asciiData);
961+ mPSStream.println("");
962+ }
963+
964+ } catch (IOException e) {
965+ throw new PrinterIOException(e);
966+ }
967+
968+ mPSStream.println(IMAGE_RESTORE);
969+ }
970+
971+ /**
972+ * Examine the metrics captured by the
973+ * <code>PeekGraphics</code> instance and
974+ * if capable of directly converting this
975+ * print job to the printer's control language
976+ * or the native OS's graphics primitives, then
977+ * return a <code>PSPathGraphics</code> to perform
978+ * that conversion. If there is not an object
979+ * capable of the conversion then return
980+ * <code>null</code>. Returning <code>null</code>
981+ * causes the print job to be rasterized.
982+ */
983+
984+ protected Graphics2D createPathGraphics(PeekGraphics peekGraphics,
985+ PrinterJob printerJob,
986+ Printable painter,
987+ PageFormat pageFormat,
988+ int pageIndex) {
989+
990+ PSPathGraphics pathGraphics;
991+ PeekMetrics metrics = peekGraphics.getMetrics();
992+
993+ /* If the application has drawn anything that
994+ * out PathGraphics class can not handle then
995+ * return a null PathGraphics.
996+ */
997+ if (forcePDL == false && (forceRaster == true
998+ || metrics.hasNonSolidColors()
999+ || metrics.hasCompositing())) {
1000+
1001+ pathGraphics = null;
1002+ } else {
1003+
1004+ BufferedImage bufferedImage = new BufferedImage(8, 8,
1005+ BufferedImage.TYPE_INT_RGB);
1006+ Graphics2D bufferedGraphics = bufferedImage.createGraphics();
1007+ boolean canRedraw = peekGraphics.getAWTDrawingOnly() == false;
1008+
1009+ pathGraphics = new PSPathGraphics(bufferedGraphics, printerJob,
1010+ painter, pageFormat, pageIndex,
1011+ canRedraw);
1012+ }
1013+
1014+ return pathGraphics;
1015+ }
1016+
1017+ /**
1018+ * Intersect the gstate's current path with the
1019+ * current clip and make the result the new clip.
1020+ */
1021+ protected void selectClipPath() {
1022+
1023+ mPSStream.println(mClipOpStr);
1024+ }
1025+
1026+ protected void setClip(Shape clip) {
1027+
1028+ mLastClip = clip;
1029+ }
1030+
1031+ protected void setTransform(AffineTransform transform) {
1032+ mLastTransform = transform;
1033+ }
1034+
1035+ /**
1036+ * Set the current PostScript font.
1037+ * Taken from outFont in PSPrintStream.
1038+ */
1039+ protected boolean setFont(Font font) {
1040+ mLastFont = font;
1041+ return true;
1042+ }
1043+
1044+ /**
1045+ * Given an array of CharsetStrings that make up a run
1046+ * of text, this routine converts each CharsetString to
1047+ * an index into our PostScript font list. If one or more
1048+ * CharsetStrings can not be represented by a PostScript
1049+ * font, then this routine will return a null array.
1050+ */
1051+ private int[] getPSFontIndexArray(Font font, CharsetString[] charSet) {
1052+ int[] psFont = null;
1053+
1054+ if (mFontProps != null) {
1055+ psFont = new int[charSet.length];
1056+ }
1057+
1058+ for (int i = 0; i < charSet.length && psFont != null; i++){
1059+
1060+ /* Get the encoding of the run of text.
1061+ */
1062+ CharsetString cs = charSet[i];
1063+
1064+ CharsetEncoder fontCS = cs.fontDescriptor.encoder;
1065+ String charsetName = cs.fontDescriptor.getFontCharsetName();
1066+ /*
1067+ * sun.awt.Symbol perhaps should return "symbol" for encoding.
1068+ * Similarly X11Dingbats should return "dingbats"
1069+ * Forced to check for win32 & x/unix names for these converters.
1070+ */
1071+
1072+ if ("Symbol".equals(charsetName)) {
1073+ charsetName = "symbol";
1074+ } else if ("WingDings".equals(charsetName) ||
1075+ "X11Dingbats".equals(charsetName)) {
1076+ charsetName = "dingbats";
1077+ } else {
1078+ charsetName = makeCharsetName(charsetName, cs.charsetChars);
1079+ }
1080+
1081+ int styleMask = font.getStyle() |
1082+ FontUtilities.getFont2D(font).getStyle();
1083+
1084+ String style = FontConfiguration.getStyleString(styleMask);
1085+
1086+ /* First we map the font name through the properties file.
1087+ * This mapping provides alias names for fonts, for example,
1088+ * "timesroman" is mapped to "serif".
1089+ */
1090+ String fontName = font.getFamily().toLowerCase(Locale.ENGLISH);
1091+ fontName = fontName.replace(' ', '_');
1092+ String name = mFontProps.getProperty(fontName, "");
1093+
1094+ /* Now map the alias name, character set name, and style
1095+ * to a PostScript name.
1096+ */
1097+ String psName =
1098+ mFontProps.getProperty(name + "." + charsetName + "." + style,
1099+ null);
1100+
1101+ if (psName != null) {
1102+
1103+ /* Get the PostScript font index for the PostScript font.
1104+ */
1105+ try {
1106+ psFont[i] =
1107+ Integer.parseInt(mFontProps.getProperty(psName));
1108+
1109+ /* If there is no PostScript font for this font name,
1110+ * then we want to termintate the loop and the method
1111+ * indicating our failure. Setting the array to null
1112+ * is used to indicate these failures.
1113+ */
1114+ } catch(NumberFormatException e){
1115+ psFont = null;
1116+ }
1117+
1118+ /* There was no PostScript name for the font, character set,
1119+ * and style so give up.
1120+ */
1121+ } else {
1122+ psFont = null;
1123+ }
1124+ }
1125+
1126+ return psFont;
1127+ }
1128+
1129+
1130+ private static String escapeParens(String str) {
1131+ if (str.indexOf('(') == -1 && str.indexOf(')') == -1 ) {
1132+ return str;
1133+ } else {
1134+ int count = 0;
1135+ int pos = 0;
1136+ while ((pos = str.indexOf('(', pos)) != -1) {
1137+ count++;
1138+ pos++;
1139+ }
1140+ pos = 0;
1141+ while ((pos = str.indexOf(')', pos)) != -1) {
1142+ count++;
1143+ pos++;
1144+ }
1145+ char []inArr = str.toCharArray();
1146+ char []outArr = new char[inArr.length+count];
1147+ pos = 0;
1148+ for (int i=0;i<inArr.length;i++) {
1149+ if (inArr[i] == '(' || inArr[i] == ')') {
1150+ outArr[pos++] = '\\';
1151+ }
1152+ outArr[pos++] = inArr[i];
1153+ }
1154+ return new String(outArr);
1155+
1156+ }
1157+ }
1158+
1159+ /* return of 0 means unsupported. Other return indicates the number
1160+ * of distinct PS fonts needed to draw this text. This saves us
1161+ * doing this processing one extra time.
1162+ */
1163+ protected int platformFontCount(Font font, String str) {
1164+ if (mFontProps == null) {
1165+ return 0;
1166+ }
1167+ CharsetString[] acs =
1168+ ((PlatformFont)(font.getPeer())).makeMultiCharsetString(str,false);
1169+ if (acs == null) {
1170+ /* AWT can't convert all chars so use 2D path */
1171+ return 0;
1172+ }
1173+ int[] psFonts = getPSFontIndexArray(font, acs);
1174+ return (psFonts == null) ? 0 : psFonts.length;
1175+ }
1176+
1177+ protected boolean textOut(Graphics g, String str, float x, float y,
1178+ Font mLastFont, FontRenderContext frc,
1179+ float width) {
1180+ boolean didText = true;
1181+
1182+ if (mFontProps == null) {
1183+ return false;
1184+ } else {
1185+ prepDrawing();
1186+
1187+ /* On-screen drawString renders most control chars as the missing
1188+ * glyph and have the non-zero advance of that glyph.
1189+ * Exceptions are \t, \n and \r which are considered zero-width.
1190+ * Postscript handles control chars mostly as a missing glyph.
1191+ * But we use 'ashow' specifying a width for the string which
1192+ * assumes zero-width for those three exceptions, and Postscript
1193+ * tries to squeeze the extra char in, with the result that the
1194+ * glyphs look compressed or even overlap.
1195+ * So exclude those control chars from the string sent to PS.
1196+ */
1197+ str = removeControlChars(str);
1198+ if (str.length() == 0) {
1199+ return true;
1200+ }
1201+ CharsetString[] acs =
1202+ ((PlatformFont)
1203+ (mLastFont.getPeer())).makeMultiCharsetString(str, false);
1204+ if (acs == null) {
1205+ /* AWT can't convert all chars so use 2D path */
1206+ return false;
1207+ }
1208+ /* Get an array of indices into our PostScript name
1209+ * table. If all of the runs can not be converted
1210+ * to PostScript fonts then null is returned and
1211+ * we'll want to fall back to printing the text
1212+ * as shapes.
1213+ */
1214+ int[] psFonts = getPSFontIndexArray(mLastFont, acs);
1215+ if (psFonts != null) {
1216+
1217+ for (int i = 0; i < acs.length; i++){
1218+ CharsetString cs = acs[i];
1219+ CharsetEncoder fontCS = cs.fontDescriptor.encoder;
1220+
1221+ StringBuffer nativeStr = new StringBuffer();
1222+ byte[] strSeg = new byte[cs.length * 2];
1223+ int len = 0;
1224+ try {
1225+ ByteBuffer bb = ByteBuffer.wrap(strSeg);
1226+ fontCS.encode(CharBuffer.wrap(cs.charsetChars,
1227+ cs.offset,
1228+ cs.length),
1229+ bb, true);
1230+ bb.flip();
1231+ len = bb.limit();
1232+ } catch(IllegalStateException xx){
1233+ continue;
1234+ } catch(CoderMalfunctionError xx){
1235+ continue;
1236+ }
1237+ /* The width to fit to may either be specified,
1238+ * or calculated. Specifying by the caller is only
1239+ * valid if the text does not need to be decomposed
1240+ * into multiple calls.
1241+ */
1242+ float desiredWidth;
1243+ if (acs.length == 1 && width != 0f) {
1244+ desiredWidth = width;
1245+ } else {
1246+ Rectangle2D r2d =
1247+ mLastFont.getStringBounds(cs.charsetChars,
1248+ cs.offset,
1249+ cs.offset+cs.length,
1250+ frc);
1251+ desiredWidth = (float)r2d.getWidth();
1252+ }
1253+ /* unprintable chars had width of 0, causing a PS error
1254+ */
1255+ if (desiredWidth == 0) {
1256+ return didText;
1257+ }
1258+ nativeStr.append('<');
1259+ for (int j = 0; j < len; j++){
1260+ byte b = strSeg[j];
1261+ // to avoid encoding conversion with println()
1262+ String hexS = Integer.toHexString(b);
1263+ int length = hexS.length();
1264+ if (length > 2) {
1265+ hexS = hexS.substring(length - 2, length);
1266+ } else if (length == 1) {
1267+ hexS = "0" + hexS;
1268+ } else if (length == 0) {
1269+ hexS = "00";
1270+ }
1271+ nativeStr.append(hexS);
1272+ }
1273+ nativeStr.append('>');
1274+ /* This comment costs too much in output file size */
1275+// mPSStream.println("% Font[" + mLastFont.getName() + ", " +
1276+// FontConfiguration.getStyleString(mLastFont.getStyle()) + ", "
1277+// + mLastFont.getSize2D() + "]");
1278+ getGState().emitPSFont(psFonts[i], mLastFont.getSize2D());
1279+
1280+ // out String
1281+ mPSStream.println(nativeStr.toString() + " " +
1282+ desiredWidth + " " + x + " " + y + " " +
1283+ DrawStringName);
1284+ x += desiredWidth;
1285+ }
1286+ } else {
1287+ didText = false;
1288+ }
1289+ }
1290+
1291+ return didText;
1292+ }
1293+ /**
1294+ * Set the current path rule to be either
1295+ * <code>FILL_EVEN_ODD</code> (using the
1296+ * even-odd file rule) or <code>FILL_WINDING</code>
1297+ * (using the non-zero winding rule.)
1298+ */
1299+ protected void setFillMode(int fillRule) {
1300+
1301+ switch (fillRule) {
1302+
1303+ case FILL_EVEN_ODD:
1304+ mFillOpStr = EVEN_ODD_FILL_STR;
1305+ mClipOpStr = EVEN_ODD_CLIP_STR;
1306+ break;
1307+
1308+ case FILL_WINDING:
1309+ mFillOpStr = WINDING_FILL_STR;
1310+ mClipOpStr = WINDING_CLIP_STR;
1311+ break;
1312+
1313+ default:
1314+ throw new IllegalArgumentException();
1315+ }
1316+
1317+ }
1318+
1319+ /**
1320+ * Set the printer's current color to be that
1321+ * defined by <code>color</code>
1322+ */
1323+ protected void setColor(Color color) {
1324+ mLastColor = color;
1325+ }
1326+
1327+ /**
1328+ * Fill the current path using the current fill mode
1329+ * and color.
1330+ */
1331+ protected void fillPath() {
1332+
1333+ mPSStream.println(mFillOpStr);
1334+ }
1335+
1336+ /**
1337+ * Called to mark the start of a new path.
1338+ */
1339+ protected void beginPath() {
1340+
1341+ prepDrawing();
1342+ mPSStream.println(NEWPATH_STR);
1343+
1344+ mPenX = 0;
1345+ mPenY = 0;
1346+ }
1347+
1348+ /**
1349+ * Close the current subpath by appending a straight
1350+ * line from the current point to the subpath's
1351+ * starting point.
1352+ */
1353+ protected void closeSubpath() {
1354+
1355+ mPSStream.println(CLOSEPATH_STR);
1356+
1357+ mPenX = mStartPathX;
1358+ mPenY = mStartPathY;
1359+ }
1360+
1361+
1362+ /**
1363+ * Generate PostScript to move the current pen
1364+ * position to <code>(x, y)</code>.
1365+ */
1366+ protected void moveTo(float x, float y) {
1367+
1368+ mPSStream.println(trunc(x) + " " + trunc(y) + MOVETO_STR);
1369+
1370+ /* moveto marks the start of a new subpath
1371+ * and we need to remember that starting
1372+ * position so that we know where the
1373+ * pen returns to with a close path.
1374+ */
1375+ mStartPathX = x;
1376+ mStartPathY = y;
1377+
1378+ mPenX = x;
1379+ mPenY = y;
1380+ }
1381+ /**
1382+ * Generate PostScript to draw a line from the
1383+ * current pen position to <code>(x, y)</code>.
1384+ */
1385+ protected void lineTo(float x, float y) {
1386+
1387+ mPSStream.println(trunc(x) + " " + trunc(y) + LINETO_STR);
1388+
1389+ mPenX = x;
1390+ mPenY = y;
1391+ }
1392+
1393+ /**
1394+ * Add to the current path a bezier curve formed
1395+ * by the current pen position and the method parameters
1396+ * which are two control points and an ending
1397+ * point.
1398+ */
1399+ protected void bezierTo(float control1x, float control1y,
1400+ float control2x, float control2y,
1401+ float endX, float endY) {
1402+
1403+// mPSStream.println(control1x + " " + control1y
1404+// + " " + control2x + " " + control2y
1405+// + " " + endX + " " + endY
1406+// + CURVETO_STR);
1407+ mPSStream.println(trunc(control1x) + " " + trunc(control1y)
1408+ + " " + trunc(control2x) + " " + trunc(control2y)
1409+ + " " + trunc(endX) + " " + trunc(endY)
1410+ + CURVETO_STR);
1411+
1412+
1413+ mPenX = endX;
1414+ mPenY = endY;
1415+ }
1416+
1417+ String trunc(float f) {
1418+ float af = Math.abs(f);
1419+ if (af >= 1f && af <=1000f) {
1420+ f = Math.round(f*1000)/1000f;
1421+ }
1422+ return Float.toString(f);
1423+ }
1424+
1425+ /**
1426+ * Return the x coordinate of the pen in the
1427+ * current path.
1428+ */
1429+ protected float getPenX() {
1430+
1431+ return mPenX;
1432+ }
1433+ /**
1434+ * Return the y coordinate of the pen in the
1435+ * current path.
1436+ */
1437+ protected float getPenY() {
1438+
1439+ return mPenY;
1440+ }
1441+
1442+ /**
1443+ * Return the x resolution of the coordinates
1444+ * to be rendered.
1445+ */
1446+ protected double getXRes() {
1447+ return PS_XRES;
1448+ }
1449+ /**
1450+ * Return the y resolution of the coordinates
1451+ * to be rendered.
1452+ */
1453+ protected double getYRes() {
1454+ return PS_YRES;
1455+ }
1456+
1457+ /**
1458+ * For PostScript the origin is in the upper-left of the
1459+ * paper not at the imageable area corner.
1460+ */
1461+ protected double getPhysicalPrintableX(Paper p) {
1462+ return 0;
1463+
1464+ }
1465+
1466+ /**
1467+ * For PostScript the origin is in the upper-left of the
1468+ * paper not at the imageable area corner.
1469+ */
1470+ protected double getPhysicalPrintableY(Paper p) {
1471+ return 0;
1472+ }
1473+
1474+ protected double getPhysicalPrintableWidth(Paper p) {
1475+ return p.getImageableWidth();
1476+ }
1477+
1478+ protected double getPhysicalPrintableHeight(Paper p) {
1479+ return p.getImageableHeight();
1480+ }
1481+
1482+ protected double getPhysicalPageWidth(Paper p) {
1483+ return p.getWidth();
1484+ }
1485+
1486+ protected double getPhysicalPageHeight(Paper p) {
1487+ return p.getHeight();
1488+ }
1489+
1490+ /**
1491+ * Returns how many times each page in the book
1492+ * should be consecutively printed by PrintJob.
1493+ * If the printer makes copies itself then this
1494+ * method should return 1.
1495+ */
1496+ protected int getNoncollatedCopies() {
1497+ return 1;
1498+ }
1499+
1500+ protected int getCollatedCopies() {
1501+ return 1;
1502+ }
1503+
1504+ private String[] printExecCmd(String printer, String options,
1505+ boolean noJobSheet,
1506+ String banner, int copies, String spoolFile) {
1507+ int PRINTER = 0x1;
1508+ int OPTIONS = 0x2;
1509+ int BANNER = 0x4;
1510+ int COPIES = 0x8;
1511+ int NOSHEET = 0x10;
1512+ int pFlags = 0;
1513+ String execCmd[];
1514+ int ncomps = 2; // minimum number of print args
1515+ int n = 0;
1516+
1517+ if (printer != null && !printer.equals("") && !printer.equals("lp")) {
1518+ pFlags |= PRINTER;
1519+ ncomps+=1;
1520+ }
1521+ if (options != null && !options.equals("")) {
1522+ pFlags |= OPTIONS;
1523+ ncomps+=1;
1524+ }
1525+ if (banner != null && !banner.equals("")) {
1526+ pFlags |= BANNER;
1527+ ncomps+=1;
1528+ }
1529+ if (copies > 1) {
1530+ pFlags |= COPIES;
1531+ ncomps+=1;
1532+ }
1533+ if (noJobSheet) {
1534+ pFlags |= NOSHEET;
1535+ ncomps+=1;
1536+ }
1537+ if (System.getProperty("os.name").equals("Linux")) {
1538+ execCmd = new String[ncomps];
1539+ execCmd[n++] = "/usr/bin/lpr";
1540+ if ((pFlags & PRINTER) != 0) {
1541+ execCmd[n++] = "-P" + printer;
1542+ }
1543+ if ((pFlags & BANNER) != 0) {
1544+ execCmd[n++] = "-J" + banner;
1545+ }
1546+ if ((pFlags & COPIES) != 0) {
1547+ execCmd[n++] = "-#" + copies;
1548+ }
1549+ if ((pFlags & NOSHEET) != 0) {
1550+ execCmd[n++] = "-h";
1551+ }
1552+ if ((pFlags & OPTIONS) != 0) {
1553+ execCmd[n++] = new String(options);
1554+ }
1555+ } else {
1556+ ncomps+=1; //add 1 arg for lp
1557+ execCmd = new String[ncomps];
1558+ execCmd[n++] = "/usr/bin/lp";
1559+ execCmd[n++] = "-c"; // make a copy of the spool file
1560+ if ((pFlags & PRINTER) != 0) {
1561+ execCmd[n++] = "-d" + printer;
1562+ }
1563+ if ((pFlags & BANNER) != 0) {
1564+ execCmd[n++] = "-t" + banner;
1565+ }
1566+ if ((pFlags & COPIES) != 0) {
1567+ execCmd[n++] = "-n" + copies;
1568+ }
1569+ if ((pFlags & NOSHEET) != 0) {
1570+ execCmd[n++] = "-o nobanner";
1571+ }
1572+ if ((pFlags & OPTIONS) != 0) {
1573+ execCmd[n++] = "-o" + options;
1574+ }
1575+ }
1576+ execCmd[n++] = spoolFile;
1577+ return execCmd;
1578+ }
1579+
1580+ private static int swapBGRtoRGB(byte[] image, int index, byte[] dest) {
1581+ int destIndex = 0;
1582+ while(index < image.length-2 && destIndex < dest.length-2) {
1583+ dest[destIndex++] = image[index+2];
1584+ dest[destIndex++] = image[index+1];
1585+ dest[destIndex++] = image[index+0];
1586+ index+=3;
1587+ }
1588+ return index;
1589+ }
1590+
1591+ /*
1592+ * Currently CharToByteConverter.getCharacterEncoding() return values are
1593+ * not fixed yet. These are used as the part of the key of
1594+ * psfont.propeties. When those name are fixed this routine can
1595+ * be erased.
1596+ */
1597+ private String makeCharsetName(String name, char[] chs) {
1598+ if (name.equals("Cp1252") || name.equals("ISO8859_1")) {
1599+ return "latin1";
1600+ } else if (name.equals("UTF8")) {
1601+ // same as latin 1 if all chars < 256
1602+ for (int i=0; i < chs.length; i++) {
1603+ if (chs[i] > 255) {
1604+ return name.toLowerCase();
1605+ }
1606+ }
1607+ return "latin1";
1608+ } else if (name.startsWith("ISO8859")) {
1609+ // same as latin 1 if all chars < 128
1610+ for (int i=0; i < chs.length; i++) {
1611+ if (chs[i] > 127) {
1612+ return name.toLowerCase();
1613+ }
1614+ }
1615+ return "latin1";
1616+ } else {
1617+ return name.toLowerCase();
1618+ }
1619+ }
1620+
1621+ private void prepDrawing() {
1622+
1623+ /* Pop gstates until we can set the needed clip
1624+ * and transform or until we are at the outer most
1625+ * gstate.
1626+ */
1627+ while (isOuterGState() == false
1628+ && (getGState().canSetClip(mLastClip) == false
1629+ || getGState().mTransform.equals(mLastTransform) == false)) {
1630+
1631+
1632+ grestore();
1633+ }
1634+
1635+ /* Set the color. This can push the color to the
1636+ * outer most gsave which is often a good thing.
1637+ */
1638+ getGState().emitPSColor(mLastColor);
1639+
1640+ /* We do not want to change the outermost
1641+ * transform or clip so if we are at the
1642+ * outer clip the generate a gsave.
1643+ */
1644+ if (isOuterGState()) {
1645+ gsave();
1646+ getGState().emitTransform(mLastTransform);
1647+ getGState().emitPSClip(mLastClip);
1648+ }
1649+
1650+ /* Set the font if we have been asked to. It is
1651+ * important that the font is set after the
1652+ * transform in order to get the font size
1653+ * correct.
1654+ */
1655+// if (g != null) {
1656+// getGState().emitPSFont(g, mLastFont);
1657+// }
1658+
1659+ }
1660+
1661+ /**
1662+ * Return the GState that is currently on top
1663+ * of the GState stack. There should always be
1664+ * a GState on top of the stack. If there isn't
1665+ * then this method will throw an IndexOutOfBounds
1666+ * exception.
1667+ */
1668+ private GState getGState() {
1669+ int count = mGStateStack.size();
1670+ return (GState) mGStateStack.get(count - 1);
1671+ }
1672+
1673+ /**
1674+ * Emit a PostScript gsave command and add a
1675+ * new GState on to our stack which represents
1676+ * the printer's gstate stack.
1677+ */
1678+ private void gsave() {
1679+ GState oldGState = getGState();
1680+ mGStateStack.add(new GState(oldGState));
1681+ mPSStream.println(GSAVE_STR);
1682+ }
1683+
1684+ /**
1685+ * Emit a PostScript grestore command and remove
1686+ * a GState from our stack which represents the
1687+ * printer's gstate stack.
1688+ */
1689+ private void grestore() {
1690+ int count = mGStateStack.size();
1691+ mGStateStack.remove(count - 1);
1692+ mPSStream.println(GRESTORE_STR);
1693+ }
1694+
1695+ /**
1696+ * Return true if the current GState is the
1697+ * outermost GState and therefore should not
1698+ * be restored.
1699+ */
1700+ private boolean isOuterGState() {
1701+ return mGStateStack.size() == 1;
1702+ }
1703+
1704+ /**
1705+ * A stack of GStates is maintained to model the printer's
1706+ * gstate stack. Each GState holds information about
1707+ * the current graphics attributes.
1708+ */
1709+ private class GState{
1710+ Color mColor;
1711+ Shape mClip;
1712+ Font mFont;
1713+ AffineTransform mTransform;
1714+
1715+ GState() {
1716+ mColor = Color.black;
1717+ mClip = null;
1718+ mFont = null;
1719+ mTransform = new AffineTransform();
1720+ }
1721+
1722+ GState(GState copyGState) {
1723+ mColor = copyGState.mColor;
1724+ mClip = copyGState.mClip;
1725+ mFont = copyGState.mFont;
1726+ mTransform = copyGState.mTransform;
1727+ }
1728+
1729+ boolean canSetClip(Shape clip) {
1730+
1731+ return mClip == null || mClip.equals(clip);
1732+ }
1733+
1734+
1735+ void emitPSClip(Shape clip) {
1736+ if (clip != null
1737+ && (mClip == null || mClip.equals(clip) == false)) {
1738+ String saveFillOp = mFillOpStr;
1739+ String saveClipOp = mClipOpStr;
1740+ convertToPSPath(clip.getPathIterator(new AffineTransform()));
1741+ selectClipPath();
1742+ mClip = clip;
1743+ /* The clip is a shape and has reset the winding rule state */
1744+ mClipOpStr = saveFillOp;
1745+ mFillOpStr = saveFillOp;
1746+ }
1747+ }
1748+
1749+ void emitTransform(AffineTransform transform) {
1750+
1751+ if (transform != null && transform.equals(mTransform) == false) {
1752+ double[] matrix = new double[6];
1753+ transform.getMatrix(matrix);
1754+ mPSStream.println("[" + (float)matrix[0]
1755+ + " " + (float)matrix[1]
1756+ + " " + (float)matrix[2]
1757+ + " " + (float)matrix[3]
1758+ + " " + (float)matrix[4]
1759+ + " " + (float)matrix[5]
1760+ + "] concat");
1761+
1762+ mTransform = transform;
1763+ }
1764+ }
1765+
1766+ void emitPSColor(Color color) {
1767+ if (color != null && color.equals(mColor) == false) {
1768+ float[] rgb = color.getRGBColorComponents(null);
1769+
1770+ /* If the color is a gray value then use
1771+ * setgray.
1772+ */
1773+ if (rgb[0] == rgb[1] && rgb[1] == rgb[2]) {
1774+ mPSStream.println(rgb[0] + SETGRAY_STR);
1775+
1776+ /* It's not gray so use setrgbcolor.
1777+ */
1778+ } else {
1779+ mPSStream.println(rgb[0] + " "
1780+ + rgb[1] + " "
1781+ + rgb[2] + " "
1782+ + SETRGBCOLOR_STR);
1783+ }
1784+
1785+ mColor = color;
1786+
1787+ }
1788+ }
1789+
1790+ void emitPSFont(int psFontIndex, float fontSize) {
1791+ mPSStream.println(fontSize + " " +
1792+ psFontIndex + " " + SetFontName);
1793+ }
1794+ }
1795+
1796+ /**
1797+ * Given a Java2D <code>PathIterator</code> instance,
1798+ * this method translates that into a PostScript path..
1799+ */
1800+ void convertToPSPath(PathIterator pathIter) {
1801+
1802+ float[] segment = new float[6];
1803+ int segmentType;
1804+
1805+ /* Map the PathIterator's fill rule into the PostScript
1806+ * fill rule.
1807+ */
1808+ int fillRule;
1809+ if (pathIter.getWindingRule() == PathIterator.WIND_EVEN_ODD) {
1810+ fillRule = FILL_EVEN_ODD;
1811+ } else {
1812+ fillRule = FILL_WINDING;
1813+ }
1814+
1815+ beginPath();
1816+
1817+ setFillMode(fillRule);
1818+
1819+ while (pathIter.isDone() == false) {
1820+ segmentType = pathIter.currentSegment(segment);
1821+
1822+ switch (segmentType) {
1823+ case PathIterator.SEG_MOVETO:
1824+ moveTo(segment[0], segment[1]);
1825+ break;
1826+
1827+ case PathIterator.SEG_LINETO:
1828+ lineTo(segment[0], segment[1]);
1829+ break;
1830+
1831+ /* Convert the quad path to a bezier.
1832+ */
1833+ case PathIterator.SEG_QUADTO:
1834+ float lastX = getPenX();
1835+ float lastY = getPenY();
1836+ float c1x = lastX + (segment[0] - lastX) * 2 / 3;
1837+ float c1y = lastY + (segment[1] - lastY) * 2 / 3;
1838+ float c2x = segment[2] - (segment[2] - segment[0]) * 2/ 3;
1839+ float c2y = segment[3] - (segment[3] - segment[1]) * 2/ 3;
1840+ bezierTo(c1x, c1y,
1841+ c2x, c2y,
1842+ segment[2], segment[3]);
1843+ break;
1844+
1845+ case PathIterator.SEG_CUBICTO:
1846+ bezierTo(segment[0], segment[1],
1847+ segment[2], segment[3],
1848+ segment[4], segment[5]);
1849+ break;
1850+
1851+ case PathIterator.SEG_CLOSE:
1852+ closeSubpath();
1853+ break;
1854+ }
1855+
1856+
1857+ pathIter.next();
1858+ }
1859+ }
1860+
1861+ /*
1862+ * Fill the path defined by <code>pathIter</code>
1863+ * with the specified color.
1864+ * The path is provided in current user space.
1865+ */
1866+ protected void deviceFill(PathIterator pathIter, Color color,
1867+ AffineTransform tx, Shape clip) {
1868+
1869+ setTransform(tx);
1870+ setClip(clip);
1871+ setColor(color);
1872+ convertToPSPath(pathIter);
1873+ /* Specify the path to fill as the clip, this ensures that only
1874+ * pixels which are inside the path will be filled, which is
1875+ * what the Java 2D APIs specify
1876+ */
1877+ mPSStream.println(GSAVE_STR);
1878+ selectClipPath();
1879+ fillPath();
1880+ mPSStream.println(GRESTORE_STR + " " + NEWPATH_STR);
1881+ }
1882+
1883+ /*
1884+ * Run length encode byte array in a form suitable for decoding
1885+ * by the PS Level 2 filter RunLengthDecode.
1886+ * Array data to encode is inArr. Encoded data is written to outArr
1887+ * outArr must be long enough to hold the encoded data but this
1888+ * can't be known ahead of time.
1889+ * A safe assumption is to use double the length of the input array.
1890+ * This is then copied into a new array of the correct length which
1891+ * is returned.
1892+ * Algorithm:
1893+ * Encoding is a lead byte followed by data bytes.
1894+ * Lead byte of 0->127 indicates leadByte + 1 distinct bytes follow
1895+ * Lead byte of 129->255 indicates 257 - leadByte is the number of times
1896+ * the following byte is repeated in the source.
1897+ * 128 is a special lead byte indicating end of data (EOD) and is
1898+ * written as the final byte of the returned encoded data.
1899+ */
1900+ private byte[] rlEncode(byte[] inArr) {
1901+
1902+ int inIndex = 0;
1903+ int outIndex = 0;
1904+ int startIndex = 0;
1905+ int runLen = 0;
1906+ byte[] outArr = new byte[(inArr.length * 2) +2];
1907+ while (inIndex < inArr.length) {
1908+ if (runLen == 0) {
1909+ startIndex = inIndex++;
1910+ runLen=1;
1911+ }
1912+
1913+ while (runLen < 128 && inIndex < inArr.length &&
1914+ inArr[inIndex] == inArr[startIndex]) {
1915+ runLen++; // count run of same value
1916+ inIndex++;
1917+ }
1918+
1919+ if (runLen > 1) {
1920+ outArr[outIndex++] = (byte)(257 - runLen);
1921+ outArr[outIndex++] = inArr[startIndex];
1922+ runLen = 0;
1923+ continue; // back to top of while loop.
1924+ }
1925+
1926+ // if reach here have a run of different values, or at the end.
1927+ while (runLen < 128 && inIndex < inArr.length &&
1928+ inArr[inIndex] != inArr[inIndex-1]) {
1929+ runLen++; // count run of different values
1930+ inIndex++;
1931+ }
1932+ outArr[outIndex++] = (byte)(runLen - 1);
1933+ for (int i = startIndex; i < startIndex+runLen; i++) {
1934+ outArr[outIndex++] = inArr[i];
1935+ }
1936+ runLen = 0;
1937+ }
1938+ outArr[outIndex++] = (byte)128;
1939+ byte[] encodedData = new byte[outIndex];
1940+ System.arraycopy(outArr, 0, encodedData, 0, outIndex);
1941+
1942+ return encodedData;
1943+ }
1944+
1945+ /* written acc. to Adobe Spec. "Filtered Files: ASCIIEncode Filter",
1946+ * "PS Language Reference Manual, 2nd edition: Section 3.13"
1947+ */
1948+ private byte[] ascii85Encode(byte[] inArr) {
1949+ byte[] outArr = new byte[((inArr.length+4) * 5 / 4) + 2];
1950+ long p1 = 85;
1951+ long p2 = p1*p1;
1952+ long p3 = p1*p2;
1953+ long p4 = p1*p3;
1954+ byte pling = '!';
1955+
1956+ int i = 0;
1957+ int olen = 0;
1958+ long val, rem;
1959+
1960+ while (i+3 < inArr.length) {
1961+ val = ((long)((inArr[i++]&0xff))<<24) +
1962+ ((long)((inArr[i++]&0xff))<<16) +
1963+ ((long)((inArr[i++]&0xff))<< 8) +
1964+ ((long)(inArr[i++]&0xff));
1965+ if (val == 0) {
1966+ outArr[olen++] = 'z';
1967+ } else {
1968+ rem = val;
1969+ outArr[olen++] = (byte)(rem / p4 + pling); rem = rem % p4;
1970+ outArr[olen++] = (byte)(rem / p3 + pling); rem = rem % p3;
1971+ outArr[olen++] = (byte)(rem / p2 + pling); rem = rem % p2;
1972+ outArr[olen++] = (byte)(rem / p1 + pling); rem = rem % p1;
1973+ outArr[olen++] = (byte)(rem + pling);
1974+ }
1975+ }
1976+ // input not a multiple of 4 bytes, write partial output.
1977+ if (i < inArr.length) {
1978+ int n = inArr.length - i; // n bytes remain to be written
1979+
1980+ val = 0;
1981+ while (i < inArr.length) {
1982+ val = (val << 8) + (inArr[i++]&0xff);
1983+ }
1984+
1985+ int append = 4 - n;
1986+ while (append-- > 0) {
1987+ val = val << 8;
1988+ }
1989+ byte []c = new byte[5];
1990+ rem = val;
1991+ c[0] = (byte)(rem / p4 + pling); rem = rem % p4;
1992+ c[1] = (byte)(rem / p3 + pling); rem = rem % p3;
1993+ c[2] = (byte)(rem / p2 + pling); rem = rem % p2;
1994+ c[3] = (byte)(rem / p1 + pling); rem = rem % p1;
1995+ c[4] = (byte)(rem + pling);
1996+
1997+ for (int b = 0; b < n+1 ; b++) {
1998+ outArr[olen++] = c[b];
1999+ }
2000+ }
2001+
2002+ // write EOD marker.
2003+ outArr[olen++]='~'; outArr[olen++]='>';
2004+
2005+ /* The original intention was to insert a newline after every 78 bytes.
2006+ * This was mainly intended for legibility but I decided against this
2007+ * partially because of the (small) amount of extra space, and
2008+ * partially because for line breaks either would have to hardwire
2009+ * ascii 10 (newline) or calculate space in bytes to allocate for
2010+ * the platform's newline byte sequence. Also need to be careful
2011+ * about where its inserted:
2012+ * Ascii 85 decoder ignores white space except for one special case:
2013+ * you must ensure you do not split the EOD marker across lines.
2014+ */
2015+ byte[] retArr = new byte[olen];
2016+ System.arraycopy(outArr, 0, retArr, 0, olen);
2017+ return retArr;
2018+
2019+ }
2020+
2021+ /**
2022+ * PluginPrinter generates EPSF wrapped with a header and trailer
2023+ * comment. This conforms to the new requirements of Mozilla 1.7
2024+ * and FireFox 1.5 and later. Earlier versions of these browsers
2025+ * did not support plugin printing in the general sense (not just Java).
2026+ * A notable limitation of these browsers is that they handle plugins
2027+ * which would span page boundaries by scaling plugin content to fit on a
2028+ * single page. This means white space is left at the bottom of the
2029+ * previous page and its impossible to print these cases as they appear on
2030+ * the web page. This is contrast to how the same browsers behave on
2031+ * Windows where it renders as on-screen.
2032+ * Cases where the content fits on a single page do work fine, and they
2033+ * are the majority of cases.
2034+ * The scaling that the browser specifies to make the plugin content fit
2035+ * when it is larger than a single page can hold is non-uniform. It
2036+ * scales the axis in which the content is too large just enough to
2037+ * ensure it fits. For content which is extremely long this could lead
2038+ * to noticeable distortion. However that is probably rare enough that
2039+ * its not worth compensating for that here, but we can revisit that if
2040+ * needed, and compensate by making the scale for the other axis the
2041+ * same.
2042+ */
2043+ public static class PluginPrinter implements Printable {
2044+
2045+ private EPSPrinter epsPrinter;
2046+ private Component applet;
2047+ private PrintStream stream;
2048+ private String epsTitle;
2049+ private int bx, by, bw, bh;
2050+ private int width, height;
2051+
2052+ /**
2053+ * This is called from the Java Plug-in to print an Applet's
2054+ * contents as EPS to a postscript stream provided by the browser.
2055+ * @param applet the applet component to print.
2056+ * @param stream the print stream provided by the plug-in
2057+ * @param x the x location of the applet panel in the browser window
2058+ * @param y the y location of the applet panel in the browser window
2059+ * @param w the width of the applet panel in the browser window
2060+ * @param h the width of the applet panel in the browser window
2061+ */
2062+ public PluginPrinter(Component applet,
2063+ PrintStream stream,
2064+ int x, int y, int w, int h) {
2065+
2066+ this.applet = applet;
2067+ this.epsTitle = "Java Plugin Applet";
2068+ this.stream = stream;
2069+ bx = x;
2070+ by = y;
2071+ bw = w;
2072+ bh = h;
2073+ width = applet.size().width;
2074+ height = applet.size().height;
2075+ epsPrinter = new EPSPrinter(this, epsTitle, stream,
2076+ 0, 0, width, height);
2077+ }
2078+
2079+ public void printPluginPSHeader() {
2080+ stream.println("%%BeginDocument: JavaPluginApplet");
2081+ }
2082+
2083+ public void printPluginApplet() {
2084+ try {
2085+ epsPrinter.print();
2086+ } catch (PrinterException e) {
2087+ }
2088+ }
2089+
2090+ public void printPluginPSTrailer() {
2091+ stream.println("%%EndDocument: JavaPluginApplet");
2092+ stream.flush();
2093+ }
2094+
2095+ public void printAll() {
2096+ printPluginPSHeader();
2097+ printPluginApplet();
2098+ printPluginPSTrailer();
2099+ }
2100+
2101+ public int print(Graphics g, PageFormat pf, int pgIndex) {
2102+ if (pgIndex > 0) {
2103+ return Printable.NO_SUCH_PAGE;
2104+ } else {
2105+ // "aware" client code can detect that its been passed a
2106+ // PrinterGraphics and could theoretically print
2107+ // differently. I think this is more likely useful than
2108+ // a problem.
2109+ applet.printAll(g);
2110+ return Printable.PAGE_EXISTS;
2111+ }
2112+ }
2113+
2114+ }
2115+
2116+ /*
2117+ * This class can take an application-client supplied printable object
2118+ * and send the result to a stream.
2119+ * The application does not need to send any postscript to this stream
2120+ * unless it needs to specify a translation etc.
2121+ * It assumes that its importing application obeys all the conventions
2122+ * for importation of EPS. See Appendix H - Encapsulated Postscript File
2123+ * Format - of the Adobe Postscript Language Reference Manual, 2nd edition.
2124+ * This class could be used as the basis for exposing the ability to
2125+ * generate EPSF from 2D graphics as a StreamPrintService.
2126+ * In that case a MediaPrintableArea attribute could be used to
2127+ * communicate the bounding box.
2128+ */
2129+ public static class EPSPrinter implements Pageable {
2130+
2131+ private PageFormat pf;
2132+ private PSPrinterJob job;
2133+ private int llx, lly, urx, ury;
2134+ private Printable printable;
2135+ private PrintStream stream;
2136+ private String epsTitle;
2137+
2138+ public EPSPrinter(Printable printable, String title,
2139+ PrintStream stream,
2140+ int x, int y, int wid, int hgt) {
2141+
2142+ this.printable = printable;
2143+ this.epsTitle = title;
2144+ this.stream = stream;
2145+ llx = x;
2146+ lly = y;
2147+ urx = llx+wid;
2148+ ury = lly+hgt;
2149+ // construct a PageFormat with zero margins representing the
2150+ // exact bounds of the applet. ie construct a theoretical
2151+ // paper which happens to exactly match applet panel size.
2152+ Paper p = new Paper();
2153+ p.setSize((double)wid, (double)hgt);
2154+ p.setImageableArea(0.0,0.0, (double)wid, (double)hgt);
2155+ pf = new PageFormat();
2156+ pf.setPaper(p);
2157+ }
2158+
2159+ public void print() throws PrinterException {
2160+ stream.println("%!PS-Adobe-3.0 EPSF-3.0");
2161+ stream.println("%%BoundingBox: " +
2162+ llx + " " + lly + " " + urx + " " + ury);
2163+ stream.println("%%Title: " + epsTitle);
2164+ stream.println("%%Creator: Java Printing");
2165+ stream.println("%%CreationDate: " + new java.util.Date());
2166+ stream.println("%%EndComments");
2167+ stream.println("/pluginSave save def");
2168+ stream.println("mark"); // for restoring stack state on return
2169+
2170+ job = new PSPrinterJob();
2171+ job.epsPrinter = this; // modifies the behaviour of PSPrinterJob
2172+ job.mPSStream = stream;
2173+ job.mDestType = RasterPrinterJob.STREAM; // prevents closure
2174+
2175+ job.startDoc();
2176+ try {
2177+ job.printPage(this, 0);
2178+ } catch (Throwable t) {
2179+ if (t instanceof PrinterException) {
2180+ throw (PrinterException)t;
2181+ } else {
2182+ throw new PrinterException(t.toString());
2183+ }
2184+ } finally {
2185+ stream.println("cleartomark"); // restore stack state
2186+ stream.println("pluginSave restore");
2187+ job.endDoc();
2188+ }
2189+ stream.flush();
2190+ }
2191+
2192+ public int getNumberOfPages() {
2193+ return 1;
2194+ }
2195+
2196+ public PageFormat getPageFormat(int pgIndex) {
2197+ if (pgIndex > 0) {
2198+ throw new IndexOutOfBoundsException("pgIndex");
2199+ } else {
2200+ return pf;
2201+ }
2202+ }
2203+
2204+ public Printable getPrintable(int pgIndex) {
2205+ if (pgIndex > 0) {
2206+ throw new IndexOutOfBoundsException("pgIndex");
2207+ } else {
2208+ return printable;
2209+ }
2210+ }
2211+
2212+ }
2213+}