0N/A/*
4944N/A * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.print;
0N/A
0N/Aimport java.awt.Color;
0N/Aimport java.awt.Component;
0N/Aimport java.awt.Font;
0N/Aimport java.awt.FontMetrics;
0N/Aimport java.awt.GraphicsEnvironment;
0N/Aimport java.awt.Graphics;
0N/Aimport java.awt.Graphics2D;
0N/Aimport java.awt.HeadlessException;
0N/Aimport java.awt.Rectangle;
0N/Aimport java.awt.Shape;
0N/A
0N/Aimport java.awt.image.BufferedImage;
0N/A
0N/Aimport java.awt.font.FontRenderContext;
0N/A
0N/Aimport java.awt.geom.AffineTransform;
0N/Aimport java.awt.geom.PathIterator;
0N/Aimport java.awt.geom.Rectangle2D;
0N/A
0N/Aimport java.awt.image.BufferedImage;
0N/A
0N/Aimport java.awt.print.Pageable;
0N/Aimport java.awt.print.PageFormat;
0N/Aimport java.awt.print.Paper;
0N/Aimport java.awt.print.Printable;
0N/Aimport java.awt.print.PrinterException;
0N/Aimport java.awt.print.PrinterIOException;
0N/Aimport java.awt.print.PrinterJob;
0N/A
0N/Aimport javax.print.DocFlavor;
0N/Aimport javax.print.PrintService;
0N/Aimport javax.print.StreamPrintService;
0N/Aimport javax.print.attribute.HashPrintRequestAttributeSet;
0N/Aimport javax.print.attribute.PrintRequestAttributeSet;
0N/Aimport javax.print.attribute.standard.Chromaticity;
0N/Aimport javax.print.attribute.standard.Copies;
0N/Aimport javax.print.attribute.standard.Destination;
0N/Aimport javax.print.attribute.standard.DialogTypeSelection;
0N/Aimport javax.print.attribute.standard.JobName;
0N/Aimport javax.print.attribute.standard.Sides;
0N/A
0N/Aimport java.io.BufferedInputStream;
0N/Aimport java.io.BufferedOutputStream;
5478N/Aimport java.io.BufferedReader;
0N/Aimport java.io.CharConversionException;
0N/Aimport java.io.File;
0N/Aimport java.io.InputStream;
5478N/Aimport java.io.InputStreamReader;
0N/Aimport java.io.IOException;
0N/Aimport java.io.FileInputStream;
0N/Aimport java.io.FileOutputStream;
0N/Aimport java.io.OutputStream;
0N/Aimport java.io.PrintStream;
5478N/Aimport java.io.PrintWriter;
5478N/Aimport java.io.StringWriter;
0N/A
0N/Aimport java.util.ArrayList;
0N/Aimport java.util.Enumeration;
0N/Aimport java.util.Locale;
0N/Aimport java.util.Properties;
0N/A
0N/Aimport sun.awt.CharsetString;
0N/Aimport sun.awt.FontConfiguration;
0N/Aimport sun.awt.FontDescriptor;
0N/Aimport sun.awt.PlatformFont;
0N/Aimport sun.awt.SunToolkit;
1686N/Aimport sun.font.FontManagerFactory;
1686N/Aimport sun.font.FontUtilities;
0N/A
0N/Aimport java.nio.charset.*;
0N/Aimport java.nio.CharBuffer;
0N/Aimport java.nio.ByteBuffer;
5089N/Aimport java.nio.file.Files;
0N/A
0N/A//REMIND: Remove use of this class when IPPPrintService is moved to share directory.
0N/Aimport java.lang.reflect.Method;
0N/A
0N/A/**
0N/A * A class which initiates and executes a PostScript printer job.
0N/A *
0N/A * @author Richard Blanchard
0N/A */
0N/Apublic class PSPrinterJob extends RasterPrinterJob {
0N/A
0N/A /* Class Constants */
0N/A
0N/A /**
0N/A * Passed to the <code>setFillMode</code>
0N/A * method this value forces fills to be
0N/A * done using the even-odd fill rule.
0N/A */
0N/A protected static final int FILL_EVEN_ODD = 1;
0N/A
0N/A /**
0N/A * Passed to the <code>setFillMode</code>
0N/A * method this value forces fills to be
0N/A * done using the non-zero winding rule.
0N/A */
0N/A protected static final int FILL_WINDING = 2;
0N/A
0N/A /* PostScript has a 64K maximum on its strings.
0N/A */
0N/A private static final int MAX_PSSTR = (1024 * 64 - 1);
0N/A
0N/A private static final int RED_MASK = 0x00ff0000;
0N/A private static final int GREEN_MASK = 0x0000ff00;
0N/A private static final int BLUE_MASK = 0x000000ff;
0N/A
0N/A private static final int RED_SHIFT = 16;
0N/A private static final int GREEN_SHIFT = 8;
0N/A private static final int BLUE_SHIFT = 0;
0N/A
0N/A private static final int LOWNIBBLE_MASK = 0x0000000f;
0N/A private static final int HINIBBLE_MASK = 0x000000f0;
0N/A private static final int HINIBBLE_SHIFT = 4;
0N/A private static final byte hexDigits[] = {
0N/A (byte)'0', (byte)'1', (byte)'2', (byte)'3',
0N/A (byte)'4', (byte)'5', (byte)'6', (byte)'7',
0N/A (byte)'8', (byte)'9', (byte)'A', (byte)'B',
0N/A (byte)'C', (byte)'D', (byte)'E', (byte)'F'
0N/A };
0N/A
0N/A private static final int PS_XRES = 300;
0N/A private static final int PS_YRES = 300;
0N/A
0N/A private static final String ADOBE_PS_STR = "%!PS-Adobe-3.0";
0N/A private static final String EOF_COMMENT = "%%EOF";
0N/A private static final String PAGE_COMMENT = "%%Page: ";
0N/A
0N/A private static final String READIMAGEPROC = "/imStr 0 def /imageSrc " +
0N/A "{currentfile /ASCII85Decode filter /RunLengthDecode filter " +
0N/A " imStr readstring pop } def";
0N/A
0N/A private static final String COPIES = "/#copies exch def";
0N/A private static final String PAGE_SAVE = "/pgSave save def";
0N/A private static final String PAGE_RESTORE = "pgSave restore";
0N/A private static final String SHOWPAGE = "showpage";
0N/A private static final String IMAGE_SAVE = "/imSave save def";
0N/A private static final String IMAGE_STR = " string /imStr exch def";
0N/A private static final String IMAGE_RESTORE = "imSave restore";
0N/A
0N/A private static final String COORD_PREP = " 0 exch translate "
0N/A + "1 -1 scale"
0N/A + "[72 " + PS_XRES + " div "
0N/A + "0 0 "
0N/A + "72 " + PS_YRES + " div "
0N/A + "0 0]concat";
0N/A
0N/A private static final String SetFontName = "F";
0N/A
0N/A private static final String DrawStringName = "S";
0N/A
0N/A /**
0N/A * The PostScript invocation to fill a path using the
0N/A * even-odd rule. (eofill)
0N/A */
0N/A private static final String EVEN_ODD_FILL_STR = "EF";
0N/A
0N/A /**
0N/A * The PostScript invocation to fill a path using the
0N/A * non-zero winding rule. (fill)
0N/A */
0N/A private static final String WINDING_FILL_STR = "WF";
0N/A
0N/A /**
0N/A * The PostScript to set the clip to be the current path
0N/A * using the even odd rule. (eoclip)
0N/A */
0N/A private static final String EVEN_ODD_CLIP_STR = "EC";
0N/A
0N/A /**
0N/A * The PostScript to set the clip to be the current path
0N/A * using the non-zero winding rule. (clip)
0N/A */
0N/A private static final String WINDING_CLIP_STR = "WC";
0N/A
0N/A /**
0N/A * Expecting two numbers on the PostScript stack, this
0N/A * invocation moves the current pen position. (moveto)
0N/A */
0N/A private static final String MOVETO_STR = " M";
0N/A /**
0N/A * Expecting two numbers on the PostScript stack, this
0N/A * invocation draws a PS line from the current pen
0N/A * position to the point on the stack. (lineto)
0N/A */
0N/A private static final String LINETO_STR = " L";
0N/A
0N/A /**
0N/A * This PostScript operator takes two control points
0N/A * and an ending point and using the current pen
0N/A * position as a starting point adds a bezier
0N/A * curve to the current path. (curveto)
0N/A */
0N/A private static final String CURVETO_STR = " C";
0N/A
0N/A /**
0N/A * The PostScript to pop a state off of the printer's
0N/A * gstate stack. (grestore)
0N/A */
0N/A private static final String GRESTORE_STR = "R";
0N/A /**
0N/A * The PostScript to push a state on to the printer's
0N/A * gstate stack. (gsave)
0N/A */
0N/A private static final String GSAVE_STR = "G";
0N/A
0N/A /**
0N/A * Make the current PostScript path an empty path. (newpath)
0N/A */
0N/A private static final String NEWPATH_STR = "N";
0N/A
0N/A /**
0N/A * Close the current subpath by generating a line segment
0N/A * from the current position to the start of the subpath. (closepath)
0N/A */
0N/A private static final String CLOSEPATH_STR = "P";
0N/A
0N/A /**
0N/A * Use the three numbers on top of the PS operator
0N/A * stack to set the rgb color. (setrgbcolor)
0N/A */
0N/A private static final String SETRGBCOLOR_STR = " SC";
0N/A
0N/A /**
0N/A * Use the top number on the stack to set the printer's
0N/A * current gray value. (setgray)
0N/A */
0N/A private static final String SETGRAY_STR = " SG";
0N/A
0N/A /* Instance Variables */
0N/A
0N/A private int mDestType;
0N/A
0N/A private String mDestination = "lp";
0N/A
0N/A private boolean mNoJobSheet = false;
0N/A
0N/A private String mOptions;
0N/A
0N/A private Font mLastFont;
0N/A
0N/A private Color mLastColor;
0N/A
0N/A private Shape mLastClip;
0N/A
0N/A private AffineTransform mLastTransform;
0N/A
0N/A /* non-null if printing EPS for Java Plugin */
0N/A private EPSPrinter epsPrinter = null;
0N/A
0N/A /**
0N/A * The metrics for the font currently set.
0N/A */
0N/A FontMetrics mCurMetrics;
0N/A
0N/A /**
0N/A * The output stream to which the generated PostScript
0N/A * is written.
0N/A */
0N/A PrintStream mPSStream;
0N/A
0N/A /* The temporary file to which we spool before sending to the printer */
0N/A
0N/A File spoolFile;
0N/A
0N/A /**
0N/A * This string holds the PostScript operator to
0N/A * be used to fill a path. It can be changed
0N/A * by the <code>setFillMode</code> method.
0N/A */
0N/A private String mFillOpStr = WINDING_FILL_STR;
0N/A
0N/A /**
0N/A * This string holds the PostScript operator to
0N/A * be used to clip to a path. It can be changed
0N/A * by the <code>setFillMode</code> method.
0N/A */
0N/A private String mClipOpStr = WINDING_CLIP_STR;
0N/A
0N/A /**
0N/A * A stack that represents the PostScript gstate stack.
0N/A */
0N/A ArrayList mGStateStack = new ArrayList();
0N/A
0N/A /**
0N/A * The x coordinate of the current pen position.
0N/A */
0N/A private float mPenX;
0N/A
0N/A /**
0N/A * The y coordinate of the current pen position.
0N/A */
0N/A private float mPenY;
0N/A
0N/A /**
0N/A * The x coordinate of the starting point of
0N/A * the current subpath.
0N/A */
0N/A private float mStartPathX;
0N/A
0N/A /**
0N/A * The y coordinate of the starting point of
0N/A * the current subpath.
0N/A */
0N/A private float mStartPathY;
0N/A
0N/A /**
0N/A * An optional mapping of fonts to PostScript names.
0N/A */
0N/A private static Properties mFontProps = null;
0N/A
0N/A /* Class static initialiser block */
0N/A static {
0N/A //enable priviledges so initProps can access system properties,
0N/A // open the property file, etc.
0N/A java.security.AccessController.doPrivileged(
0N/A new java.security.PrivilegedAction() {
0N/A public Object run() {
0N/A mFontProps = initProps();
0N/A return null;
0N/A }
0N/A });
0N/A }
0N/A
0N/A /*
0N/A * Initialize PostScript font properties.
0N/A * Copied from PSPrintStream
0N/A */
0N/A private static Properties initProps() {
0N/A // search psfont.properties for fonts
0N/A // and create and initialize fontProps if it exist.
0N/A
0N/A String jhome = System.getProperty("java.home");
0N/A
0N/A if (jhome != null){
0N/A String ulocale = SunToolkit.getStartupLocale().getLanguage();
0N/A try {
0N/A
0N/A File f = new File(jhome + File.separator +
0N/A "lib" + File.separator +
0N/A "psfontj2d.properties." + ulocale);
0N/A
0N/A if (!f.canRead()){
0N/A
0N/A f = new File(jhome + File.separator +
0N/A "lib" + File.separator +
0N/A "psfont.properties." + ulocale);
0N/A if (!f.canRead()){
0N/A
0N/A f = new File(jhome + File.separator + "lib" +
0N/A File.separator + "psfontj2d.properties");
0N/A
0N/A if (!f.canRead()){
0N/A
0N/A f = new File(jhome + File.separator + "lib" +
0N/A File.separator + "psfont.properties");
0N/A
0N/A if (!f.canRead()){
0N/A return (Properties)null;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A // Load property file
0N/A InputStream in =
0N/A new BufferedInputStream(new FileInputStream(f.getPath()));
0N/A Properties props = new Properties();
0N/A props.load(in);
0N/A in.close();
0N/A return props;
0N/A } catch (Exception e){
0N/A return (Properties)null;
0N/A }
0N/A }
0N/A return (Properties)null;
0N/A }
0N/A
0N/A /* Constructors */
0N/A
0N/A public PSPrinterJob()
0N/A {
0N/A }
0N/A
0N/A /* Instance Methods */
0N/A
0N/A /**
0N/A * Presents the user a dialog for changing properties of the
0N/A * print job interactively.
0N/A * @returns false if the user cancels the dialog and
0N/A * true otherwise.
0N/A * @exception HeadlessException if GraphicsEnvironment.isHeadless()
0N/A * returns true.
0N/A * @see java.awt.GraphicsEnvironment#isHeadless
0N/A */
0N/A public boolean printDialog() throws HeadlessException {
0N/A
0N/A if (GraphicsEnvironment.isHeadless()) {
0N/A throw new HeadlessException();
0N/A }
0N/A
0N/A if (attributes == null) {
0N/A attributes = new HashPrintRequestAttributeSet();
0N/A }
0N/A attributes.add(new Copies(getCopies()));
0N/A attributes.add(new JobName(getJobName(), null));
0N/A
0N/A boolean doPrint = false;
0N/A DialogTypeSelection dts =
0N/A (DialogTypeSelection)attributes.get(DialogTypeSelection.class);
0N/A if (dts == DialogTypeSelection.NATIVE) {
0N/A // Remove DialogTypeSelection.NATIVE to prevent infinite loop in
0N/A // RasterPrinterJob.
0N/A attributes.remove(DialogTypeSelection.class);
0N/A doPrint = printDialog(attributes);
0N/A // restore attribute
0N/A attributes.add(DialogTypeSelection.NATIVE);
0N/A } else {
0N/A doPrint = printDialog(attributes);
0N/A }
0N/A
0N/A if (doPrint) {
0N/A JobName jobName = (JobName)attributes.get(JobName.class);
0N/A if (jobName != null) {
0N/A setJobName(jobName.getValue());
0N/A }
0N/A Copies copies = (Copies)attributes.get(Copies.class);
0N/A if (copies != null) {
0N/A setCopies(copies.getValue());
0N/A }
0N/A
0N/A Destination dest = (Destination)attributes.get(Destination.class);
0N/A
0N/A if (dest != null) {
0N/A try {
0N/A mDestType = RasterPrinterJob.FILE;
0N/A mDestination = (new File(dest.getURI())).getPath();
0N/A } catch (Exception e) {
0N/A mDestination = "out.ps";
0N/A }
0N/A } else {
0N/A mDestType = RasterPrinterJob.PRINTER;
0N/A PrintService pServ = getPrintService();
0N/A if (pServ != null) {
0N/A mDestination = pServ.getName();
0N/A }
0N/A }
0N/A }
0N/A
0N/A return doPrint;
0N/A }
0N/A
0N/A /**
0N/A * Invoked by the RasterPrinterJob super class
0N/A * this method is called to mark the start of a
0N/A * document.
0N/A */
0N/A protected void startDoc() throws PrinterException {
0N/A
0N/A // A security check has been performed in the
0N/A // java.awt.print.printerJob.getPrinterJob method.
0N/A // We use an inner class to execute the privilged open operations.
0N/A // Note that we only open a file if it has been nominated by
0N/A // the end-user in a dialog that we ouselves put up.
0N/A
0N/A OutputStream output;
0N/A
0N/A if (epsPrinter == null) {
0N/A if (getPrintService() instanceof PSStreamPrintService) {
0N/A StreamPrintService sps = (StreamPrintService)getPrintService();
0N/A mDestType = RasterPrinterJob.STREAM;
0N/A if (sps.isDisposed()) {
0N/A throw new PrinterException("service is disposed");
0N/A }
0N/A output = sps.getOutputStream();
0N/A if (output == null) {
0N/A throw new PrinterException("Null output stream");
0N/A }
0N/A } else {
0N/A /* REMIND: This needs to be more maintainable */
0N/A mNoJobSheet = super.noJobSheet;
0N/A if (super.destinationAttr != null) {
0N/A mDestType = RasterPrinterJob.FILE;
0N/A mDestination = super.destinationAttr;
0N/A }
0N/A if (mDestType == RasterPrinterJob.FILE) {
0N/A try {
0N/A spoolFile = new File(mDestination);
0N/A output = new FileOutputStream(spoolFile);
0N/A } catch (IOException ex) {
0N/A throw new PrinterIOException(ex);
0N/A }
0N/A } else {
0N/A PrinterOpener po = new PrinterOpener();
0N/A java.security.AccessController.doPrivileged(po);
0N/A if (po.pex != null) {
0N/A throw po.pex;
0N/A }
0N/A output = po.result;
0N/A }
0N/A }
0N/A
0N/A mPSStream = new PrintStream(new BufferedOutputStream(output));
0N/A mPSStream.println(ADOBE_PS_STR);
0N/A }
0N/A
0N/A mPSStream.println("%%BeginProlog");
0N/A mPSStream.println(READIMAGEPROC);
0N/A mPSStream.println("/BD {bind def} bind def");
0N/A mPSStream.println("/D {def} BD");
0N/A mPSStream.println("/C {curveto} BD");
0N/A mPSStream.println("/L {lineto} BD");
0N/A mPSStream.println("/M {moveto} BD");
0N/A mPSStream.println("/R {grestore} BD");
0N/A mPSStream.println("/G {gsave} BD");
0N/A mPSStream.println("/N {newpath} BD");
0N/A mPSStream.println("/P {closepath} BD");
0N/A mPSStream.println("/EC {eoclip} BD");
0N/A mPSStream.println("/WC {clip} BD");
0N/A mPSStream.println("/EF {eofill} BD");
0N/A mPSStream.println("/WF {fill} BD");
0N/A mPSStream.println("/SG {setgray} BD");
0N/A mPSStream.println("/SC {setrgbcolor} BD");
0N/A mPSStream.println("/ISOF {");
0N/A mPSStream.println(" dup findfont dup length 1 add dict begin {");
0N/A mPSStream.println(" 1 index /FID eq {pop pop} {D} ifelse");
0N/A mPSStream.println(" } forall /Encoding ISOLatin1Encoding D");
0N/A mPSStream.println(" currentdict end definefont");
0N/A mPSStream.println("} BD");
0N/A mPSStream.println("/NZ {dup 1 lt {pop 1} if} BD");
0N/A /* The following procedure takes args: string, x, y, desiredWidth.
0N/A * It calculates using stringwidth the width of the string in the
0N/A * current font and subtracts it from the desiredWidth and divides
0N/A * this by stringLen-1. This gives us a per-glyph adjustment in
0N/A * the spacing needed (either +ve or -ve) to make the string
0N/A * print at the desiredWidth. The ashow procedure call takes this
0N/A * per-glyph adjustment as an argument. This is necessary for WYSIWYG
0N/A */
0N/A mPSStream.println("/"+DrawStringName +" {");
0N/A mPSStream.println(" moveto 1 index stringwidth pop NZ sub");
0N/A mPSStream.println(" 1 index length 1 sub NZ div 0");
0N/A mPSStream.println(" 3 2 roll ashow newpath} BD");
0N/A mPSStream.println("/FL [");
0N/A if (mFontProps == null){
0N/A mPSStream.println(" /Helvetica ISOF");
0N/A mPSStream.println(" /Helvetica-Bold ISOF");
0N/A mPSStream.println(" /Helvetica-Oblique ISOF");
0N/A mPSStream.println(" /Helvetica-BoldOblique ISOF");
0N/A mPSStream.println(" /Times-Roman ISOF");
0N/A mPSStream.println(" /Times-Bold ISOF");
0N/A mPSStream.println(" /Times-Italic ISOF");
0N/A mPSStream.println(" /Times-BoldItalic ISOF");
0N/A mPSStream.println(" /Courier ISOF");
0N/A mPSStream.println(" /Courier-Bold ISOF");
0N/A mPSStream.println(" /Courier-Oblique ISOF");
0N/A mPSStream.println(" /Courier-BoldOblique ISOF");
0N/A } else {
0N/A int cnt = Integer.parseInt(mFontProps.getProperty("font.num", "9"));
0N/A for (int i = 0; i < cnt; i++){
0N/A mPSStream.println(" /" + mFontProps.getProperty
0N/A ("font." + String.valueOf(i), "Courier ISOF"));
0N/A }
0N/A }
0N/A mPSStream.println("] D");
0N/A
0N/A mPSStream.println("/"+SetFontName +" {");
0N/A mPSStream.println(" FL exch get exch scalefont");
0N/A mPSStream.println(" [1 0 0 -1 0 0] makefont setfont} BD");
0N/A
0N/A mPSStream.println("%%EndProlog");
0N/A
0N/A mPSStream.println("%%BeginSetup");
0N/A if (epsPrinter == null) {
0N/A // Set Page Size using first page's format.
0N/A PageFormat pageFormat = getPageable().getPageFormat(0);
0N/A double paperHeight = pageFormat.getPaper().getHeight();
0N/A double paperWidth = pageFormat.getPaper().getWidth();
0N/A
0N/A /* PostScript printers can always generate uncollated copies.
0N/A */
0N/A mPSStream.print("<< /PageSize [" +
0N/A paperWidth + " "+ paperHeight+"]");
0N/A
0N/A final PrintService pservice = getPrintService();
0N/A Boolean isPS = (Boolean)java.security.AccessController.doPrivileged(
0N/A new java.security.PrivilegedAction() {
0N/A public Object run() {
0N/A try {
0N/A Class psClass = Class.forName("sun.print.IPPPrintService");
0N/A if (psClass.isInstance(pservice)) {
0N/A Method isPSMethod = psClass.getMethod("isPostscript",
0N/A (Class[])null);
0N/A return (Boolean)isPSMethod.invoke(pservice, (Object[])null);
0N/A }
0N/A } catch (Throwable t) {
0N/A }
0N/A return Boolean.TRUE;
0N/A }
0N/A }
0N/A );
0N/A if (isPS) {
0N/A mPSStream.print(" /DeferredMediaSelection true");
0N/A }
0N/A
0N/A mPSStream.print(" /ImagingBBox null /ManualFeed false");
0N/A mPSStream.print(isCollated() ? " /Collate true":"");
0N/A mPSStream.print(" /NumCopies " +getCopiesInt());
0N/A
0N/A if (sidesAttr != Sides.ONE_SIDED) {
0N/A if (sidesAttr == Sides.TWO_SIDED_LONG_EDGE) {
0N/A mPSStream.print(" /Duplex true ");
0N/A } else if (sidesAttr == Sides.TWO_SIDED_SHORT_EDGE) {
0N/A mPSStream.print(" /Duplex true /Tumble true ");
0N/A }
0N/A }
0N/A mPSStream.println(" >> setpagedevice ");
0N/A }
0N/A mPSStream.println("%%EndSetup");
0N/A }
0N/A
0N/A // Inner class to run "privileged" to open the printer output stream.
0N/A
0N/A private class PrinterOpener implements java.security.PrivilegedAction {
0N/A PrinterException pex;
0N/A OutputStream result;
0N/A
0N/A public Object run() {
0N/A try {
0N/A
0N/A /* Write to a temporary file which will be spooled to
0N/A * the printer then deleted. In the case that the file
0N/A * is not removed for some reason, request that it is
0N/A * removed when the VM exits.
0N/A */
5089N/A spoolFile = Files.createTempFile("javaprint", ".ps").toFile();
0N/A spoolFile.deleteOnExit();
0N/A
0N/A result = new FileOutputStream(spoolFile);
0N/A return result;
0N/A } catch (IOException ex) {
0N/A // If there is an IOError we subvert it to a PrinterException.
0N/A pex = new PrinterIOException(ex);
0N/A }
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A // Inner class to run "privileged" to invoke the system print command
0N/A
0N/A private class PrinterSpooler implements java.security.PrivilegedAction {
0N/A PrinterException pex;
0N/A
5478N/A private void handleProcessFailure(final Process failedProcess,
5478N/A final String[] execCmd, final int result) throws IOException {
5478N/A try (StringWriter sw = new StringWriter();
5478N/A PrintWriter pw = new PrintWriter(sw)) {
5478N/A pw.append("error=").append(Integer.toString(result));
5478N/A pw.append(" running:");
5478N/A for (String arg: execCmd) {
5478N/A pw.append(" '").append(arg).append("'");
5478N/A }
5478N/A try (InputStream is = failedProcess.getErrorStream();
5478N/A InputStreamReader isr = new InputStreamReader(is);
5478N/A BufferedReader br = new BufferedReader(isr)) {
5478N/A while (br.ready()) {
5478N/A pw.println();
5478N/A pw.append("\t\t").append(br.readLine());
5478N/A }
5478N/A } finally {
5478N/A pw.flush();
5478N/A throw new IOException(sw.toString());
5478N/A }
5478N/A }
5478N/A }
5478N/A
0N/A public Object run() {
5478N/A if (spoolFile == null || !spoolFile.exists()) {
5478N/A pex = new PrinterException("No spool file");
5478N/A return null;
5478N/A }
0N/A try {
0N/A /**
0N/A * Spool to the printer.
0N/A */
0N/A String fileName = spoolFile.getAbsolutePath();
0N/A String execCmd[] = printExecCmd(mDestination, mOptions,
0N/A mNoJobSheet, getJobNameInt(),
0N/A 1, fileName);
0N/A
0N/A Process process = Runtime.getRuntime().exec(execCmd);
0N/A process.waitFor();
5478N/A final int result = process.exitValue();
5478N/A if (0 != result) {
5478N/A handleProcessFailure(process, execCmd, result);
5478N/A }
0N/A } catch (IOException ex) {
0N/A pex = new PrinterIOException(ex);
0N/A } catch (InterruptedException ie) {
0N/A pex = new PrinterException(ie.toString());
5478N/A } finally {
5478N/A spoolFile.delete();
0N/A }
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Invoked if the application cancelled the printjob.
0N/A */
0N/A protected void abortDoc() {
0N/A if (mPSStream != null && mDestType != RasterPrinterJob.STREAM) {
0N/A mPSStream.close();
0N/A }
0N/A java.security.AccessController.doPrivileged(
0N/A new java.security.PrivilegedAction() {
0N/A
0N/A public Object run() {
0N/A if (spoolFile != null && spoolFile.exists()) {
0N/A spoolFile.delete();
0N/A }
0N/A return null;
0N/A }
0N/A });
0N/A }
0N/A
0N/A /**
0N/A * Invoked by the RasterPrintJob super class
0N/A * this method is called after that last page
0N/A * has been imaged.
0N/A */
0N/A protected void endDoc() throws PrinterException {
0N/A if (mPSStream != null) {
0N/A mPSStream.println(EOF_COMMENT);
0N/A mPSStream.flush();
0N/A if (mDestType != RasterPrinterJob.STREAM) {
0N/A mPSStream.close();
0N/A }
0N/A }
0N/A if (mDestType == RasterPrinterJob.PRINTER) {
0N/A if (getPrintService() != null) {
0N/A mDestination = getPrintService().getName();
0N/A }
0N/A PrinterSpooler spooler = new PrinterSpooler();
0N/A java.security.AccessController.doPrivileged(spooler);
0N/A if (spooler.pex != null) {
0N/A throw spooler.pex;
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * The RasterPrintJob super class calls this method
0N/A * at the start of each page.
0N/A */
0N/A protected void startPage(PageFormat pageFormat, Printable painter,
0N/A int index, boolean paperChanged)
0N/A throws PrinterException
0N/A {
0N/A double paperHeight = pageFormat.getPaper().getHeight();
0N/A double paperWidth = pageFormat.getPaper().getWidth();
0N/A int pageNumber = index + 1;
0N/A
0N/A /* Place an initial gstate on to our gstate stack.
0N/A * It will have the default PostScript gstate
0N/A * attributes.
0N/A */
0N/A mGStateStack = new ArrayList();
0N/A mGStateStack.add(new GState());
0N/A
0N/A mPSStream.println(PAGE_COMMENT + pageNumber + " " + pageNumber);
0N/A
0N/A /* Check current page's pageFormat against the previous pageFormat,
0N/A */
0N/A if (index > 0 && paperChanged) {
0N/A
0N/A mPSStream.print("<< /PageSize [" +
0N/A paperWidth + " " + paperHeight + "]");
0N/A
0N/A final PrintService pservice = getPrintService();
0N/A Boolean isPS =
0N/A (Boolean)java.security.AccessController.doPrivileged(
0N/A
0N/A new java.security.PrivilegedAction() {
0N/A public Object run() {
0N/A try {
0N/A Class psClass =
0N/A Class.forName("sun.print.IPPPrintService");
0N/A if (psClass.isInstance(pservice)) {
0N/A Method isPSMethod =
0N/A psClass.getMethod("isPostscript",
0N/A (Class[])null);
0N/A return (Boolean)
0N/A isPSMethod.invoke(pservice,
0N/A (Object[])null);
0N/A }
0N/A } catch (Throwable t) {
0N/A }
0N/A return Boolean.TRUE;
0N/A }
0N/A }
0N/A );
0N/A
0N/A if (isPS) {
0N/A mPSStream.print(" /DeferredMediaSelection true");
0N/A }
0N/A mPSStream.println(" >> setpagedevice");
0N/A }
0N/A mPSStream.println(PAGE_SAVE);
0N/A mPSStream.println(paperHeight + COORD_PREP);
0N/A }
0N/A
0N/A /**
0N/A * The RastePrintJob super class calls this method
0N/A * at the end of each page.
0N/A */
0N/A protected void endPage(PageFormat format, Printable painter,
0N/A int index)
0N/A throws PrinterException
0N/A {
0N/A mPSStream.println(PAGE_RESTORE);
0N/A mPSStream.println(SHOWPAGE);
0N/A }
0N/A
0N/A /**
0N/A * Convert the 24 bit BGR image buffer represented by
0N/A * <code>image</code> to PostScript. The image is drawn at
0N/A * <code>(destX, destY)</code> in device coordinates.
0N/A * The image is scaled into a square of size
0N/A * specified by <code>destWidth</code> and
0N/A * <code>destHeight</code>. The portion of the
0N/A * source image copied into that square is specified
0N/A * by <code>srcX</code>, <code>srcY</code>,
0N/A * <code>srcWidth</code>, and srcHeight.
0N/A */
0N/A protected void drawImageBGR(byte[] bgrData,
0N/A float destX, float destY,
0N/A float destWidth, float destHeight,
0N/A float srcX, float srcY,
0N/A float srcWidth, float srcHeight,
0N/A int srcBitMapWidth, int srcBitMapHeight) {
0N/A
0N/A /* We draw images at device resolution so we probably need
0N/A * to change the current PostScript transform.
0N/A */
0N/A setTransform(new AffineTransform());
0N/A prepDrawing();
0N/A
0N/A int intSrcWidth = (int) srcWidth;
0N/A int intSrcHeight = (int) srcHeight;
0N/A
0N/A mPSStream.println(IMAGE_SAVE);
0N/A
0N/A /* Create a PS string big enough to hold a row of pixels.
0N/A */
0N/A int psBytesPerRow = 3 * (int) intSrcWidth;
0N/A while (psBytesPerRow > MAX_PSSTR) {
0N/A psBytesPerRow /= 2;
0N/A }
0N/A
0N/A mPSStream.println(psBytesPerRow + IMAGE_STR);
0N/A
0N/A /* Scale and translate the unit image.
0N/A */
0N/A mPSStream.println("[" + destWidth + " 0 "
0N/A + "0 " + destHeight
0N/A + " " + destX + " " + destY
0N/A +"]concat");
0N/A
0N/A /* Color Image invocation.
0N/A */
0N/A mPSStream.println(intSrcWidth + " " + intSrcHeight + " " + 8 + "["
0N/A + intSrcWidth + " 0 "
0N/A + "0 " + intSrcHeight
0N/A + " 0 " + 0 + "]"
0N/A + "/imageSrc load false 3 colorimage");
0N/A
0N/A /* Image data.
0N/A */
0N/A int index = 0;
0N/A byte[] rgbData = new byte[intSrcWidth * 3];
0N/A
0N/A try {
0N/A /* Skip the parts of the image that are not part
0N/A * of the source rectangle.
0N/A */
0N/A index = (int) srcY * srcBitMapWidth;
0N/A
0N/A for(int i = 0; i < intSrcHeight; i++) {
0N/A
0N/A /* Skip the left part of the image that is not
0N/A * part of the source rectangle.
0N/A */
0N/A index += (int) srcX;
0N/A
0N/A index = swapBGRtoRGB(bgrData, index, rgbData);
0N/A byte[] encodedData = rlEncode(rgbData);
0N/A byte[] asciiData = ascii85Encode(encodedData);
0N/A mPSStream.write(asciiData);
0N/A mPSStream.println("");
0N/A }
0N/A
0N/A /*
0N/A * If there is an IOError we subvert it to a PrinterException.
0N/A * Fix: There has got to be a better way, maybe define
0N/A * a PrinterIOException and then throw that?
0N/A */
0N/A } catch (IOException e) {
0N/A //throw new PrinterException(e.toString());
0N/A }
0N/A
0N/A mPSStream.println(IMAGE_RESTORE);
0N/A }
0N/A
0N/A /**
0N/A * Prints the contents of the array of ints, 'data'
0N/A * to the current page. The band is placed at the
0N/A * location (x, y) in device coordinates on the
0N/A * page. The width and height of the band is
0N/A * specified by the caller. Currently the data
0N/A * is 24 bits per pixel in BGR format.
0N/A */
0N/A protected void printBand(byte[] bgrData, int x, int y,
0N/A int width, int height)
0N/A throws PrinterException
0N/A {
0N/A
0N/A mPSStream.println(IMAGE_SAVE);
0N/A
0N/A /* Create a PS string big enough to hold a row of pixels.
0N/A */
0N/A int psBytesPerRow = 3 * width;
0N/A while (psBytesPerRow > MAX_PSSTR) {
0N/A psBytesPerRow /= 2;
0N/A }
0N/A
0N/A mPSStream.println(psBytesPerRow + IMAGE_STR);
0N/A
0N/A /* Scale and translate the unit image.
0N/A */
0N/A mPSStream.println("[" + width + " 0 "
0N/A + "0 " + height
0N/A + " " + x + " " + y
0N/A +"]concat");
0N/A
0N/A /* Color Image invocation.
0N/A */
0N/A mPSStream.println(width + " " + height + " " + 8 + "["
0N/A + width + " 0 "
0N/A + "0 " + -height
0N/A + " 0 " + height + "]"
0N/A + "/imageSrc load false 3 colorimage");
0N/A
0N/A /* Image data.
0N/A */
0N/A int index = 0;
0N/A byte[] rgbData = new byte[width*3];
0N/A
0N/A try {
0N/A for(int i = 0; i < height; i++) {
0N/A index = swapBGRtoRGB(bgrData, index, rgbData);
0N/A byte[] encodedData = rlEncode(rgbData);
0N/A byte[] asciiData = ascii85Encode(encodedData);
0N/A mPSStream.write(asciiData);
0N/A mPSStream.println("");
0N/A }
0N/A
0N/A } catch (IOException e) {
0N/A throw new PrinterIOException(e);
0N/A }
0N/A
0N/A mPSStream.println(IMAGE_RESTORE);
0N/A }
0N/A
0N/A /**
0N/A * Examine the metrics captured by the
0N/A * <code>PeekGraphics</code> instance and
0N/A * if capable of directly converting this
0N/A * print job to the printer's control language
0N/A * or the native OS's graphics primitives, then
0N/A * return a <code>PSPathGraphics</code> to perform
0N/A * that conversion. If there is not an object
0N/A * capable of the conversion then return
0N/A * <code>null</code>. Returning <code>null</code>
0N/A * causes the print job to be rasterized.
0N/A */
0N/A
0N/A protected Graphics2D createPathGraphics(PeekGraphics peekGraphics,
0N/A PrinterJob printerJob,
0N/A Printable painter,
0N/A PageFormat pageFormat,
0N/A int pageIndex) {
0N/A
0N/A PSPathGraphics pathGraphics;
0N/A PeekMetrics metrics = peekGraphics.getMetrics();
0N/A
0N/A /* If the application has drawn anything that
0N/A * out PathGraphics class can not handle then
0N/A * return a null PathGraphics.
0N/A */
0N/A if (forcePDL == false && (forceRaster == true
0N/A || metrics.hasNonSolidColors()
0N/A || metrics.hasCompositing())) {
0N/A
0N/A pathGraphics = null;
0N/A } else {
0N/A
0N/A BufferedImage bufferedImage = new BufferedImage(8, 8,
0N/A BufferedImage.TYPE_INT_RGB);
0N/A Graphics2D bufferedGraphics = bufferedImage.createGraphics();
0N/A boolean canRedraw = peekGraphics.getAWTDrawingOnly() == false;
0N/A
0N/A pathGraphics = new PSPathGraphics(bufferedGraphics, printerJob,
0N/A painter, pageFormat, pageIndex,
0N/A canRedraw);
0N/A }
0N/A
0N/A return pathGraphics;
0N/A }
0N/A
0N/A /**
0N/A * Intersect the gstate's current path with the
0N/A * current clip and make the result the new clip.
0N/A */
0N/A protected void selectClipPath() {
0N/A
0N/A mPSStream.println(mClipOpStr);
0N/A }
0N/A
0N/A protected void setClip(Shape clip) {
0N/A
0N/A mLastClip = clip;
0N/A }
0N/A
0N/A protected void setTransform(AffineTransform transform) {
0N/A mLastTransform = transform;
0N/A }
0N/A
0N/A /**
0N/A * Set the current PostScript font.
0N/A * Taken from outFont in PSPrintStream.
0N/A */
0N/A protected boolean setFont(Font font) {
0N/A mLastFont = font;
0N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * Given an array of CharsetStrings that make up a run
0N/A * of text, this routine converts each CharsetString to
0N/A * an index into our PostScript font list. If one or more
0N/A * CharsetStrings can not be represented by a PostScript
0N/A * font, then this routine will return a null array.
0N/A */
0N/A private int[] getPSFontIndexArray(Font font, CharsetString[] charSet) {
0N/A int[] psFont = null;
0N/A
0N/A if (mFontProps != null) {
0N/A psFont = new int[charSet.length];
0N/A }
0N/A
0N/A for (int i = 0; i < charSet.length && psFont != null; i++){
0N/A
0N/A /* Get the encoding of the run of text.
0N/A */
0N/A CharsetString cs = charSet[i];
0N/A
0N/A CharsetEncoder fontCS = cs.fontDescriptor.encoder;
0N/A String charsetName = cs.fontDescriptor.getFontCharsetName();
0N/A /*
0N/A * sun.awt.Symbol perhaps should return "symbol" for encoding.
0N/A * Similarly X11Dingbats should return "dingbats"
0N/A * Forced to check for win32 & x/unix names for these converters.
0N/A */
0N/A
0N/A if ("Symbol".equals(charsetName)) {
0N/A charsetName = "symbol";
0N/A } else if ("WingDings".equals(charsetName) ||
0N/A "X11Dingbats".equals(charsetName)) {
0N/A charsetName = "dingbats";
0N/A } else {
0N/A charsetName = makeCharsetName(charsetName, cs.charsetChars);
0N/A }
0N/A
0N/A int styleMask = font.getStyle() |
1686N/A FontUtilities.getFont2D(font).getStyle();
0N/A
0N/A String style = FontConfiguration.getStyleString(styleMask);
0N/A
0N/A /* First we map the font name through the properties file.
0N/A * This mapping provides alias names for fonts, for example,
0N/A * "timesroman" is mapped to "serif".
0N/A */
0N/A String fontName = font.getFamily().toLowerCase(Locale.ENGLISH);
0N/A fontName = fontName.replace(' ', '_');
0N/A String name = mFontProps.getProperty(fontName, "");
0N/A
0N/A /* Now map the alias name, character set name, and style
0N/A * to a PostScript name.
0N/A */
0N/A String psName =
0N/A mFontProps.getProperty(name + "." + charsetName + "." + style,
0N/A null);
0N/A
0N/A if (psName != null) {
0N/A
0N/A /* Get the PostScript font index for the PostScript font.
0N/A */
0N/A try {
0N/A psFont[i] =
0N/A Integer.parseInt(mFontProps.getProperty(psName));
0N/A
0N/A /* If there is no PostScript font for this font name,
0N/A * then we want to termintate the loop and the method
0N/A * indicating our failure. Setting the array to null
0N/A * is used to indicate these failures.
0N/A */
0N/A } catch(NumberFormatException e){
0N/A psFont = null;
0N/A }
0N/A
0N/A /* There was no PostScript name for the font, character set,
0N/A * and style so give up.
0N/A */
0N/A } else {
0N/A psFont = null;
0N/A }
0N/A }
0N/A
0N/A return psFont;
0N/A }
0N/A
0N/A
0N/A private static String escapeParens(String str) {
0N/A if (str.indexOf('(') == -1 && str.indexOf(')') == -1 ) {
0N/A return str;
0N/A } else {
0N/A int count = 0;
0N/A int pos = 0;
0N/A while ((pos = str.indexOf('(', pos)) != -1) {
0N/A count++;
0N/A pos++;
0N/A }
0N/A pos = 0;
0N/A while ((pos = str.indexOf(')', pos)) != -1) {
0N/A count++;
0N/A pos++;
0N/A }
0N/A char []inArr = str.toCharArray();
0N/A char []outArr = new char[inArr.length+count];
0N/A pos = 0;
0N/A for (int i=0;i<inArr.length;i++) {
0N/A if (inArr[i] == '(' || inArr[i] == ')') {
0N/A outArr[pos++] = '\\';
0N/A }
0N/A outArr[pos++] = inArr[i];
0N/A }
0N/A return new String(outArr);
0N/A
0N/A }
0N/A }
0N/A
0N/A /* return of 0 means unsupported. Other return indicates the number
0N/A * of distinct PS fonts needed to draw this text. This saves us
0N/A * doing this processing one extra time.
0N/A */
0N/A protected int platformFontCount(Font font, String str) {
0N/A if (mFontProps == null) {
0N/A return 0;
0N/A }
0N/A CharsetString[] acs =
0N/A ((PlatformFont)(font.getPeer())).makeMultiCharsetString(str,false);
0N/A if (acs == null) {
0N/A /* AWT can't convert all chars so use 2D path */
0N/A return 0;
0N/A }
0N/A int[] psFonts = getPSFontIndexArray(font, acs);
0N/A return (psFonts == null) ? 0 : psFonts.length;
0N/A }
0N/A
0N/A protected boolean textOut(Graphics g, String str, float x, float y,
0N/A Font mLastFont, FontRenderContext frc,
0N/A float width) {
0N/A boolean didText = true;
0N/A
0N/A if (mFontProps == null) {
0N/A return false;
0N/A } else {
0N/A prepDrawing();
0N/A
0N/A /* On-screen drawString renders most control chars as the missing
0N/A * glyph and have the non-zero advance of that glyph.
0N/A * Exceptions are \t, \n and \r which are considered zero-width.
0N/A * Postscript handles control chars mostly as a missing glyph.
0N/A * But we use 'ashow' specifying a width for the string which
0N/A * assumes zero-width for those three exceptions, and Postscript
0N/A * tries to squeeze the extra char in, with the result that the
0N/A * glyphs look compressed or even overlap.
0N/A * So exclude those control chars from the string sent to PS.
0N/A */
0N/A str = removeControlChars(str);
0N/A if (str.length() == 0) {
0N/A return true;
0N/A }
0N/A CharsetString[] acs =
0N/A ((PlatformFont)
0N/A (mLastFont.getPeer())).makeMultiCharsetString(str, false);
0N/A if (acs == null) {
0N/A /* AWT can't convert all chars so use 2D path */
0N/A return false;
0N/A }
0N/A /* Get an array of indices into our PostScript name
0N/A * table. If all of the runs can not be converted
0N/A * to PostScript fonts then null is returned and
0N/A * we'll want to fall back to printing the text
0N/A * as shapes.
0N/A */
0N/A int[] psFonts = getPSFontIndexArray(mLastFont, acs);
0N/A if (psFonts != null) {
0N/A
0N/A for (int i = 0; i < acs.length; i++){
0N/A CharsetString cs = acs[i];
0N/A CharsetEncoder fontCS = cs.fontDescriptor.encoder;
0N/A
0N/A StringBuffer nativeStr = new StringBuffer();
0N/A byte[] strSeg = new byte[cs.length * 2];
0N/A int len = 0;
0N/A try {
0N/A ByteBuffer bb = ByteBuffer.wrap(strSeg);
0N/A fontCS.encode(CharBuffer.wrap(cs.charsetChars,
0N/A cs.offset,
0N/A cs.length),
0N/A bb, true);
0N/A bb.flip();
0N/A len = bb.limit();
0N/A } catch(IllegalStateException xx){
0N/A continue;
0N/A } catch(CoderMalfunctionError xx){
0N/A continue;
0N/A }
0N/A /* The width to fit to may either be specified,
0N/A * or calculated. Specifying by the caller is only
0N/A * valid if the text does not need to be decomposed
0N/A * into multiple calls.
0N/A */
0N/A float desiredWidth;
0N/A if (acs.length == 1 && width != 0f) {
0N/A desiredWidth = width;
0N/A } else {
0N/A Rectangle2D r2d =
0N/A mLastFont.getStringBounds(cs.charsetChars,
0N/A cs.offset,
0N/A cs.offset+cs.length,
0N/A frc);
0N/A desiredWidth = (float)r2d.getWidth();
0N/A }
0N/A /* unprintable chars had width of 0, causing a PS error
0N/A */
0N/A if (desiredWidth == 0) {
0N/A return didText;
0N/A }
0N/A nativeStr.append('<');
0N/A for (int j = 0; j < len; j++){
0N/A byte b = strSeg[j];
0N/A // to avoid encoding conversion with println()
0N/A String hexS = Integer.toHexString(b);
0N/A int length = hexS.length();
0N/A if (length > 2) {
0N/A hexS = hexS.substring(length - 2, length);
0N/A } else if (length == 1) {
0N/A hexS = "0" + hexS;
0N/A } else if (length == 0) {
0N/A hexS = "00";
0N/A }
0N/A nativeStr.append(hexS);
0N/A }
0N/A nativeStr.append('>');
0N/A /* This comment costs too much in output file size */
0N/A// mPSStream.println("% Font[" + mLastFont.getName() + ", " +
0N/A// FontConfiguration.getStyleString(mLastFont.getStyle()) + ", "
0N/A// + mLastFont.getSize2D() + "]");
0N/A getGState().emitPSFont(psFonts[i], mLastFont.getSize2D());
0N/A
0N/A // out String
0N/A mPSStream.println(nativeStr.toString() + " " +
0N/A desiredWidth + " " + x + " " + y + " " +
0N/A DrawStringName);
0N/A x += desiredWidth;
0N/A }
0N/A } else {
0N/A didText = false;
0N/A }
0N/A }
0N/A
0N/A return didText;
0N/A }
0N/A /**
0N/A * Set the current path rule to be either
0N/A * <code>FILL_EVEN_ODD</code> (using the
0N/A * even-odd file rule) or <code>FILL_WINDING</code>
0N/A * (using the non-zero winding rule.)
0N/A */
0N/A protected void setFillMode(int fillRule) {
0N/A
0N/A switch (fillRule) {
0N/A
0N/A case FILL_EVEN_ODD:
0N/A mFillOpStr = EVEN_ODD_FILL_STR;
0N/A mClipOpStr = EVEN_ODD_CLIP_STR;
0N/A break;
0N/A
0N/A case FILL_WINDING:
0N/A mFillOpStr = WINDING_FILL_STR;
0N/A mClipOpStr = WINDING_CLIP_STR;
0N/A break;
0N/A
0N/A default:
0N/A throw new IllegalArgumentException();
0N/A }
0N/A
0N/A }
0N/A
0N/A /**
0N/A * Set the printer's current color to be that
0N/A * defined by <code>color</code>
0N/A */
0N/A protected void setColor(Color color) {
0N/A mLastColor = color;
0N/A }
0N/A
0N/A /**
0N/A * Fill the current path using the current fill mode
0N/A * and color.
0N/A */
0N/A protected void fillPath() {
0N/A
0N/A mPSStream.println(mFillOpStr);
0N/A }
0N/A
0N/A /**
0N/A * Called to mark the start of a new path.
0N/A */
0N/A protected void beginPath() {
0N/A
0N/A prepDrawing();
0N/A mPSStream.println(NEWPATH_STR);
0N/A
0N/A mPenX = 0;
0N/A mPenY = 0;
0N/A }
0N/A
0N/A /**
0N/A * Close the current subpath by appending a straight
0N/A * line from the current point to the subpath's
0N/A * starting point.
0N/A */
0N/A protected void closeSubpath() {
0N/A
0N/A mPSStream.println(CLOSEPATH_STR);
0N/A
0N/A mPenX = mStartPathX;
0N/A mPenY = mStartPathY;
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Generate PostScript to move the current pen
0N/A * position to <code>(x, y)</code>.
0N/A */
0N/A protected void moveTo(float x, float y) {
0N/A
0N/A mPSStream.println(trunc(x) + " " + trunc(y) + MOVETO_STR);
0N/A
0N/A /* moveto marks the start of a new subpath
0N/A * and we need to remember that starting
0N/A * position so that we know where the
0N/A * pen returns to with a close path.
0N/A */
0N/A mStartPathX = x;
0N/A mStartPathY = y;
0N/A
0N/A mPenX = x;
0N/A mPenY = y;
0N/A }
0N/A /**
0N/A * Generate PostScript to draw a line from the
0N/A * current pen position to <code>(x, y)</code>.
0N/A */
0N/A protected void lineTo(float x, float y) {
0N/A
0N/A mPSStream.println(trunc(x) + " " + trunc(y) + LINETO_STR);
0N/A
0N/A mPenX = x;
0N/A mPenY = y;
0N/A }
0N/A
0N/A /**
0N/A * Add to the current path a bezier curve formed
0N/A * by the current pen position and the method parameters
0N/A * which are two control points and an ending
0N/A * point.
0N/A */
0N/A protected void bezierTo(float control1x, float control1y,
0N/A float control2x, float control2y,
0N/A float endX, float endY) {
0N/A
0N/A// mPSStream.println(control1x + " " + control1y
0N/A// + " " + control2x + " " + control2y
0N/A// + " " + endX + " " + endY
0N/A// + CURVETO_STR);
0N/A mPSStream.println(trunc(control1x) + " " + trunc(control1y)
0N/A + " " + trunc(control2x) + " " + trunc(control2y)
0N/A + " " + trunc(endX) + " " + trunc(endY)
0N/A + CURVETO_STR);
0N/A
0N/A
0N/A mPenX = endX;
0N/A mPenY = endY;
0N/A }
0N/A
0N/A String trunc(float f) {
0N/A float af = Math.abs(f);
0N/A if (af >= 1f && af <=1000f) {
0N/A f = Math.round(f*1000)/1000f;
0N/A }
0N/A return Float.toString(f);
0N/A }
0N/A
0N/A /**
0N/A * Return the x coordinate of the pen in the
0N/A * current path.
0N/A */
0N/A protected float getPenX() {
0N/A
0N/A return mPenX;
0N/A }
0N/A /**
0N/A * Return the y coordinate of the pen in the
0N/A * current path.
0N/A */
0N/A protected float getPenY() {
0N/A
0N/A return mPenY;
0N/A }
0N/A
0N/A /**
0N/A * Return the x resolution of the coordinates
0N/A * to be rendered.
0N/A */
0N/A protected double getXRes() {
0N/A return PS_XRES;
0N/A }
0N/A /**
0N/A * Return the y resolution of the coordinates
0N/A * to be rendered.
0N/A */
0N/A protected double getYRes() {
0N/A return PS_YRES;
0N/A }
0N/A
0N/A /**
0N/A * For PostScript the origin is in the upper-left of the
0N/A * paper not at the imageable area corner.
0N/A */
0N/A protected double getPhysicalPrintableX(Paper p) {
0N/A return 0;
0N/A
0N/A }
0N/A
0N/A /**
0N/A * For PostScript the origin is in the upper-left of the
0N/A * paper not at the imageable area corner.
0N/A */
0N/A protected double getPhysicalPrintableY(Paper p) {
0N/A return 0;
0N/A }
0N/A
0N/A protected double getPhysicalPrintableWidth(Paper p) {
0N/A return p.getImageableWidth();
0N/A }
0N/A
0N/A protected double getPhysicalPrintableHeight(Paper p) {
0N/A return p.getImageableHeight();
0N/A }
0N/A
0N/A protected double getPhysicalPageWidth(Paper p) {
0N/A return p.getWidth();
0N/A }
0N/A
0N/A protected double getPhysicalPageHeight(Paper p) {
0N/A return p.getHeight();
0N/A }
0N/A
0N/A /**
0N/A * Returns how many times each page in the book
0N/A * should be consecutively printed by PrintJob.
0N/A * If the printer makes copies itself then this
0N/A * method should return 1.
0N/A */
0N/A protected int getNoncollatedCopies() {
0N/A return 1;
0N/A }
0N/A
0N/A protected int getCollatedCopies() {
0N/A return 1;
0N/A }
0N/A
0N/A private String[] printExecCmd(String printer, String options,
0N/A boolean noJobSheet,
0N/A String banner, int copies, String spoolFile) {
0N/A int PRINTER = 0x1;
0N/A int OPTIONS = 0x2;
0N/A int BANNER = 0x4;
0N/A int COPIES = 0x8;
0N/A int NOSHEET = 0x10;
0N/A int pFlags = 0;
0N/A String execCmd[];
0N/A int ncomps = 2; // minimum number of print args
0N/A int n = 0;
0N/A
0N/A if (printer != null && !printer.equals("") && !printer.equals("lp")) {
0N/A pFlags |= PRINTER;
0N/A ncomps+=1;
0N/A }
0N/A if (options != null && !options.equals("")) {
0N/A pFlags |= OPTIONS;
0N/A ncomps+=1;
0N/A }
0N/A if (banner != null && !banner.equals("")) {
0N/A pFlags |= BANNER;
0N/A ncomps+=1;
0N/A }
0N/A if (copies > 1) {
0N/A pFlags |= COPIES;
0N/A ncomps+=1;
0N/A }
0N/A if (noJobSheet) {
0N/A pFlags |= NOSHEET;
0N/A ncomps+=1;
0N/A }
4632N/A
4632N/A String osname = System.getProperty("os.name");
4944N/A if (osname.equals("Linux") || osname.contains("OS X")) {
0N/A execCmd = new String[ncomps];
0N/A execCmd[n++] = "/usr/bin/lpr";
0N/A if ((pFlags & PRINTER) != 0) {
215N/A execCmd[n++] = "-P" + printer;
0N/A }
0N/A if ((pFlags & BANNER) != 0) {
215N/A execCmd[n++] = "-J" + banner;
0N/A }
0N/A if ((pFlags & COPIES) != 0) {
215N/A execCmd[n++] = "-#" + copies;
0N/A }
0N/A if ((pFlags & NOSHEET) != 0) {
215N/A execCmd[n++] = "-h";
0N/A }
0N/A if ((pFlags & OPTIONS) != 0) {
0N/A execCmd[n++] = new String(options);
0N/A }
0N/A } else {
0N/A ncomps+=1; //add 1 arg for lp
0N/A execCmd = new String[ncomps];
0N/A execCmd[n++] = "/usr/bin/lp";
0N/A execCmd[n++] = "-c"; // make a copy of the spool file
0N/A if ((pFlags & PRINTER) != 0) {
215N/A execCmd[n++] = "-d" + printer;
0N/A }
0N/A if ((pFlags & BANNER) != 0) {
215N/A execCmd[n++] = "-t" + banner;
0N/A }
0N/A if ((pFlags & COPIES) != 0) {
215N/A execCmd[n++] = "-n" + copies;
0N/A }
0N/A if ((pFlags & NOSHEET) != 0) {
215N/A execCmd[n++] = "-o nobanner";
0N/A }
0N/A if ((pFlags & OPTIONS) != 0) {
215N/A execCmd[n++] = "-o" + options;
0N/A }
0N/A }
0N/A execCmd[n++] = spoolFile;
0N/A return execCmd;
0N/A }
0N/A
0N/A private static int swapBGRtoRGB(byte[] image, int index, byte[] dest) {
0N/A int destIndex = 0;
0N/A while(index < image.length-2 && destIndex < dest.length-2) {
0N/A dest[destIndex++] = image[index+2];
0N/A dest[destIndex++] = image[index+1];
0N/A dest[destIndex++] = image[index+0];
0N/A index+=3;
0N/A }
0N/A return index;
0N/A }
0N/A
0N/A /*
0N/A * Currently CharToByteConverter.getCharacterEncoding() return values are
0N/A * not fixed yet. These are used as the part of the key of
0N/A * psfont.propeties. When those name are fixed this routine can
0N/A * be erased.
0N/A */
0N/A private String makeCharsetName(String name, char[] chs) {
0N/A if (name.equals("Cp1252") || name.equals("ISO8859_1")) {
0N/A return "latin1";
0N/A } else if (name.equals("UTF8")) {
0N/A // same as latin 1 if all chars < 256
0N/A for (int i=0; i < chs.length; i++) {
0N/A if (chs[i] > 255) {
0N/A return name.toLowerCase();
0N/A }
0N/A }
0N/A return "latin1";
0N/A } else if (name.startsWith("ISO8859")) {
0N/A // same as latin 1 if all chars < 128
0N/A for (int i=0; i < chs.length; i++) {
0N/A if (chs[i] > 127) {
0N/A return name.toLowerCase();
0N/A }
0N/A }
0N/A return "latin1";
0N/A } else {
0N/A return name.toLowerCase();
0N/A }
0N/A }
0N/A
0N/A private void prepDrawing() {
0N/A
0N/A /* Pop gstates until we can set the needed clip
0N/A * and transform or until we are at the outer most
0N/A * gstate.
0N/A */
0N/A while (isOuterGState() == false
0N/A && (getGState().canSetClip(mLastClip) == false
0N/A || getGState().mTransform.equals(mLastTransform) == false)) {
0N/A
0N/A
0N/A grestore();
0N/A }
0N/A
0N/A /* Set the color. This can push the color to the
0N/A * outer most gsave which is often a good thing.
0N/A */
0N/A getGState().emitPSColor(mLastColor);
0N/A
0N/A /* We do not want to change the outermost
0N/A * transform or clip so if we are at the
0N/A * outer clip the generate a gsave.
0N/A */
0N/A if (isOuterGState()) {
0N/A gsave();
0N/A getGState().emitTransform(mLastTransform);
0N/A getGState().emitPSClip(mLastClip);
0N/A }
0N/A
0N/A /* Set the font if we have been asked to. It is
0N/A * important that the font is set after the
0N/A * transform in order to get the font size
0N/A * correct.
0N/A */
0N/A// if (g != null) {
0N/A// getGState().emitPSFont(g, mLastFont);
0N/A// }
0N/A
0N/A }
0N/A
0N/A /**
0N/A * Return the GState that is currently on top
0N/A * of the GState stack. There should always be
0N/A * a GState on top of the stack. If there isn't
0N/A * then this method will throw an IndexOutOfBounds
0N/A * exception.
0N/A */
0N/A private GState getGState() {
0N/A int count = mGStateStack.size();
0N/A return (GState) mGStateStack.get(count - 1);
0N/A }
0N/A
0N/A /**
0N/A * Emit a PostScript gsave command and add a
0N/A * new GState on to our stack which represents
0N/A * the printer's gstate stack.
0N/A */
0N/A private void gsave() {
0N/A GState oldGState = getGState();
0N/A mGStateStack.add(new GState(oldGState));
0N/A mPSStream.println(GSAVE_STR);
0N/A }
0N/A
0N/A /**
0N/A * Emit a PostScript grestore command and remove
0N/A * a GState from our stack which represents the
0N/A * printer's gstate stack.
0N/A */
0N/A private void grestore() {
0N/A int count = mGStateStack.size();
0N/A mGStateStack.remove(count - 1);
0N/A mPSStream.println(GRESTORE_STR);
0N/A }
0N/A
0N/A /**
0N/A * Return true if the current GState is the
0N/A * outermost GState and therefore should not
0N/A * be restored.
0N/A */
0N/A private boolean isOuterGState() {
0N/A return mGStateStack.size() == 1;
0N/A }
0N/A
0N/A /**
0N/A * A stack of GStates is maintained to model the printer's
0N/A * gstate stack. Each GState holds information about
0N/A * the current graphics attributes.
0N/A */
0N/A private class GState{
0N/A Color mColor;
0N/A Shape mClip;
0N/A Font mFont;
0N/A AffineTransform mTransform;
0N/A
0N/A GState() {
0N/A mColor = Color.black;
0N/A mClip = null;
0N/A mFont = null;
0N/A mTransform = new AffineTransform();
0N/A }
0N/A
0N/A GState(GState copyGState) {
0N/A mColor = copyGState.mColor;
0N/A mClip = copyGState.mClip;
0N/A mFont = copyGState.mFont;
0N/A mTransform = copyGState.mTransform;
0N/A }
0N/A
0N/A boolean canSetClip(Shape clip) {
0N/A
0N/A return mClip == null || mClip.equals(clip);
0N/A }
0N/A
0N/A
0N/A void emitPSClip(Shape clip) {
0N/A if (clip != null
0N/A && (mClip == null || mClip.equals(clip) == false)) {
0N/A String saveFillOp = mFillOpStr;
0N/A String saveClipOp = mClipOpStr;
0N/A convertToPSPath(clip.getPathIterator(new AffineTransform()));
0N/A selectClipPath();
0N/A mClip = clip;
0N/A /* The clip is a shape and has reset the winding rule state */
0N/A mClipOpStr = saveFillOp;
0N/A mFillOpStr = saveFillOp;
0N/A }
0N/A }
0N/A
0N/A void emitTransform(AffineTransform transform) {
0N/A
0N/A if (transform != null && transform.equals(mTransform) == false) {
0N/A double[] matrix = new double[6];
0N/A transform.getMatrix(matrix);
0N/A mPSStream.println("[" + (float)matrix[0]
0N/A + " " + (float)matrix[1]
0N/A + " " + (float)matrix[2]
0N/A + " " + (float)matrix[3]
0N/A + " " + (float)matrix[4]
0N/A + " " + (float)matrix[5]
0N/A + "] concat");
0N/A
0N/A mTransform = transform;
0N/A }
0N/A }
0N/A
0N/A void emitPSColor(Color color) {
0N/A if (color != null && color.equals(mColor) == false) {
0N/A float[] rgb = color.getRGBColorComponents(null);
0N/A
0N/A /* If the color is a gray value then use
0N/A * setgray.
0N/A */
0N/A if (rgb[0] == rgb[1] && rgb[1] == rgb[2]) {
0N/A mPSStream.println(rgb[0] + SETGRAY_STR);
0N/A
0N/A /* It's not gray so use setrgbcolor.
0N/A */
0N/A } else {
0N/A mPSStream.println(rgb[0] + " "
0N/A + rgb[1] + " "
0N/A + rgb[2] + " "
0N/A + SETRGBCOLOR_STR);
0N/A }
0N/A
0N/A mColor = color;
0N/A
0N/A }
0N/A }
0N/A
0N/A void emitPSFont(int psFontIndex, float fontSize) {
0N/A mPSStream.println(fontSize + " " +
0N/A psFontIndex + " " + SetFontName);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Given a Java2D <code>PathIterator</code> instance,
0N/A * this method translates that into a PostScript path..
0N/A */
0N/A void convertToPSPath(PathIterator pathIter) {
0N/A
0N/A float[] segment = new float[6];
0N/A int segmentType;
0N/A
0N/A /* Map the PathIterator's fill rule into the PostScript
0N/A * fill rule.
0N/A */
0N/A int fillRule;
0N/A if (pathIter.getWindingRule() == PathIterator.WIND_EVEN_ODD) {
0N/A fillRule = FILL_EVEN_ODD;
0N/A } else {
0N/A fillRule = FILL_WINDING;
0N/A }
0N/A
0N/A beginPath();
0N/A
0N/A setFillMode(fillRule);
0N/A
0N/A while (pathIter.isDone() == false) {
0N/A segmentType = pathIter.currentSegment(segment);
0N/A
0N/A switch (segmentType) {
0N/A case PathIterator.SEG_MOVETO:
0N/A moveTo(segment[0], segment[1]);
0N/A break;
0N/A
0N/A case PathIterator.SEG_LINETO:
0N/A lineTo(segment[0], segment[1]);
0N/A break;
0N/A
0N/A /* Convert the quad path to a bezier.
0N/A */
0N/A case PathIterator.SEG_QUADTO:
0N/A float lastX = getPenX();
0N/A float lastY = getPenY();
0N/A float c1x = lastX + (segment[0] - lastX) * 2 / 3;
0N/A float c1y = lastY + (segment[1] - lastY) * 2 / 3;
0N/A float c2x = segment[2] - (segment[2] - segment[0]) * 2/ 3;
0N/A float c2y = segment[3] - (segment[3] - segment[1]) * 2/ 3;
0N/A bezierTo(c1x, c1y,
0N/A c2x, c2y,
0N/A segment[2], segment[3]);
0N/A break;
0N/A
0N/A case PathIterator.SEG_CUBICTO:
0N/A bezierTo(segment[0], segment[1],
0N/A segment[2], segment[3],
0N/A segment[4], segment[5]);
0N/A break;
0N/A
0N/A case PathIterator.SEG_CLOSE:
0N/A closeSubpath();
0N/A break;
0N/A }
0N/A
0N/A
0N/A pathIter.next();
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Fill the path defined by <code>pathIter</code>
0N/A * with the specified color.
0N/A * The path is provided in current user space.
0N/A */
0N/A protected void deviceFill(PathIterator pathIter, Color color,
0N/A AffineTransform tx, Shape clip) {
0N/A
0N/A setTransform(tx);
0N/A setClip(clip);
0N/A setColor(color);
0N/A convertToPSPath(pathIter);
0N/A /* Specify the path to fill as the clip, this ensures that only
0N/A * pixels which are inside the path will be filled, which is
0N/A * what the Java 2D APIs specify
0N/A */
0N/A mPSStream.println(GSAVE_STR);
0N/A selectClipPath();
0N/A fillPath();
0N/A mPSStream.println(GRESTORE_STR + " " + NEWPATH_STR);
0N/A }
0N/A
0N/A /*
0N/A * Run length encode byte array in a form suitable for decoding
0N/A * by the PS Level 2 filter RunLengthDecode.
0N/A * Array data to encode is inArr. Encoded data is written to outArr
0N/A * outArr must be long enough to hold the encoded data but this
0N/A * can't be known ahead of time.
0N/A * A safe assumption is to use double the length of the input array.
0N/A * This is then copied into a new array of the correct length which
0N/A * is returned.
0N/A * Algorithm:
0N/A * Encoding is a lead byte followed by data bytes.
0N/A * Lead byte of 0->127 indicates leadByte + 1 distinct bytes follow
0N/A * Lead byte of 129->255 indicates 257 - leadByte is the number of times
0N/A * the following byte is repeated in the source.
0N/A * 128 is a special lead byte indicating end of data (EOD) and is
0N/A * written as the final byte of the returned encoded data.
0N/A */
0N/A private byte[] rlEncode(byte[] inArr) {
0N/A
0N/A int inIndex = 0;
0N/A int outIndex = 0;
0N/A int startIndex = 0;
0N/A int runLen = 0;
0N/A byte[] outArr = new byte[(inArr.length * 2) +2];
0N/A while (inIndex < inArr.length) {
0N/A if (runLen == 0) {
0N/A startIndex = inIndex++;
0N/A runLen=1;
0N/A }
0N/A
0N/A while (runLen < 128 && inIndex < inArr.length &&
0N/A inArr[inIndex] == inArr[startIndex]) {
0N/A runLen++; // count run of same value
0N/A inIndex++;
0N/A }
0N/A
0N/A if (runLen > 1) {
0N/A outArr[outIndex++] = (byte)(257 - runLen);
0N/A outArr[outIndex++] = inArr[startIndex];
0N/A runLen = 0;
0N/A continue; // back to top of while loop.
0N/A }
0N/A
0N/A // if reach here have a run of different values, or at the end.
0N/A while (runLen < 128 && inIndex < inArr.length &&
0N/A inArr[inIndex] != inArr[inIndex-1]) {
0N/A runLen++; // count run of different values
0N/A inIndex++;
0N/A }
0N/A outArr[outIndex++] = (byte)(runLen - 1);
0N/A for (int i = startIndex; i < startIndex+runLen; i++) {
0N/A outArr[outIndex++] = inArr[i];
0N/A }
0N/A runLen = 0;
0N/A }
0N/A outArr[outIndex++] = (byte)128;
0N/A byte[] encodedData = new byte[outIndex];
0N/A System.arraycopy(outArr, 0, encodedData, 0, outIndex);
0N/A
0N/A return encodedData;
0N/A }
0N/A
0N/A /* written acc. to Adobe Spec. "Filtered Files: ASCIIEncode Filter",
0N/A * "PS Language Reference Manual, 2nd edition: Section 3.13"
0N/A */
0N/A private byte[] ascii85Encode(byte[] inArr) {
0N/A byte[] outArr = new byte[((inArr.length+4) * 5 / 4) + 2];
0N/A long p1 = 85;
0N/A long p2 = p1*p1;
0N/A long p3 = p1*p2;
0N/A long p4 = p1*p3;
0N/A byte pling = '!';
0N/A
0N/A int i = 0;
0N/A int olen = 0;
0N/A long val, rem;
0N/A
0N/A while (i+3 < inArr.length) {
0N/A val = ((long)((inArr[i++]&0xff))<<24) +
0N/A ((long)((inArr[i++]&0xff))<<16) +
0N/A ((long)((inArr[i++]&0xff))<< 8) +
0N/A ((long)(inArr[i++]&0xff));
0N/A if (val == 0) {
0N/A outArr[olen++] = 'z';
0N/A } else {
0N/A rem = val;
0N/A outArr[olen++] = (byte)(rem / p4 + pling); rem = rem % p4;
0N/A outArr[olen++] = (byte)(rem / p3 + pling); rem = rem % p3;
0N/A outArr[olen++] = (byte)(rem / p2 + pling); rem = rem % p2;
0N/A outArr[olen++] = (byte)(rem / p1 + pling); rem = rem % p1;
0N/A outArr[olen++] = (byte)(rem + pling);
0N/A }
0N/A }
0N/A // input not a multiple of 4 bytes, write partial output.
0N/A if (i < inArr.length) {
0N/A int n = inArr.length - i; // n bytes remain to be written
0N/A
0N/A val = 0;
0N/A while (i < inArr.length) {
0N/A val = (val << 8) + (inArr[i++]&0xff);
0N/A }
0N/A
0N/A int append = 4 - n;
0N/A while (append-- > 0) {
0N/A val = val << 8;
0N/A }
0N/A byte []c = new byte[5];
0N/A rem = val;
0N/A c[0] = (byte)(rem / p4 + pling); rem = rem % p4;
0N/A c[1] = (byte)(rem / p3 + pling); rem = rem % p3;
0N/A c[2] = (byte)(rem / p2 + pling); rem = rem % p2;
0N/A c[3] = (byte)(rem / p1 + pling); rem = rem % p1;
0N/A c[4] = (byte)(rem + pling);
0N/A
0N/A for (int b = 0; b < n+1 ; b++) {
0N/A outArr[olen++] = c[b];
0N/A }
0N/A }
0N/A
0N/A // write EOD marker.
0N/A outArr[olen++]='~'; outArr[olen++]='>';
0N/A
0N/A /* The original intention was to insert a newline after every 78 bytes.
0N/A * This was mainly intended for legibility but I decided against this
0N/A * partially because of the (small) amount of extra space, and
0N/A * partially because for line breaks either would have to hardwire
0N/A * ascii 10 (newline) or calculate space in bytes to allocate for
0N/A * the platform's newline byte sequence. Also need to be careful
0N/A * about where its inserted:
0N/A * Ascii 85 decoder ignores white space except for one special case:
0N/A * you must ensure you do not split the EOD marker across lines.
0N/A */
0N/A byte[] retArr = new byte[olen];
0N/A System.arraycopy(outArr, 0, retArr, 0, olen);
0N/A return retArr;
0N/A
0N/A }
0N/A
0N/A /**
0N/A * PluginPrinter generates EPSF wrapped with a header and trailer
0N/A * comment. This conforms to the new requirements of Mozilla 1.7
0N/A * and FireFox 1.5 and later. Earlier versions of these browsers
0N/A * did not support plugin printing in the general sense (not just Java).
0N/A * A notable limitation of these browsers is that they handle plugins
0N/A * which would span page boundaries by scaling plugin content to fit on a
0N/A * single page. This means white space is left at the bottom of the
0N/A * previous page and its impossible to print these cases as they appear on
0N/A * the web page. This is contrast to how the same browsers behave on
0N/A * Windows where it renders as on-screen.
0N/A * Cases where the content fits on a single page do work fine, and they
0N/A * are the majority of cases.
0N/A * The scaling that the browser specifies to make the plugin content fit
0N/A * when it is larger than a single page can hold is non-uniform. It
0N/A * scales the axis in which the content is too large just enough to
0N/A * ensure it fits. For content which is extremely long this could lead
0N/A * to noticeable distortion. However that is probably rare enough that
0N/A * its not worth compensating for that here, but we can revisit that if
0N/A * needed, and compensate by making the scale for the other axis the
0N/A * same.
0N/A */
0N/A public static class PluginPrinter implements Printable {
0N/A
0N/A private EPSPrinter epsPrinter;
0N/A private Component applet;
0N/A private PrintStream stream;
0N/A private String epsTitle;
0N/A private int bx, by, bw, bh;
0N/A private int width, height;
0N/A
0N/A /**
0N/A * This is called from the Java Plug-in to print an Applet's
0N/A * contents as EPS to a postscript stream provided by the browser.
0N/A * @param applet the applet component to print.
0N/A * @param stream the print stream provided by the plug-in
0N/A * @param x the x location of the applet panel in the browser window
0N/A * @param y the y location of the applet panel in the browser window
0N/A * @param w the width of the applet panel in the browser window
0N/A * @param h the width of the applet panel in the browser window
0N/A */
0N/A public PluginPrinter(Component applet,
0N/A PrintStream stream,
0N/A int x, int y, int w, int h) {
0N/A
0N/A this.applet = applet;
0N/A this.epsTitle = "Java Plugin Applet";
0N/A this.stream = stream;
0N/A bx = x;
0N/A by = y;
0N/A bw = w;
0N/A bh = h;
0N/A width = applet.size().width;
0N/A height = applet.size().height;
0N/A epsPrinter = new EPSPrinter(this, epsTitle, stream,
0N/A 0, 0, width, height);
0N/A }
0N/A
0N/A public void printPluginPSHeader() {
0N/A stream.println("%%BeginDocument: JavaPluginApplet");
0N/A }
0N/A
0N/A public void printPluginApplet() {
0N/A try {
0N/A epsPrinter.print();
0N/A } catch (PrinterException e) {
0N/A }
0N/A }
0N/A
0N/A public void printPluginPSTrailer() {
0N/A stream.println("%%EndDocument: JavaPluginApplet");
0N/A stream.flush();
0N/A }
0N/A
0N/A public void printAll() {
0N/A printPluginPSHeader();
0N/A printPluginApplet();
0N/A printPluginPSTrailer();
0N/A }
0N/A
0N/A public int print(Graphics g, PageFormat pf, int pgIndex) {
0N/A if (pgIndex > 0) {
0N/A return Printable.NO_SUCH_PAGE;
0N/A } else {
0N/A // "aware" client code can detect that its been passed a
0N/A // PrinterGraphics and could theoretically print
0N/A // differently. I think this is more likely useful than
0N/A // a problem.
0N/A applet.printAll(g);
0N/A return Printable.PAGE_EXISTS;
0N/A }
0N/A }
0N/A
0N/A }
0N/A
0N/A /*
0N/A * This class can take an application-client supplied printable object
0N/A * and send the result to a stream.
0N/A * The application does not need to send any postscript to this stream
0N/A * unless it needs to specify a translation etc.
0N/A * It assumes that its importing application obeys all the conventions
0N/A * for importation of EPS. See Appendix H - Encapsulated Postscript File
0N/A * Format - of the Adobe Postscript Language Reference Manual, 2nd edition.
0N/A * This class could be used as the basis for exposing the ability to
0N/A * generate EPSF from 2D graphics as a StreamPrintService.
0N/A * In that case a MediaPrintableArea attribute could be used to
0N/A * communicate the bounding box.
0N/A */
0N/A public static class EPSPrinter implements Pageable {
0N/A
0N/A private PageFormat pf;
0N/A private PSPrinterJob job;
0N/A private int llx, lly, urx, ury;
0N/A private Printable printable;
0N/A private PrintStream stream;
0N/A private String epsTitle;
0N/A
0N/A public EPSPrinter(Printable printable, String title,
0N/A PrintStream stream,
0N/A int x, int y, int wid, int hgt) {
0N/A
0N/A this.printable = printable;
0N/A this.epsTitle = title;
0N/A this.stream = stream;
0N/A llx = x;
0N/A lly = y;
0N/A urx = llx+wid;
0N/A ury = lly+hgt;
0N/A // construct a PageFormat with zero margins representing the
0N/A // exact bounds of the applet. ie construct a theoretical
0N/A // paper which happens to exactly match applet panel size.
0N/A Paper p = new Paper();
0N/A p.setSize((double)wid, (double)hgt);
0N/A p.setImageableArea(0.0,0.0, (double)wid, (double)hgt);
0N/A pf = new PageFormat();
0N/A pf.setPaper(p);
0N/A }
0N/A
0N/A public void print() throws PrinterException {
0N/A stream.println("%!PS-Adobe-3.0 EPSF-3.0");
0N/A stream.println("%%BoundingBox: " +
0N/A llx + " " + lly + " " + urx + " " + ury);
0N/A stream.println("%%Title: " + epsTitle);
0N/A stream.println("%%Creator: Java Printing");
0N/A stream.println("%%CreationDate: " + new java.util.Date());
0N/A stream.println("%%EndComments");
0N/A stream.println("/pluginSave save def");
0N/A stream.println("mark"); // for restoring stack state on return
0N/A
0N/A job = new PSPrinterJob();
0N/A job.epsPrinter = this; // modifies the behaviour of PSPrinterJob
0N/A job.mPSStream = stream;
0N/A job.mDestType = RasterPrinterJob.STREAM; // prevents closure
0N/A
0N/A job.startDoc();
0N/A try {
0N/A job.printPage(this, 0);
0N/A } catch (Throwable t) {
0N/A if (t instanceof PrinterException) {
0N/A throw (PrinterException)t;
0N/A } else {
0N/A throw new PrinterException(t.toString());
0N/A }
0N/A } finally {
0N/A stream.println("cleartomark"); // restore stack state
0N/A stream.println("pluginSave restore");
0N/A job.endDoc();
0N/A }
0N/A stream.flush();
0N/A }
0N/A
0N/A public int getNumberOfPages() {
0N/A return 1;
0N/A }
0N/A
0N/A public PageFormat getPageFormat(int pgIndex) {
0N/A if (pgIndex > 0) {
0N/A throw new IndexOutOfBoundsException("pgIndex");
0N/A } else {
0N/A return pf;
0N/A }
0N/A }
0N/A
0N/A public Printable getPrintable(int pgIndex) {
0N/A if (pgIndex > 0) {
0N/A throw new IndexOutOfBoundsException("pgIndex");
0N/A } else {
0N/A return printable;
0N/A }
0N/A }
0N/A
0N/A }
0N/A}