PageRenderTime 159ms CodeModel.GetById 8ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://bitbucket.org/screenconnect/openjdk8-jdk
Java | 2266 lines | 1363 code | 309 blank | 594 comment | 266 complexity | b84dc4e41a2b121bfcd3aca3728e2743 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * Copyright (c) 1998, 2013, 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.PrintServiceAttributeSet;
  55. import javax.print.attribute.standard.PrinterName;
  56. import javax.print.attribute.standard.Chromaticity;
  57. import javax.print.attribute.standard.Copies;
  58. import javax.print.attribute.standard.Destination;
  59. import javax.print.attribute.standard.DialogTypeSelection;
  60. import javax.print.attribute.standard.JobName;
  61. import javax.print.attribute.standard.Sides;
  62. import java.io.BufferedInputStream;
  63. import java.io.BufferedOutputStream;
  64. import java.io.BufferedReader;
  65. import java.io.CharConversionException;
  66. import java.io.File;
  67. import java.io.InputStream;
  68. import java.io.InputStreamReader;
  69. import java.io.IOException;
  70. import java.io.FileInputStream;
  71. import java.io.FileOutputStream;
  72. import java.io.OutputStream;
  73. import java.io.PrintStream;
  74. import java.io.PrintWriter;
  75. import java.io.StringWriter;
  76. import java.util.ArrayList;
  77. import java.util.Enumeration;
  78. import java.util.Locale;
  79. import java.util.Properties;
  80. import sun.awt.CharsetString;
  81. import sun.awt.FontConfiguration;
  82. import sun.awt.FontDescriptor;
  83. import sun.awt.PlatformFont;
  84. import sun.awt.SunToolkit;
  85. import sun.font.FontManagerFactory;
  86. import sun.font.FontUtilities;
  87. import java.nio.charset.*;
  88. import java.nio.CharBuffer;
  89. import java.nio.ByteBuffer;
  90. import java.nio.file.Files;
  91. //REMIND: Remove use of this class when IPPPrintService is moved to share directory.
  92. import java.lang.reflect.Method;
  93. /**
  94. * A class which initiates and executes a PostScript printer job.
  95. *
  96. * @author Richard Blanchard
  97. */
  98. public class PSPrinterJob extends RasterPrinterJob {
  99. /* Class Constants */
  100. /**
  101. * Passed to the <code>setFillMode</code>
  102. * method this value forces fills to be
  103. * done using the even-odd fill rule.
  104. */
  105. protected static final int FILL_EVEN_ODD = 1;
  106. /**
  107. * Passed to the <code>setFillMode</code>
  108. * method this value forces fills to be
  109. * done using the non-zero winding rule.
  110. */
  111. protected static final int FILL_WINDING = 2;
  112. /* PostScript has a 64K maximum on its strings.
  113. */
  114. private static final int MAX_PSSTR = (1024 * 64 - 1);
  115. private static final int RED_MASK = 0x00ff0000;
  116. private static final int GREEN_MASK = 0x0000ff00;
  117. private static final int BLUE_MASK = 0x000000ff;
  118. private static final int RED_SHIFT = 16;
  119. private static final int GREEN_SHIFT = 8;
  120. private static final int BLUE_SHIFT = 0;
  121. private static final int LOWNIBBLE_MASK = 0x0000000f;
  122. private static final int HINIBBLE_MASK = 0x000000f0;
  123. private static final int HINIBBLE_SHIFT = 4;
  124. private static final byte hexDigits[] = {
  125. (byte)'0', (byte)'1', (byte)'2', (byte)'3',
  126. (byte)'4', (byte)'5', (byte)'6', (byte)'7',
  127. (byte)'8', (byte)'9', (byte)'A', (byte)'B',
  128. (byte)'C', (byte)'D', (byte)'E', (byte)'F'
  129. };
  130. private static final int PS_XRES = 300;
  131. private static final int PS_YRES = 300;
  132. private static final String ADOBE_PS_STR = "%!PS-Adobe-3.0";
  133. private static final String EOF_COMMENT = "%%EOF";
  134. private static final String PAGE_COMMENT = "%%Page: ";
  135. private static final String READIMAGEPROC = "/imStr 0 def /imageSrc " +
  136. "{currentfile /ASCII85Decode filter /RunLengthDecode filter " +
  137. " imStr readstring pop } def";
  138. private static final String COPIES = "/#copies exch def";
  139. private static final String PAGE_SAVE = "/pgSave save def";
  140. private static final String PAGE_RESTORE = "pgSave restore";
  141. private static final String SHOWPAGE = "showpage";
  142. private static final String IMAGE_SAVE = "/imSave save def";
  143. private static final String IMAGE_STR = " string /imStr exch def";
  144. private static final String IMAGE_RESTORE = "imSave restore";
  145. private static final String COORD_PREP = " 0 exch translate "
  146. + "1 -1 scale"
  147. + "[72 " + PS_XRES + " div "
  148. + "0 0 "
  149. + "72 " + PS_YRES + " div "
  150. + "0 0]concat";
  151. private static final String SetFontName = "F";
  152. private static final String DrawStringName = "S";
  153. /**
  154. * The PostScript invocation to fill a path using the
  155. * even-odd rule. (eofill)
  156. */
  157. private static final String EVEN_ODD_FILL_STR = "EF";
  158. /**
  159. * The PostScript invocation to fill a path using the
  160. * non-zero winding rule. (fill)
  161. */
  162. private static final String WINDING_FILL_STR = "WF";
  163. /**
  164. * The PostScript to set the clip to be the current path
  165. * using the even odd rule. (eoclip)
  166. */
  167. private static final String EVEN_ODD_CLIP_STR = "EC";
  168. /**
  169. * The PostScript to set the clip to be the current path
  170. * using the non-zero winding rule. (clip)
  171. */
  172. private static final String WINDING_CLIP_STR = "WC";
  173. /**
  174. * Expecting two numbers on the PostScript stack, this
  175. * invocation moves the current pen position. (moveto)
  176. */
  177. private static final String MOVETO_STR = " M";
  178. /**
  179. * Expecting two numbers on the PostScript stack, this
  180. * invocation draws a PS line from the current pen
  181. * position to the point on the stack. (lineto)
  182. */
  183. private static final String LINETO_STR = " L";
  184. /**
  185. * This PostScript operator takes two control points
  186. * and an ending point and using the current pen
  187. * position as a starting point adds a bezier
  188. * curve to the current path. (curveto)
  189. */
  190. private static final String CURVETO_STR = " C";
  191. /**
  192. * The PostScript to pop a state off of the printer's
  193. * gstate stack. (grestore)
  194. */
  195. private static final String GRESTORE_STR = "R";
  196. /**
  197. * The PostScript to push a state on to the printer's
  198. * gstate stack. (gsave)
  199. */
  200. private static final String GSAVE_STR = "G";
  201. /**
  202. * Make the current PostScript path an empty path. (newpath)
  203. */
  204. private static final String NEWPATH_STR = "N";
  205. /**
  206. * Close the current subpath by generating a line segment
  207. * from the current position to the start of the subpath. (closepath)
  208. */
  209. private static final String CLOSEPATH_STR = "P";
  210. /**
  211. * Use the three numbers on top of the PS operator
  212. * stack to set the rgb color. (setrgbcolor)
  213. */
  214. private static final String SETRGBCOLOR_STR = " SC";
  215. /**
  216. * Use the top number on the stack to set the printer's
  217. * current gray value. (setgray)
  218. */
  219. private static final String SETGRAY_STR = " SG";
  220. /* Instance Variables */
  221. private int mDestType;
  222. private String mDestination = "lp";
  223. private boolean mNoJobSheet = false;
  224. private String mOptions;
  225. private Font mLastFont;
  226. private Color mLastColor;
  227. private Shape mLastClip;
  228. private AffineTransform mLastTransform;
  229. /* non-null if printing EPS for Java Plugin */
  230. private EPSPrinter epsPrinter = null;
  231. /**
  232. * The metrics for the font currently set.
  233. */
  234. FontMetrics mCurMetrics;
  235. /**
  236. * The output stream to which the generated PostScript
  237. * is written.
  238. */
  239. PrintStream mPSStream;
  240. /* The temporary file to which we spool before sending to the printer */
  241. File spoolFile;
  242. /**
  243. * This string holds the PostScript operator to
  244. * be used to fill a path. It can be changed
  245. * by the <code>setFillMode</code> method.
  246. */
  247. private String mFillOpStr = WINDING_FILL_STR;
  248. /**
  249. * This string holds the PostScript operator to
  250. * be used to clip to a path. It can be changed
  251. * by the <code>setFillMode</code> method.
  252. */
  253. private String mClipOpStr = WINDING_CLIP_STR;
  254. /**
  255. * A stack that represents the PostScript gstate stack.
  256. */
  257. ArrayList mGStateStack = new ArrayList();
  258. /**
  259. * The x coordinate of the current pen position.
  260. */
  261. private float mPenX;
  262. /**
  263. * The y coordinate of the current pen position.
  264. */
  265. private float mPenY;
  266. /**
  267. * The x coordinate of the starting point of
  268. * the current subpath.
  269. */
  270. private float mStartPathX;
  271. /**
  272. * The y coordinate of the starting point of
  273. * the current subpath.
  274. */
  275. private float mStartPathY;
  276. /**
  277. * An optional mapping of fonts to PostScript names.
  278. */
  279. private static Properties mFontProps = null;
  280. private static boolean isMac;
  281. /* Class static initialiser block */
  282. static {
  283. //enable priviledges so initProps can access system properties,
  284. // open the property file, etc.
  285. java.security.AccessController.doPrivileged(
  286. new java.security.PrivilegedAction() {
  287. public Object run() {
  288. mFontProps = initProps();
  289. String osName = System.getProperty("os.name");
  290. isMac = osName.startsWith("Mac");
  291. return null;
  292. }
  293. });
  294. }
  295. /*
  296. * Initialize PostScript font properties.
  297. * Copied from PSPrintStream
  298. */
  299. private static Properties initProps() {
  300. // search psfont.properties for fonts
  301. // and create and initialize fontProps if it exist.
  302. String jhome = System.getProperty("java.home");
  303. if (jhome != null){
  304. String ulocale = SunToolkit.getStartupLocale().getLanguage();
  305. try {
  306. File f = new File(jhome + File.separator +
  307. "lib" + File.separator +
  308. "psfontj2d.properties." + ulocale);
  309. if (!f.canRead()){
  310. f = new File(jhome + File.separator +
  311. "lib" + File.separator +
  312. "psfont.properties." + ulocale);
  313. if (!f.canRead()){
  314. f = new File(jhome + File.separator + "lib" +
  315. File.separator + "psfontj2d.properties");
  316. if (!f.canRead()){
  317. f = new File(jhome + File.separator + "lib" +
  318. File.separator + "psfont.properties");
  319. if (!f.canRead()){
  320. return (Properties)null;
  321. }
  322. }
  323. }
  324. }
  325. // Load property file
  326. InputStream in =
  327. new BufferedInputStream(new FileInputStream(f.getPath()));
  328. Properties props = new Properties();
  329. props.load(in);
  330. in.close();
  331. return props;
  332. } catch (Exception e){
  333. return (Properties)null;
  334. }
  335. }
  336. return (Properties)null;
  337. }
  338. /* Constructors */
  339. public PSPrinterJob()
  340. {
  341. }
  342. /* Instance Methods */
  343. /**
  344. * Presents the user a dialog for changing properties of the
  345. * print job interactively.
  346. * @returns false if the user cancels the dialog and
  347. * true otherwise.
  348. * @exception HeadlessException if GraphicsEnvironment.isHeadless()
  349. * returns true.
  350. * @see java.awt.GraphicsEnvironment#isHeadless
  351. */
  352. public boolean printDialog() throws HeadlessException {
  353. if (GraphicsEnvironment.isHeadless()) {
  354. throw new HeadlessException();
  355. }
  356. if (attributes == null) {
  357. attributes = new HashPrintRequestAttributeSet();
  358. }
  359. attributes.add(new Copies(getCopies()));
  360. attributes.add(new JobName(getJobName(), null));
  361. boolean doPrint = false;
  362. DialogTypeSelection dts =
  363. (DialogTypeSelection)attributes.get(DialogTypeSelection.class);
  364. if (dts == DialogTypeSelection.NATIVE) {
  365. // Remove DialogTypeSelection.NATIVE to prevent infinite loop in
  366. // RasterPrinterJob.
  367. attributes.remove(DialogTypeSelection.class);
  368. doPrint = printDialog(attributes);
  369. // restore attribute
  370. attributes.add(DialogTypeSelection.NATIVE);
  371. } else {
  372. doPrint = printDialog(attributes);
  373. }
  374. if (doPrint) {
  375. JobName jobName = (JobName)attributes.get(JobName.class);
  376. if (jobName != null) {
  377. setJobName(jobName.getValue());
  378. }
  379. Copies copies = (Copies)attributes.get(Copies.class);
  380. if (copies != null) {
  381. setCopies(copies.getValue());
  382. }
  383. Destination dest = (Destination)attributes.get(Destination.class);
  384. if (dest != null) {
  385. try {
  386. mDestType = RasterPrinterJob.FILE;
  387. mDestination = (new File(dest.getURI())).getPath();
  388. } catch (Exception e) {
  389. mDestination = "out.ps";
  390. }
  391. } else {
  392. mDestType = RasterPrinterJob.PRINTER;
  393. PrintService pServ = getPrintService();
  394. if (pServ != null) {
  395. mDestination = pServ.getName();
  396. if (isMac) {
  397. PrintServiceAttributeSet psaSet = pServ.getAttributes() ;
  398. if (psaSet != null) {
  399. mDestination = psaSet.get(PrinterName.class).toString();
  400. }
  401. }
  402. }
  403. }
  404. }
  405. return doPrint;
  406. }
  407. /**
  408. * Invoked by the RasterPrinterJob super class
  409. * this method is called to mark the start of a
  410. * document.
  411. */
  412. protected void startDoc() throws PrinterException {
  413. // A security check has been performed in the
  414. // java.awt.print.printerJob.getPrinterJob method.
  415. // We use an inner class to execute the privilged open operations.
  416. // Note that we only open a file if it has been nominated by
  417. // the end-user in a dialog that we ouselves put up.
  418. OutputStream output;
  419. if (epsPrinter == null) {
  420. if (getPrintService() instanceof PSStreamPrintService) {
  421. StreamPrintService sps = (StreamPrintService)getPrintService();
  422. mDestType = RasterPrinterJob.STREAM;
  423. if (sps.isDisposed()) {
  424. throw new PrinterException("service is disposed");
  425. }
  426. output = sps.getOutputStream();
  427. if (output == null) {
  428. throw new PrinterException("Null output stream");
  429. }
  430. } else {
  431. /* REMIND: This needs to be more maintainable */
  432. mNoJobSheet = super.noJobSheet;
  433. if (super.destinationAttr != null) {
  434. mDestType = RasterPrinterJob.FILE;
  435. mDestination = super.destinationAttr;
  436. }
  437. if (mDestType == RasterPrinterJob.FILE) {
  438. try {
  439. spoolFile = new File(mDestination);
  440. output = new FileOutputStream(spoolFile);
  441. } catch (IOException ex) {
  442. throw new PrinterIOException(ex);
  443. }
  444. } else {
  445. PrinterOpener po = new PrinterOpener();
  446. java.security.AccessController.doPrivileged(po);
  447. if (po.pex != null) {
  448. throw po.pex;
  449. }
  450. output = po.result;
  451. }
  452. }
  453. mPSStream = new PrintStream(new BufferedOutputStream(output));
  454. mPSStream.println(ADOBE_PS_STR);
  455. }
  456. mPSStream.println("%%BeginProlog");
  457. mPSStream.println(READIMAGEPROC);
  458. mPSStream.println("/BD {bind def} bind def");
  459. mPSStream.println("/D {def} BD");
  460. mPSStream.println("/C {curveto} BD");
  461. mPSStream.println("/L {lineto} BD");
  462. mPSStream.println("/M {moveto} BD");
  463. mPSStream.println("/R {grestore} BD");
  464. mPSStream.println("/G {gsave} BD");
  465. mPSStream.println("/N {newpath} BD");
  466. mPSStream.println("/P {closepath} BD");
  467. mPSStream.println("/EC {eoclip} BD");
  468. mPSStream.println("/WC {clip} BD");
  469. mPSStream.println("/EF {eofill} BD");
  470. mPSStream.println("/WF {fill} BD");
  471. mPSStream.println("/SG {setgray} BD");
  472. mPSStream.println("/SC {setrgbcolor} BD");
  473. mPSStream.println("/ISOF {");
  474. mPSStream.println(" dup findfont dup length 1 add dict begin {");
  475. mPSStream.println(" 1 index /FID eq {pop pop} {D} ifelse");
  476. mPSStream.println(" } forall /Encoding ISOLatin1Encoding D");
  477. mPSStream.println(" currentdict end definefont");
  478. mPSStream.println("} BD");
  479. mPSStream.println("/NZ {dup 1 lt {pop 1} if} BD");
  480. /* The following procedure takes args: string, x, y, desiredWidth.
  481. * It calculates using stringwidth the width of the string in the
  482. * current font and subtracts it from the desiredWidth and divides
  483. * this by stringLen-1. This gives us a per-glyph adjustment in
  484. * the spacing needed (either +ve or -ve) to make the string
  485. * print at the desiredWidth. The ashow procedure call takes this
  486. * per-glyph adjustment as an argument. This is necessary for WYSIWYG
  487. */
  488. mPSStream.println("/"+DrawStringName +" {");
  489. mPSStream.println(" moveto 1 index stringwidth pop NZ sub");
  490. mPSStream.println(" 1 index length 1 sub NZ div 0");
  491. mPSStream.println(" 3 2 roll ashow newpath} BD");
  492. mPSStream.println("/FL [");
  493. if (mFontProps == null){
  494. mPSStream.println(" /Helvetica ISOF");
  495. mPSStream.println(" /Helvetica-Bold ISOF");
  496. mPSStream.println(" /Helvetica-Oblique ISOF");
  497. mPSStream.println(" /Helvetica-BoldOblique ISOF");
  498. mPSStream.println(" /Times-Roman ISOF");
  499. mPSStream.println(" /Times-Bold ISOF");
  500. mPSStream.println(" /Times-Italic ISOF");
  501. mPSStream.println(" /Times-BoldItalic ISOF");
  502. mPSStream.println(" /Courier ISOF");
  503. mPSStream.println(" /Courier-Bold ISOF");
  504. mPSStream.println(" /Courier-Oblique ISOF");
  505. mPSStream.println(" /Courier-BoldOblique ISOF");
  506. } else {
  507. int cnt = Integer.parseInt(mFontProps.getProperty("font.num", "9"));
  508. for (int i = 0; i < cnt; i++){
  509. mPSStream.println(" /" + mFontProps.getProperty
  510. ("font." + String.valueOf(i), "Courier ISOF"));
  511. }
  512. }
  513. mPSStream.println("] D");
  514. mPSStream.println("/"+SetFontName +" {");
  515. mPSStream.println(" FL exch get exch scalefont");
  516. mPSStream.println(" [1 0 0 -1 0 0] makefont setfont} BD");
  517. mPSStream.println("%%EndProlog");
  518. mPSStream.println("%%BeginSetup");
  519. if (epsPrinter == null) {
  520. // Set Page Size using first page's format.
  521. PageFormat pageFormat = getPageable().getPageFormat(0);
  522. double paperHeight = pageFormat.getPaper().getHeight();
  523. double paperWidth = pageFormat.getPaper().getWidth();
  524. /* PostScript printers can always generate uncollated copies.
  525. */
  526. mPSStream.print("<< /PageSize [" +
  527. paperWidth + " "+ paperHeight+"]");
  528. final PrintService pservice = getPrintService();
  529. Boolean isPS = (Boolean)java.security.AccessController.doPrivileged(
  530. new java.security.PrivilegedAction() {
  531. public Object run() {
  532. try {
  533. Class psClass = Class.forName("sun.print.IPPPrintService");
  534. if (psClass.isInstance(pservice)) {
  535. Method isPSMethod = psClass.getMethod("isPostscript",
  536. (Class[])null);
  537. return (Boolean)isPSMethod.invoke(pservice, (Object[])null);
  538. }
  539. } catch (Throwable t) {
  540. }
  541. return Boolean.TRUE;
  542. }
  543. }
  544. );
  545. if (isPS) {
  546. mPSStream.print(" /DeferredMediaSelection true");
  547. }
  548. mPSStream.print(" /ImagingBBox null /ManualFeed false");
  549. mPSStream.print(isCollated() ? " /Collate true":"");
  550. mPSStream.print(" /NumCopies " +getCopiesInt());
  551. if (sidesAttr != Sides.ONE_SIDED) {
  552. if (sidesAttr == Sides.TWO_SIDED_LONG_EDGE) {
  553. mPSStream.print(" /Duplex true ");
  554. } else if (sidesAttr == Sides.TWO_SIDED_SHORT_EDGE) {
  555. mPSStream.print(" /Duplex true /Tumble true ");
  556. }
  557. }
  558. mPSStream.println(" >> setpagedevice ");
  559. }
  560. mPSStream.println("%%EndSetup");
  561. }
  562. // Inner class to run "privileged" to open the printer output stream.
  563. private class PrinterOpener implements java.security.PrivilegedAction {
  564. PrinterException pex;
  565. OutputStream result;
  566. public Object run() {
  567. try {
  568. /* Write to a temporary file which will be spooled to
  569. * the printer then deleted. In the case that the file
  570. * is not removed for some reason, request that it is
  571. * removed when the VM exits.
  572. */
  573. spoolFile = Files.createTempFile("javaprint", ".ps").toFile();
  574. spoolFile.deleteOnExit();
  575. result = new FileOutputStream(spoolFile);
  576. return result;
  577. } catch (IOException ex) {
  578. // If there is an IOError we subvert it to a PrinterException.
  579. pex = new PrinterIOException(ex);
  580. }
  581. return null;
  582. }
  583. }
  584. // Inner class to run "privileged" to invoke the system print command
  585. private class PrinterSpooler implements java.security.PrivilegedAction {
  586. PrinterException pex;
  587. private void handleProcessFailure(final Process failedProcess,
  588. final String[] execCmd, final int result) throws IOException {
  589. try (StringWriter sw = new StringWriter();
  590. PrintWriter pw = new PrintWriter(sw)) {
  591. pw.append("error=").append(Integer.toString(result));
  592. pw.append(" running:");
  593. for (String arg: execCmd) {
  594. pw.append(" '").append(arg).append("'");
  595. }
  596. try (InputStream is = failedProcess.getErrorStream();
  597. InputStreamReader isr = new InputStreamReader(is);
  598. BufferedReader br = new BufferedReader(isr)) {
  599. while (br.ready()) {
  600. pw.println();
  601. pw.append("\t\t").append(br.readLine());
  602. }
  603. } finally {
  604. pw.flush();
  605. throw new IOException(sw.toString());
  606. }
  607. }
  608. }
  609. public Object run() {
  610. if (spoolFile == null || !spoolFile.exists()) {
  611. pex = new PrinterException("No spool file");
  612. return null;
  613. }
  614. try {
  615. /**
  616. * Spool to the printer.
  617. */
  618. String fileName = spoolFile.getAbsolutePath();
  619. String execCmd[] = printExecCmd(mDestination, mOptions,
  620. mNoJobSheet, getJobNameInt(),
  621. 1, fileName);
  622. Process process = Runtime.getRuntime().exec(execCmd);
  623. process.waitFor();
  624. final int result = process.exitValue();
  625. if (0 != result) {
  626. handleProcessFailure(process, execCmd, result);
  627. }
  628. } catch (IOException ex) {
  629. pex = new PrinterIOException(ex);
  630. } catch (InterruptedException ie) {
  631. pex = new PrinterException(ie.toString());
  632. } finally {
  633. spoolFile.delete();
  634. }
  635. return null;
  636. }
  637. }
  638. /**
  639. * Invoked if the application cancelled the printjob.
  640. */
  641. protected void abortDoc() {
  642. if (mPSStream != null && mDestType != RasterPrinterJob.STREAM) {
  643. mPSStream.close();
  644. }
  645. java.security.AccessController.doPrivileged(
  646. new java.security.PrivilegedAction() {
  647. public Object run() {
  648. if (spoolFile != null && spoolFile.exists()) {
  649. spoolFile.delete();
  650. }
  651. return null;
  652. }
  653. });
  654. }
  655. /**
  656. * Invoked by the RasterPrintJob super class
  657. * this method is called after that last page
  658. * has been imaged.
  659. */
  660. protected void endDoc() throws PrinterException {
  661. if (mPSStream != null) {
  662. mPSStream.println(EOF_COMMENT);
  663. mPSStream.flush();
  664. if (mDestType != RasterPrinterJob.STREAM) {
  665. mPSStream.close();
  666. }
  667. }
  668. if (mDestType == RasterPrinterJob.PRINTER) {
  669. PrintService pServ = getPrintService();
  670. if (pServ != null) {
  671. mDestination = pServ.getName();
  672. if (isMac) {
  673. PrintServiceAttributeSet psaSet = pServ.getAttributes();
  674. if (psaSet != null) {
  675. mDestination = psaSet.get(PrinterName.class).toString() ;
  676. }
  677. }
  678. }
  679. PrinterSpooler spooler = new PrinterSpooler();
  680. java.security.AccessController.doPrivileged(spooler);
  681. if (spooler.pex != null) {
  682. throw spooler.pex;
  683. }
  684. }
  685. }
  686. /**
  687. * The RasterPrintJob super class calls this method
  688. * at the start of each page.
  689. */
  690. protected void startPage(PageFormat pageFormat, Printable painter,
  691. int index, boolean paperChanged)
  692. throws PrinterException
  693. {
  694. double paperHeight = pageFormat.getPaper().getHeight();
  695. double paperWidth = pageFormat.getPaper().getWidth();
  696. int pageNumber = index + 1;
  697. /* Place an initial gstate on to our gstate stack.
  698. * It will have the default PostScript gstate
  699. * attributes.
  700. */
  701. mGStateStack = new ArrayList();
  702. mGStateStack.add(new GState());
  703. mPSStream.println(PAGE_COMMENT + pageNumber + " " + pageNumber);
  704. /* Check current page's pageFormat against the previous pageFormat,
  705. */
  706. if (index > 0 && paperChanged) {
  707. mPSStream.print("<< /PageSize [" +
  708. paperWidth + " " + paperHeight + "]");
  709. final PrintService pservice = getPrintService();
  710. Boolean isPS =
  711. (Boolean)java.security.AccessController.doPrivileged(
  712. new java.security.PrivilegedAction() {
  713. public Object run() {
  714. try {
  715. Class psClass =
  716. Class.forName("sun.print.IPPPrintService");
  717. if (psClass.isInstance(pservice)) {
  718. Method isPSMethod =
  719. psClass.getMethod("isPostscript",
  720. (Class[])null);
  721. return (Boolean)
  722. isPSMethod.invoke(pservice,
  723. (Object[])null);
  724. }
  725. } catch (Throwable t) {
  726. }
  727. return Boolean.TRUE;
  728. }
  729. }
  730. );
  731. if (isPS) {
  732. mPSStream.print(" /DeferredMediaSelection true");
  733. }
  734. mPSStream.println(" >> setpagedevice");
  735. }
  736. mPSStream.println(PAGE_SAVE);
  737. mPSStream.println(paperHeight + COORD_PREP);
  738. }
  739. /**
  740. * The RastePrintJob super class calls this method
  741. * at the end of each page.
  742. */
  743. protected void endPage(PageFormat format, Printable painter,
  744. int index)
  745. throws PrinterException
  746. {
  747. mPSStream.println(PAGE_RESTORE);
  748. mPSStream.println(SHOWPAGE);
  749. }
  750. /**
  751. * Convert the 24 bit BGR image buffer represented by
  752. * <code>image</code> to PostScript. The image is drawn at
  753. * <code>(destX, destY)</code> in device coordinates.
  754. * The image is scaled into a square of size
  755. * specified by <code>destWidth</code> and
  756. * <code>destHeight</code>. The portion of the
  757. * source image copied into that square is specified
  758. * by <code>srcX</code>, <code>srcY</code>,
  759. * <code>srcWidth</code>, and srcHeight.
  760. */
  761. protected void drawImageBGR(byte[] bgrData,
  762. float destX, float destY,
  763. float destWidth, float destHeight,
  764. float srcX, float srcY,
  765. float srcWidth, float srcHeight,
  766. int srcBitMapWidth, int srcBitMapHeight) {
  767. /* We draw images at device resolution so we probably need
  768. * to change the current PostScript transform.
  769. */
  770. setTransform(new AffineTransform());
  771. prepDrawing();
  772. int intSrcWidth = (int) srcWidth;
  773. int intSrcHeight = (int) srcHeight;
  774. mPSStream.println(IMAGE_SAVE);
  775. /* Create a PS string big enough to hold a row of pixels.
  776. */
  777. int psBytesPerRow = 3 * (int) intSrcWidth;
  778. while (psBytesPerRow > MAX_PSSTR) {
  779. psBytesPerRow /= 2;
  780. }
  781. mPSStream.println(psBytesPerRow + IMAGE_STR);
  782. /* Scale and translate the unit image.
  783. */
  784. mPSStream.println("[" + destWidth + " 0 "
  785. + "0 " + destHeight
  786. + " " + destX + " " + destY
  787. +"]concat");
  788. /* Color Image invocation.
  789. */
  790. mPSStream.println(intSrcWidth + " " + intSrcHeight + " " + 8 + "["
  791. + intSrcWidth + " 0 "
  792. + "0 " + intSrcHeight
  793. + " 0 " + 0 + "]"
  794. + "/imageSrc load false 3 colorimage");
  795. /* Image data.
  796. */
  797. int index = 0;
  798. byte[] rgbData = new byte[intSrcWidth * 3];
  799. try {
  800. /* Skip the parts of the image that are not part
  801. * of the source rectangle.
  802. */
  803. index = (int) srcY * srcBitMapWidth;
  804. for(int i = 0; i < intSrcHeight; i++) {
  805. /* Skip the left part of the image that is not
  806. * part of the source rectangle.
  807. */
  808. index += (int) srcX;
  809. index = swapBGRtoRGB(bgrData, index, rgbData);
  810. byte[] encodedData = rlEncode(rgbData);
  811. byte[] asciiData = ascii85Encode(encodedData);
  812. mPSStream.write(asciiData);
  813. mPSStream.println("");
  814. }
  815. /*
  816. * If there is an IOError we subvert it to a PrinterException.
  817. * Fix: There has got to be a better way, maybe define
  818. * a PrinterIOException and then throw that?
  819. */
  820. } catch (IOException e) {
  821. //throw new PrinterException(e.toString());
  822. }
  823. mPSStream.println(IMAGE_RESTORE);
  824. }
  825. /**
  826. * Prints the contents of the array of ints, 'data'
  827. * to the current page. The band is placed at the
  828. * location (x, y) in device coordinates on the
  829. * page. The width and height of the band is
  830. * specified by the caller. Currently the data
  831. * is 24 bits per pixel in BGR format.
  832. */
  833. protected void printBand(byte[] bgrData, int x, int y,
  834. int width, int height)
  835. throws PrinterException
  836. {
  837. mPSStream.println(IMAGE_SAVE);
  838. /* Create a PS string big enough to hold a row of pixels.
  839. */
  840. int psBytesPerRow = 3 * width;
  841. while (psBytesPerRow > MAX_PSSTR) {
  842. psBytesPerRow /= 2;
  843. }
  844. mPSStream.println(psBytesPerRow + IMAGE_STR);
  845. /* Scale and translate the unit image.
  846. */
  847. mPSStream.println("[" + width + " 0 "
  848. + "0 " + height
  849. + " " + x + " " + y
  850. +"]concat");
  851. /* Color Image invocation.
  852. */
  853. mPSStream.println(width + " " + height + " " + 8 + "["
  854. + width + " 0 "
  855. + "0 " + -height
  856. + " 0 " + height + "]"
  857. + "/imageSrc load false 3 colorimage");
  858. /* Image data.
  859. */
  860. int index = 0;
  861. byte[] rgbData = new byte[width*3];
  862. try {
  863. for(int i = 0; i < height; i++) {
  864. index = swapBGRtoRGB(bgrData, index, rgbData);
  865. byte[] encodedData = rlEncode(rgbData);
  866. byte[] asciiData = ascii85Encode(encodedData);
  867. mPSStream.write(asciiData);
  868. mPSStream.println("");
  869. }
  870. } catch (IOException e) {
  871. throw new PrinterIOException(e);
  872. }
  873. mPSStream.println(IMAGE_RESTORE);
  874. }
  875. /**
  876. * Examine the metrics captured by the
  877. * <code>PeekGraphics</code> instance and
  878. * if capable of directly converting this
  879. * print job to the printer's control language
  880. * or the native OS's graphics primitives, then
  881. * return a <code>PSPathGraphics</code> to perform
  882. * that conversion. If there is not an object
  883. * capable of the conversion then return
  884. * <code>null</code>. Returning <code>null</code>
  885. * causes the print job to be rasterized.
  886. */
  887. protected Graphics2D createPathGraphics(PeekGraphics peekGraphics,
  888. PrinterJob printerJob,
  889. Printable painter,
  890. PageFormat pageFormat,
  891. int pageIndex) {
  892. PSPathGraphics pathGraphics;
  893. PeekMetrics metrics = peekGraphics.getMetrics();
  894. /* If the application has drawn anything that
  895. * out PathGraphics class can not handle then
  896. * return a null PathGraphics.
  897. */
  898. if (forcePDL == false && (forceRaster == true
  899. || metrics.hasNonSolidColors()
  900. || metrics.hasCompositing())) {
  901. pathGraphics = null;
  902. } else {
  903. BufferedImage bufferedImage = new BufferedImage(8, 8,
  904. BufferedImage.TYPE_INT_RGB);
  905. Graphics2D bufferedGraphics = bufferedImage.createGraphics();
  906. boolean canRedraw = peekGraphics.getAWTDrawingOnly() == false;
  907. pathGraphics = new PSPathGraphics(bufferedGraphics, printerJob,
  908. painter, pageFormat, pageIndex,
  909. canRedraw);
  910. }
  911. return pathGraphics;
  912. }
  913. /**
  914. * Intersect the gstate's current path with the
  915. * current clip and make the result the new clip.
  916. */
  917. protected void selectClipPath() {
  918. mPSStream.println(mClipOpStr);
  919. }
  920. protected void setClip(Shape clip) {
  921. mLastClip = clip;
  922. }
  923. protected void setTransform(AffineTransform transform) {
  924. mLastTransform = transform;
  925. }
  926. /**
  927. * Set the current PostScript font.
  928. * Taken from outFont in PSPrintStream.
  929. */
  930. protected boolean setFont(Font font) {
  931. mLastFont = font;
  932. return true;
  933. }
  934. /**
  935. * Given an array of CharsetStrings that make up a run
  936. * of text, this routine converts each CharsetString to
  937. * an index into our PostScript font list. If one or more
  938. * CharsetStrings can not be represented by a PostScript
  939. * font, then this routine will return a null array.
  940. */
  941. private int[] getPSFontIndexArray(Font font, CharsetString[] charSet) {
  942. int[] psFont = null;
  943. if (mFontProps != null) {
  944. psFont = new int[charSet.length];
  945. }
  946. for (int i = 0; i < charSet.length && psFont != null; i++){
  947. /* Get the encoding of the run of text.
  948. */
  949. CharsetString cs = charSet[i];
  950. CharsetEncoder fontCS = cs.fontDescriptor.encoder;
  951. String charsetName = cs.fontDescriptor.getFontCharsetName();
  952. /*
  953. * sun.awt.Symbol perhaps should return "symbol" for encoding.
  954. * Similarly X11Dingbats should return "dingbats"
  955. * Forced to check for win32 & x/unix names for these converters.
  956. */
  957. if ("Symbol".equals(charsetName)) {
  958. charsetName = "symbol";
  959. } else if ("WingDings".equals(charsetName) ||
  960. "X11Dingbats".equals(charsetName)) {
  961. charsetName = "dingbats";
  962. } else {
  963. charsetName = makeCharsetName(charsetName, cs.charsetChars);
  964. }
  965. int styleMask = font.getStyle() |
  966. FontUtilities.getFont2D(font).getStyle();
  967. String style = FontConfiguration.getStyleString(styleMask);
  968. /* First we map the font name through the properties file.
  969. * This mapping provides alias names for fonts, for example,
  970. * "timesroman" is mapped to "serif".
  971. */
  972. String fontName = font.getFamily().toLowerCase(Locale.ENGLISH);
  973. fontName = fontName.replace(' ', '_');
  974. String name = mFontProps.getProperty(fontName, "");
  975. /* Now map the alias name, character set name, and style
  976. * to a PostScript name.
  977. */
  978. String psName =
  979. mFontProps.getProperty(name + "." + charsetName + "." + style,
  980. null);
  981. if (psName != null) {
  982. /* Get the PostScript font index for the PostScript font.
  983. */
  984. try {
  985. psFont[i] =
  986. Integer.parseInt(mFontProps.getProperty(psName));
  987. /* If there is no PostScript font for this font name,
  988. * then we want to termintate the loop and the method
  989. * indicating our failure. Setting the array to null
  990. * is used to indicate these failures.
  991. */
  992. } catch(NumberFormatException e){
  993. psFont = null;
  994. }
  995. /* There was no PostScript name for the font, character set,
  996. * and style so give up.
  997. */
  998. } else {
  999. psFont = null;
  1000. }
  1001. }
  1002. return psFont;
  1003. }
  1004. private static String escapeParens(String str) {
  1005. if (str.indexOf('(') == -1 && str.indexOf(')') == -1 ) {
  1006. return str;
  1007. } else {
  1008. int count = 0;
  1009. int pos = 0;
  1010. while ((pos = str.indexOf('(', pos)) != -1) {
  1011. count++;
  1012. pos++;
  1013. }
  1014. pos = 0;
  1015. while ((pos = str.indexOf(')', pos)) != -1) {
  1016. count++;
  1017. pos++;
  1018. }
  1019. char []inArr = str.toCharArray();
  1020. char []outArr = new char[inArr.length+count];
  1021. pos = 0;
  1022. for (int i=0;i<inArr.length;i++) {
  1023. if (inArr[i] == '(' || inArr[i] == ')') {
  1024. outArr[pos++] = '\\';
  1025. }
  1026. outArr[pos++] = inArr[i];
  1027. }
  1028. return new String(outArr);
  1029. }
  1030. }
  1031. /* return of 0 means unsupported. Other return indicates the number
  1032. * of distinct PS fonts needed to draw this text. This saves us
  1033. * doing this processing one extra time.
  1034. */
  1035. protected int platformFontCount(Font font, String str) {
  1036. if (mFontProps == null) {
  1037. return 0;
  1038. }
  1039. CharsetString[] acs =
  1040. ((PlatformFont)(font.getPeer())).makeMultiCharsetString(str,false);
  1041. if (acs == null) {
  1042. /* AWT can't convert all chars so use 2D path */
  1043. return 0;
  1044. }
  1045. int[] psFonts = getPSFontIndexArray(font, acs);
  1046. return (psFonts == null) ? 0 : psFonts.length;
  1047. }
  1048. protected boolean textOut(Graphics g, String str, float x, float y,
  1049. Font mLastFont, FontRenderContext frc,
  1050. float width) {
  1051. boolean didText = true;
  1052. if (mFontProps == null) {
  1053. return false;
  1054. } else {
  1055. prepDrawing();
  1056. /* On-screen drawString renders most control chars as the missing
  1057. * glyph and have the non-zero advance of that glyph.
  1058. * Exceptions are \t, \n and \r which are considered zero-width.
  1059. * Postscript handles control chars mostly as a missing glyph.
  1060. * But we use 'ashow' specifying a width for the string which
  1061. * assumes zero-width for those three exceptions, and Postscript
  1062. * tries to squeeze the extra char in, with the result that the
  1063. * glyphs look compressed or even overlap.
  1064. * So exclude those control chars from the string sent to PS.
  1065. */
  1066. str = removeControlChars(str);
  1067. if (str.length() == 0) {
  1068. return true;
  1069. }
  1070. CharsetString[] acs =
  1071. ((PlatformFont)
  1072. (mLastFont.getPeer())).makeMultiCharsetString(str, false);
  1073. if (acs == null) {
  1074. /* AWT can't convert all chars so use 2D path */
  1075. return false;
  1076. }
  1077. /* Get an array of indices into our PostScript name
  1078. * table. If all of the runs can not be converted
  1079. * to PostScript fonts then null is returned and
  1080. * we'll want to fall back to printing the text
  1081. * as shapes.
  1082. */
  1083. int[] psFonts = getPSFontIndexArray(mLastFont, acs);
  1084. if (psFonts != null) {
  1085. for (int i = 0; i < acs.length; i++){
  1086. CharsetString cs = acs[i];
  1087. CharsetEncoder fontCS = cs.fontDescriptor.encoder;
  1088. StringBuffer nativeStr = new StringBuffer();
  1089. byte[] strSeg = new byte[cs.length * 2];
  1090. int len = 0;
  1091. try {
  1092. ByteBuffer bb = ByteBuffer.wrap(strSeg);
  1093. fontCS.encode(CharBuffer.wrap(cs.charsetChars,
  1094. cs.offset,
  1095. cs.length),
  1096. bb, true);
  1097. bb.flip();
  1098. len = bb.limit();
  1099. } catch(IllegalStateException xx){
  1100. continue;
  1101. } catch(CoderMalfunctionError xx){
  1102. continue;
  1103. }
  1104. /* The width to fit to may either be specified,
  1105. * or calculated. Specifying by the caller is only
  1106. * valid if the text does not need to be decomposed
  1107. * into multiple calls.
  1108. */
  1109. float desiredWidth;
  1110. if (acs.length == 1 && width != 0f) {
  1111. desiredWidth = width;
  1112. } else {
  1113. Rectangle2D r2d =
  1114. mLastFont.getStringBounds(cs.charsetChars,
  1115. cs.offset,
  1116. cs.offset+cs.length,
  1117. frc);
  1118. desiredWidth = (float)r2d.getWidth();
  1119. }
  1120. /* unprintable chars had width of 0, causing a PS error
  1121. */
  1122. if (desiredWidth == 0) {
  1123. return didText;
  1124. }
  1125. nativeStr.append('<');
  1126. for (int j = 0; j < len; j++){
  1127. byte b = strSeg[j];
  1128. // to avoid encoding conversion with println()
  1129. String hexS = Integer.toHexString(b);
  1130. int length = hexS.length();
  1131. if (length > 2) {
  1132. hexS = hexS.substring(length - 2, length);
  1133. } else if (length == 1) {
  1134. hexS = "0" + hexS;
  1135. } else if (length == 0) {
  1136. hexS = "00";
  1137. }
  1138. nativeStr.append(hexS);
  1139. }
  1140. nativeStr.append('>');
  1141. /* This comment costs too much in output file size */
  1142. // mPSStream.println("% Font[" + mLastFont.getName() + ", " +
  1143. // FontConfiguration.getStyleString(mLastFont.getStyle()) + ", "
  1144. // + mLastFont.getSize2D() + "]");
  1145. getGState().emitPSFont(psFonts[i], mLastFont.getSize2D());
  1146. // out String
  1147. mPSStream.println(nativeStr.toString() + " " +
  1148. desiredWidth + " " + x + " " + y + " " +
  1149. DrawStringName);
  1150. x += desiredWidth;
  1151. }
  1152. } else {
  1153. didText = false;
  1154. }
  1155. }
  1156. return didText;
  1157. }
  1158. /**
  1159. * Set the current path rule to be either
  1160. * <code>FILL_EVEN_ODD</code> (using the
  1161. * even-odd file rule) or <code>FILL_WINDING</code>
  1162. * (using the non-zero winding rule.)
  1163. */
  1164. protected void setFillMode(int fillRule) {
  1165. switch (fillRule) {
  1166. case FILL_EVEN_ODD:
  1167. mFillOpStr = EVEN_ODD_FILL_STR;
  1168. mClipOpStr = EVEN_ODD_CLIP_STR;
  1169. break;
  1170. case FILL_WINDING:
  1171. mFillOpStr = WINDING_FILL_STR;
  1172. mClipOpStr = WINDING_CLIP_STR;
  1173. break;
  1174. default:
  1175. throw new IllegalArgumentException();
  1176. }
  1177. }
  1178. /**
  1179. * Set the printer's current color to be that
  1180. * defined by <code>color</code>
  1181. */
  1182. protected void setColor(Color color) {
  1183. mLastColor = color;
  1184. }
  1185. /**
  1186. * Fill the current path using the current fill mode
  1187. * and color.
  1188. */
  1189. protected void fillPath() {
  1190. mPSStream.println(mFillOpStr);
  1191. }
  1192. /**
  1193. * Called to mark the start of a new path.
  1194. */
  1195. protected void beginPath() {
  1196. prepDrawing();
  1197. mPSStream.println(NEWPATH_STR);
  1198. mPenX = 0;
  1199. mPenY = 0;
  1200. }
  1201. /**
  1202. * Close the current subpath by appending a straight
  1203. * line from the current point…

Large files files are truncated, but you can click here to view the full file