PageRenderTime 50ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://bitbucket.org/sreilly/openjdk7u-jdk
Java | 2216 lines | 1314 code | 308 blank | 594 comment | 256 complexity | 9414abb8f5d4514ecc65b517a9626999 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause-No-Nuclear-License-2014, LGPL-3.0

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

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

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