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
26package sun.print;
27
28import java.awt.Color;
29import java.awt.Component;
30import java.awt.Font;
31import java.awt.FontMetrics;
32import java.awt.GraphicsEnvironment;
33import java.awt.Graphics;
34import java.awt.Graphics2D;
35import java.awt.HeadlessException;
36import java.awt.Rectangle;
37import java.awt.Shape;
38
39import java.awt.image.BufferedImage;
40
41import java.awt.font.FontRenderContext;
42
43import java.awt.geom.AffineTransform;
44import java.awt.geom.PathIterator;
45import java.awt.geom.Rectangle2D;
46
47import java.awt.image.BufferedImage;
48
49import java.awt.print.Pageable;
50import java.awt.print.PageFormat;
51import java.awt.print.Paper;
52import java.awt.print.Printable;
53import java.awt.print.PrinterException;
54import java.awt.print.PrinterIOException;
55import java.awt.print.PrinterJob;
56
57import javax.print.DocFlavor;
58import javax.print.PrintService;
59import javax.print.StreamPrintService;
60import javax.print.attribute.HashPrintRequestAttributeSet;
61import javax.print.attribute.PrintRequestAttributeSet;
62import javax.print.attribute.standard.Chromaticity;
63import javax.print.attribute.standard.Copies;
64import javax.print.attribute.standard.Destination;
65import javax.print.attribute.standard.DialogTypeSelection;
66import javax.print.attribute.standard.JobName;
67import javax.print.attribute.standard.Sides;
68
69import java.io.BufferedInputStream;
70import java.io.BufferedOutputStream;
71import java.io.CharConversionException;
72import java.io.File;
73import java.io.InputStream;
74import java.io.IOException;
75import java.io.FileInputStream;
76import java.io.FileOutputStream;
77import java.io.OutputStream;
78import java.io.PrintStream;
79
80import java.util.ArrayList;
81import java.util.Enumeration;
82import java.util.Locale;
83import java.util.Properties;
84
85import sun.awt.CharsetString;
86import sun.awt.FontConfiguration;
87import sun.awt.FontDescriptor;
88import sun.awt.PlatformFont;
89import sun.awt.SunToolkit;
90import sun.font.FontManagerFactory;
91import sun.font.FontUtilities;
92
93import java.nio.charset.*;
94import java.nio.CharBuffer;
95import java.nio.ByteBuffer;
96
97//REMIND: Remove use of this class when IPPPrintService is moved to share directory.
98import java.lang.reflect.Method;
99
100/**
101 * A class which initiates and executes a PostScript printer job.
102 *
103 * @author Richard Blanchard
104 */
105public 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}