PageRenderTime 230ms CodeModel.GetById 47ms RepoModel.GetById 10ms app.codeStats 1ms

/src/share/classes/sun/print/PSPrinterJob.java

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