Merge branch 'master' of https://source.jalview.org/git/jalviewjs.git
[jalviewjs.git] / src / javajs / export / PDFCreator.java
index 5055d7b..8ffc540 100644 (file)
-/* $RCSfile$\r
- * $Author: hansonr $\r
- * $Date: 2009-06-30 18:58:33 -0500 (Tue, 30 Jun 2009) $\r
- * $Revision: 11158 $\r
- *\r
- * Copyright (C) 2002-2005  The Jmol Development Team\r
- *\r
- * Contact: jmol-developers@lists.sf.net\r
- *\r
- *  This library is free software; you can redistribute it and/or\r
- *  modify it under the terms of the GNU Lesser General Public\r
- *  License as published by the Free Software Foundation; either\r
- *  version 2.1 of the License, or (at your option) any later version.\r
- *\r
- *  This library is distributed in the hope that it will be useful,\r
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
- *  Lesser General Public License for more details.\r
- *\r
- *  You should have received a copy of the GNU Lesser General Public\r
- *  License along with this library; if not, write to the Free Software\r
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r
- */\r
-package javajs.export;\r
-\r
-import java.io.IOException;\r
-import java.io.OutputStream;\r
-import java.util.Hashtable;\r
-import java.util.Map;\r
-import java.util.Map.Entry;\r
-\r
-import javajs.util.Lst;\r
-import javajs.util.SB;\r
-\r
-\r
-\r
-public class PDFCreator {\r
\r
-  private OutputStream os;\r
-  private Lst<PDFObject> indirectObjects;\r
-  private PDFObject root;\r
-  private PDFObject graphics; \r
-//  private PDFObject pageResources;\r
-//  private PDFObject graphicsResources;\r
-\r
-  private int pt;\r
-  private int xrefPt;\r
-  private int count;\r
-\r
-  private int height;\r
-  private int width;\r
-  \r
-  private Map<String, PDFObject>fonts;\r
-\r
-  public PDFCreator() {\r
-   // for Class.forName  \r
-  }\r
-\r
-  public void setOutputStream(OutputStream os) {\r
-    this.os = os;\r
-  }\r
-\r
-  public void newDocument(int paperWidth, int paperHeight, boolean isLandscape) {\r
-    width = (isLandscape ? paperHeight : paperWidth);\r
-    height = (isLandscape ? paperWidth : paperHeight);\r
-    System.out.println("Creating PDF with width=" + width + " and height=" + height);\r
-    fonts = new Hashtable<String, PDFObject>();\r
-    indirectObjects = new Lst<PDFObject>();\r
-    //graphicsResources = newObject(null);\r
-    //pageResources = newObject(null); // will set this to compressed stream later\r
-    root = newObject("Catalog");\r
-    PDFObject pages = newObject("Pages");\r
-    PDFObject page = newObject("Page");\r
-    PDFObject pageContents = newObject(null);\r
-    graphics = newObject("XObject");\r
-    \r
-    root.addDef("Pages", pages.getRef());\r
-    pages.addDef("Count", "1");\r
-    pages.addDef("Kids", "[ " + page.getRef() +" ]");\r
-    page.addDef("Parent", pages.getRef());\r
-    page.addDef("MediaBox", "[ 0 0 " + paperWidth + " " + paperHeight + " ]");\r
-    if (isLandscape)\r
-      page.addDef("Rotate", "90");\r
-\r
-    pageContents.addDef("Length", "?");\r
-    pageContents.append((isLandscape ? "q 0 1 1 0 0 0 " : "q 1 0 0 -1 0 "+(paperHeight))+" cm /" + graphics.getID() + " Do Q");\r
-    page.addDef("Contents", pageContents.getRef());   \r
-    addProcSet(page);\r
-    addProcSet(graphics);\r
-    // will add fonts as well as they are needed\r
-    graphics.addDef("Subtype", "/Form");\r
-    graphics.addDef("FormType", "1");\r
-    graphics.addDef("BBox", "[0 0 " + width + " " + height + "]");\r
-    graphics.addDef("Matrix", "[1 0 0 1 0 0]");\r
-    graphics.addDef("Length", "?");\r
-    page.addResource("XObject", graphics.getID(), graphics.getRef());   \r
-    g("q 1 w 1 J 1 j 10 M []0 d q "); // line width 1, line cap circle, line join circle, miter limit 10, solid\r
-    clip(0, 0, width, height);\r
-  }   \r
-\r
-  private void addProcSet(PDFObject o) {\r
-    o.addResource(null, "ProcSet", "[/PDF /Text /ImageB /ImageC /ImageI]");\r
-  }\r
-\r
-  private void clip(int x1, int y1, int x2, int y2) {\r
-    moveto(x1, y1);\r
-    lineto(x2, y1);\r
-    lineto(x2, y2);\r
-    lineto(x1, y2);\r
-    g("h W n");\r
-  }\r
-\r
-  public void moveto(int x, int y) {\r
-    g(x + " " + y  + " m");\r
-  }\r
-\r
-  public void lineto(int x, int y) {\r
-    g(x + " " + y  + " l");\r
-  }\r
-\r
-  private PDFObject newObject(String type) {\r
-    PDFObject o = new PDFObject(++count);\r
-    if (type != null)\r
-      o.addDef("Type", "/" + type);\r
-    indirectObjects.addLast(o);\r
-    return o;\r
-  }\r
-\r
-  public void addInfo(Map<String, String> data) {\r
-    Hashtable<String, Object> info = new Hashtable<String, Object>();\r
-    for (Entry<String, String> e: data.entrySet()) {\r
-      String value = "(" + e.getValue().replace(')','_').replace('(','_')+ ")";\r
-      info.put(e.getKey(), value);      \r
-    }\r
-    root.addDef("Info", info);\r
-  }\r
-\r
-  private PDFObject addFontResource(String fname) {\r
-    PDFObject f = newObject("Font");\r
-    fonts.put(fname, f);\r
-    f.addDef("BaseFont", fname);\r
-    f.addDef("Encoding", "/WinAnsiEncoding");\r
-    f.addDef("Subtype", "/Type1");\r
-    graphics.addResource("Font", f.getID(), f.getRef());\r
-    return f;\r
-  }\r
-\r
-  private Map<Object, PDFObject> images;\r
-  \r
-  public void addImageResource(Object newImage, int width, int height, int[] buffer, boolean isRGB) {\r
-    PDFObject imageObj = newObject("XObject");\r
-    if (images == null)\r
-      images = new Hashtable<Object, PDFObject>();\r
-    images.put(newImage, imageObj);   \r
-    imageObj.addDef("Subtype", "/Image");\r
-    imageObj.addDef("Length", "?");\r
-    imageObj.addDef("ColorSpace", isRGB ? "/DeviceRGB" : "/DeviceGray");\r
-    imageObj.addDef("BitsPerComponent", "8");\r
-    imageObj.addDef("Width", "" + width);\r
-    imageObj.addDef("Height", "" + height);\r
-    graphics.addResource("XObject", imageObj.getID(), imageObj.getRef());\r
-    int n = buffer.length;\r
-    byte[] stream = new byte[n * (isRGB ? 3 : 1)];\r
-    if (isRGB) {\r
-      for (int i = 0, pt = 0; i < n; i++) {\r
-        stream[pt++] = (byte) ((buffer[i] >> 16) & 0xFF);\r
-        stream[pt++] = (byte) ((buffer[i] >> 8) & 0xFF);\r
-        stream[pt++] = (byte) (buffer[i] & 0xFF);\r
-      }\r
-    } else {\r
-      for (int i = 0; i < n; i++)\r
-        stream[i] = (byte) buffer[i];\r
-    }\r
-    imageObj.setStream(stream);\r
-    graphics.addResource("XObject", imageObj.getID(), imageObj.getRef());\r
-  }\r
-\r
-  public void g(String cmd) {\r
-    graphics.append(cmd).appendC('\n');\r
-  }\r
-\r
-  private void output(String s) throws IOException {\r
-   byte[] b = s.getBytes();\r
-   os.write(b, 0, b.length);\r
-   pt += b.length;\r
-  }\r
-\r
-  public void closeDocument() throws IOException {\r
-    g("Q Q");\r
-    outputHeader();\r
-    writeObjects();\r
-    writeXRefTable();\r
-    writeTrailer();\r
-    os.flush();\r
-    os.close();\r
-  }\r
-\r
-  private void outputHeader() throws IOException {\r
-    output("%PDF-1.3\n%");\r
-    byte[] b = new byte[] {-1, -1, -1, -1};\r
-    os.write(b, 0, b.length);\r
-    pt += 4;\r
-    output("\n");\r
-  }\r
-\r
-  private void writeTrailer() throws IOException {\r
-    PDFObject trailer = new PDFObject(-2);\r
-    output("trailer");\r
-    trailer.addDef("Size", "" + indirectObjects.size());\r
-    trailer.addDef("Root", root.getRef());\r
-    trailer.output(os);\r
-    output("startxref\n");\r
-    output("" + xrefPt + "\n");\r
-    output("%%EOF\n");\r
-  }\r
-\r
-  /**\r
-   * Write Font objects first.\r
-   * \r
-   * @throws IOException\r
-   */\r
-  private void writeObjects() throws IOException {\r
-    int nObj = indirectObjects.size();\r
-    for (int i = 0; i < nObj; i++) {\r
-      PDFObject o = indirectObjects.get(i);\r
-      if (!o.isFont())\r
-        continue;\r
-      o.pt = pt;\r
-      pt += o.output(os);\r
-    }\r
-    for (int i = 0; i < nObj; i++) {\r
-      PDFObject o = indirectObjects.get(i);\r
-      if (o.isFont())\r
-        continue;\r
-      o.pt = pt;\r
-      pt += o.output(os);\r
-    }\r
-  }\r
-\r
-  private void writeXRefTable() throws IOException {\r
-    xrefPt = pt;\r
-    int nObj = indirectObjects.size();\r
-    SB sb = new SB();\r
-    // note trailing space, needed because \n is just one character\r
-    sb.append("xref\n0 " + (nObj + 1) \r
-        + "\n0000000000 65535 f\r\n");\r
-    for (int i = 0; i < nObj; i++) {\r
-      PDFObject o = indirectObjects.get(i);\r
-      String s = "0000000000" + o.pt;\r
-      sb.append(s.substring(s.length() - 10));\r
-      sb.append(" 00000 n\r\n");\r
-    }\r
-    output(sb.toString());\r
-  }\r
-\r
-  public boolean canDoLineTo() {\r
-    return true;\r
-  }\r
-\r
-  public void fill() {\r
-    g("f");   \r
-  }\r
-\r
-  public void stroke() {\r
-    g("S");   \r
-  }\r
-\r
-  public void doCircle(int x, int y, int r, boolean doFill) {\r
-    double d = r*4*(Math.sqrt(2)-1)/3;\r
-    double dx = x;\r
-    double dy = y;\r
-    g((dx + r) + " " + dy + " m");\r
-    g((dx + r) + " " + (dy + d) + " " + (dx + d) + " " + (dy + r) + " " + (dx) + " " + (dy + r) + " "  + " c");\r
-    g((dx - d) + " " + (dy + r) + " " + (dx - r) + " " + (dy + d) + " " + (dx - r) + " " + (dy) + " c");\r
-    g((dx - r) + " " + (dy - d) + " " + (dx - d) + " " + (dy - r) + " " + (dx) + " " + (dy - r) + " c");\r
-    g((dx + d) + " " + (dy - r) + " " + (dx + r) + " " + (dy - d) + " " + (dx + r) + " " + (dy) + " c");\r
-    g(doFill ? "f" : "s");\r
-  }\r
-\r
-  public void doPolygon(int[] axPoints, int[] ayPoints, int nPoints, boolean doFill) {\r
-    moveto(axPoints[0], ayPoints[0]);\r
-    for (int i = 1; i < nPoints; i++)\r
-      lineto(axPoints[i], ayPoints[i]);\r
-    g(doFill ? "f" : "s");\r
-  }\r
-\r
-  public void doRect(int x, int y, int width, int height, boolean doFill) {\r
-    g(x + " " + y + " " + width + " " + height + " re " + (doFill ? "f" : "s"));\r
-  }\r
-\r
-  public void drawImage(Object image, int destX0, int destY0,\r
-      int destX1, int destY1, int srcX0, int srcY0, int srcX1, int srcY1) {\r
-    PDFObject imageObj = images.get(image);\r
-    if (imageObj == null)\r
-      return;\r
-    g("q");\r
-    clip(destX0, destY0, destX1, destY1);\r
-    double iw = Double.parseDouble((String) imageObj.getDef("Width"));\r
-    double ih = Double.parseDouble((String) imageObj.getDef("Height"));   \r
-    double dw = (destX1 - destX0 + 1);\r
-    double dh  = (destY1 - destY0 + 1);\r
-    double sw = (srcX1 - srcX0 + 1);\r
-    double sh = (srcY1 - srcY0 + 1);\r
-    double scaleX = dw / sw;\r
-    double scaleY = dh / sh;\r
-    double transX = destX0 - srcX0 * scaleX;\r
-    double transY = destY0 + (ih - srcY0) * scaleY;\r
-    g(scaleX*iw + " 0 0 " + -scaleY*ih + " " + transX + " " + transY + " cm");\r
-    g("/" + imageObj.getID() + " Do");\r
-    g("Q");\r
-  }\r
-\r
-  public void drawStringRotated(String s, int x, int y, int angle) {\r
-    g("q " + getRotation(angle) + " " + x + " " + y\r
-        + " cm BT(" + s + ")Tj ET Q");\r
-  }\r
-\r
-  public String getRotation(int angle) {    \r
-    float cos = 0, sin = 0;\r
-    switch (angle) {\r
-    case 0:\r
-      cos = 1;\r
-      break;\r
-    case 90:\r
-      sin = 1;\r
-      break;\r
-    case -90:\r
-      sin = -1;\r
-      break;\r
-    case 180:\r
-      cos = -1;\r
-      break;\r
-    default:\r
-      float a = (float) (angle / 180.0 * Math.PI);\r
-      cos = (float) Math.cos(a);\r
-      sin = (float) Math.sin(a);\r
-      if (Math.abs(cos) < 0.0001)\r
-        cos = 0;\r
-      if (Math.abs(sin) < 0.0001)\r
-        sin = 0;\r
-    }\r
-    return  cos + " " + sin + " " + sin + " " + -cos;\r
-  }\r
-\r
-  public void setColor(float[] rgb, boolean isFill) {\r
-    g(rgb[0] + " " + rgb[1] + " " + rgb[2] + (isFill ? " rg" : " RG"));\r
-  }\r
-\r
-  public void setFont(String fname, float size) {\r
-    PDFObject f = fonts.get(fname);\r
-    if (f == null)\r
-      f = addFontResource(fname);\r
-    g("/" + f.getID() + " " + size + " Tf");\r
-  }\r
-\r
-  public void setLineWidth(float width) {\r
-    g(width + " w");    \r
-  }\r
-\r
-  public void translateScale(float x, float y, float scale) {\r
-    g(scale + " 0 0 " + scale + " " + x + " " + y + " cm");\r
-  }\r
-\r
-}\r
+/* $RCSfile$
+ * $Author: hansonr $
+ * $Date: 2009-06-30 18:58:33 -0500 (Tue, 30 Jun 2009) $
+ * $Revision: 11158 $
+ *
+ * Copyright (C) 2002-2005  The Jmol Development Team
+ *
+ * Contact: jmol-developers@lists.sf.net
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+package javajs.export;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javajs.util.Lst;
+import javajs.util.SB;
+
+
+
+public class PDFCreator {
+  private OutputStream os;
+  private Lst<PDFObject> indirectObjects;
+  private PDFObject root;
+  private PDFObject graphics; 
+//  private PDFObject pageResources;
+//  private PDFObject graphicsResources;
+
+  private int pt;
+  private int xrefPt;
+  private int count;
+
+  private int height;
+  private int width;
+  
+  private Map<String, PDFObject>fonts;
+
+  public PDFCreator() {
+   // for Class.forName  
+  }
+
+  public void setOutputStream(OutputStream os) {
+    this.os = os;
+  }
+
+  public void newDocument(int paperWidth, int paperHeight, boolean isLandscape) {
+    width = (isLandscape ? paperHeight : paperWidth);
+    height = (isLandscape ? paperWidth : paperHeight);
+    System.out.println("Creating PDF with width=" + width + " and height=" + height);
+    fonts = new Hashtable<String, PDFObject>();
+    indirectObjects = new Lst<PDFObject>();
+    //graphicsResources = newObject(null);
+    //pageResources = newObject(null); // will set this to compressed stream later
+    root = newObject("Catalog");
+    PDFObject pages = newObject("Pages");
+    PDFObject page = newObject("Page");
+    PDFObject pageContents = newObject(null);
+    graphics = newObject("XObject");
+    
+    root.addDef("Pages", pages.getRef());
+    pages.addDef("Count", "1");
+    pages.addDef("Kids", "[ " + page.getRef() +" ]");
+    page.addDef("Parent", pages.getRef());
+    page.addDef("MediaBox", "[ 0 0 " + paperWidth + " " + paperHeight + " ]");
+    if (isLandscape)
+      page.addDef("Rotate", "90");
+
+    pageContents.addDef("Length", "?");
+    pageContents.append((isLandscape ? "q 0 1 1 0 0 0 " : "q 1 0 0 -1 0 "+(paperHeight))+" cm /" + graphics.getID() + " Do Q");
+    page.addDef("Contents", pageContents.getRef());   
+    addProcSet(page);
+    addProcSet(graphics);
+    // will add fonts as well as they are needed
+    graphics.addDef("Subtype", "/Form");
+    graphics.addDef("FormType", "1");
+    graphics.addDef("BBox", "[0 0 " + width + " " + height + "]");
+    graphics.addDef("Matrix", "[1 0 0 1 0 0]");
+    graphics.addDef("Length", "?");
+    page.addResource("XObject", graphics.getID(), graphics.getRef());   
+    g("q 1 w 1 J 1 j 10 M []0 d q "); // line width 1, line cap circle, line join circle, miter limit 10, solid
+    clip(0, 0, width, height);
+  }   
+
+  private void addProcSet(PDFObject o) {
+    o.addResource(null, "ProcSet", "[/PDF /Text /ImageB /ImageC /ImageI]");
+  }
+
+  private void clip(int x1, int y1, int x2, int y2) {
+    moveto(x1, y1);
+    lineto(x2, y1);
+    lineto(x2, y2);
+    lineto(x1, y2);
+    g("h W n");
+  }
+
+  public void moveto(int x, int y) {
+    g(x + " " + y  + " m");
+  }
+
+  public void lineto(int x, int y) {
+    g(x + " " + y  + " l");
+  }
+
+  private PDFObject newObject(String type) {
+    PDFObject o = new PDFObject(++count);
+    if (type != null)
+      o.addDef("Type", "/" + type);
+    indirectObjects.addLast(o);
+    return o;
+  }
+
+  public void addInfo(Map<String, String> data) {
+    Hashtable<String, Object> info = new Hashtable<String, Object>();
+    for (Entry<String, String> e: data.entrySet()) {
+      String value = "(" + e.getValue().replace(')','_').replace('(','_')+ ")";
+      info.put(e.getKey(), value);      
+    }
+    root.addDef("Info", info);
+  }
+
+  private PDFObject addFontResource(String fname) {
+    PDFObject f = newObject("Font");
+    fonts.put(fname, f);
+    f.addDef("BaseFont", fname);
+    f.addDef("Encoding", "/WinAnsiEncoding");
+    f.addDef("Subtype", "/Type1");
+    graphics.addResource("Font", f.getID(), f.getRef());
+    return f;
+  }
+
+  private Map<Object, PDFObject> images;
+  
+  public void addImageResource(Object newImage, int width, int height, int[] buffer, boolean isRGB) {
+    PDFObject imageObj = newObject("XObject");
+    if (images == null)
+      images = new Hashtable<Object, PDFObject>();
+    images.put(newImage, imageObj);   
+    imageObj.addDef("Subtype", "/Image");
+    imageObj.addDef("Length", "?");
+    imageObj.addDef("ColorSpace", isRGB ? "/DeviceRGB" : "/DeviceGray");
+    imageObj.addDef("BitsPerComponent", "8");
+    imageObj.addDef("Width", "" + width);
+    imageObj.addDef("Height", "" + height);
+    graphics.addResource("XObject", imageObj.getID(), imageObj.getRef());
+    int n = buffer.length;
+    byte[] stream = new byte[n * (isRGB ? 3 : 1)];
+    if (isRGB) {
+      for (int i = 0, pt = 0; i < n; i++) {
+        stream[pt++] = (byte) ((buffer[i] >> 16) & 0xFF);
+        stream[pt++] = (byte) ((buffer[i] >> 8) & 0xFF);
+        stream[pt++] = (byte) (buffer[i] & 0xFF);
+      }
+    } else {
+      for (int i = 0; i < n; i++)
+        stream[i] = (byte) buffer[i];
+    }
+    imageObj.setStream(stream);
+    graphics.addResource("XObject", imageObj.getID(), imageObj.getRef());
+  }
+
+  public void g(String cmd) {
+    graphics.append(cmd).appendC('\n');
+  }
+
+  private void output(String s) throws IOException {
+   byte[] b = s.getBytes();
+   os.write(b, 0, b.length);
+   pt += b.length;
+  }
+
+  public void closeDocument() throws IOException {
+    g("Q Q");
+    outputHeader();
+    writeObjects();
+    writeXRefTable();
+    writeTrailer();
+    os.flush();
+    os.close();
+  }
+
+  private void outputHeader() throws IOException {
+    output("%PDF-1.3\n%");
+    byte[] b = new byte[] {-1, -1, -1, -1};
+    os.write(b, 0, b.length);
+    pt += 4;
+    output("\n");
+  }
+
+  private void writeTrailer() throws IOException {
+    PDFObject trailer = new PDFObject(-2);
+    output("trailer");
+    trailer.addDef("Size", "" + indirectObjects.size());
+    trailer.addDef("Root", root.getRef());
+    trailer.output(os);
+    output("startxref\n");
+    output("" + xrefPt + "\n");
+    output("%%EOF\n");
+  }
+
+  /**
+   * Write Font objects first.
+   * 
+   * @throws IOException
+   */
+  private void writeObjects() throws IOException {
+    int nObj = indirectObjects.size();
+    for (int i = 0; i < nObj; i++) {
+      PDFObject o = indirectObjects.get(i);
+      if (!o.isFont())
+        continue;
+      o.pt = pt;
+      pt += o.output(os);
+    }
+    for (int i = 0; i < nObj; i++) {
+      PDFObject o = indirectObjects.get(i);
+      if (o.isFont())
+        continue;
+      o.pt = pt;
+      pt += o.output(os);
+    }
+  }
+
+  private void writeXRefTable() throws IOException {
+    xrefPt = pt;
+    int nObj = indirectObjects.size();
+    SB sb = new SB();
+    // note trailing space, needed because \n is just one character
+    sb.append("xref\n0 " + (nObj + 1) 
+        + "\n0000000000 65535 f\r\n");
+    for (int i = 0; i < nObj; i++) {
+      PDFObject o = indirectObjects.get(i);
+      String s = "0000000000" + o.pt;
+      sb.append(s.substring(s.length() - 10));
+      sb.append(" 00000 n\r\n");
+    }
+    output(sb.toString());
+  }
+
+  public boolean canDoLineTo() {
+    return true;
+  }
+
+  public void fill() {
+    g("f");   
+  }
+
+  public void stroke() {
+    g("S");   
+  }
+
+  public void doCircle(int x, int y, int r, boolean doFill) {
+    double d = r*4*(Math.sqrt(2)-1)/3;
+    double dx = x;
+    double dy = y;
+    g((dx + r) + " " + dy + " m");
+    g((dx + r) + " " + (dy + d) + " " + (dx + d) + " " + (dy + r) + " " + (dx) + " " + (dy + r) + " "  + " c");
+    g((dx - d) + " " + (dy + r) + " " + (dx - r) + " " + (dy + d) + " " + (dx - r) + " " + (dy) + " c");
+    g((dx - r) + " " + (dy - d) + " " + (dx - d) + " " + (dy - r) + " " + (dx) + " " + (dy - r) + " c");
+    g((dx + d) + " " + (dy - r) + " " + (dx + r) + " " + (dy - d) + " " + (dx + r) + " " + (dy) + " c");
+    g(doFill ? "f" : "s");
+  }
+
+  public void doPolygon(int[] axPoints, int[] ayPoints, int nPoints, boolean doFill) {
+    moveto(axPoints[0], ayPoints[0]);
+    for (int i = 1; i < nPoints; i++)
+      lineto(axPoints[i], ayPoints[i]);
+    g(doFill ? "f" : "s");
+  }
+
+  public void doRect(int x, int y, int width, int height, boolean doFill) {
+    g(x + " " + y + " " + width + " " + height + " re " + (doFill ? "f" : "s"));
+  }
+
+  public void drawImage(Object image, int destX0, int destY0,
+      int destX1, int destY1, int srcX0, int srcY0, int srcX1, int srcY1) {
+    PDFObject imageObj = images.get(image);
+    if (imageObj == null)
+      return;
+    g("q");
+    clip(destX0, destY0, destX1, destY1);
+    double iw = Double.parseDouble((String) imageObj.getDef("Width"));
+    double ih = Double.parseDouble((String) imageObj.getDef("Height"));   
+    double dw = (destX1 - destX0 + 1);
+    double dh  = (destY1 - destY0 + 1);
+    double sw = (srcX1 - srcX0 + 1);
+    double sh = (srcY1 - srcY0 + 1);
+    double scaleX = dw / sw;
+    double scaleY = dh / sh;
+    double transX = destX0 - srcX0 * scaleX;
+    double transY = destY0 + (ih - srcY0) * scaleY;
+    g(scaleX*iw + " 0 0 " + -scaleY*ih + " " + transX + " " + transY + " cm");
+    g("/" + imageObj.getID() + " Do");
+    g("Q");
+  }
+
+  public void drawStringRotated(String s, int x, int y, int angle) {
+    g("q " + getRotation(angle) + " " + x + " " + y
+        + " cm BT(" + s + ")Tj ET Q");
+  }
+
+  public String getRotation(int angle) {    
+    float cos = 0, sin = 0;
+    switch (angle) {
+    case 0:
+      cos = 1;
+      break;
+    case 90:
+      sin = 1;
+      break;
+    case -90:
+      sin = -1;
+      break;
+    case 180:
+      cos = -1;
+      break;
+    default:
+      float a = (float) (angle / 180.0 * Math.PI);
+      cos = (float) Math.cos(a);
+      sin = (float) Math.sin(a);
+      if (Math.abs(cos) < 0.0001)
+        cos = 0;
+      if (Math.abs(sin) < 0.0001)
+        sin = 0;
+    }
+    return  cos + " " + sin + " " + sin + " " + -cos;
+  }
+
+  public void setColor(float[] rgb, boolean isFill) {
+    g(rgb[0] + " " + rgb[1] + " " + rgb[2] + (isFill ? " rg" : " RG"));
+  }
+
+  public void setFont(String fname, float size) {
+    PDFObject f = fonts.get(fname);
+    if (f == null)
+      f = addFontResource(fname);
+    g("/" + f.getID() + " " + size + " Tf");
+  }
+
+  public void setLineWidth(float width) {
+    g(width + " w");    
+  }
+
+  public void translateScale(float x, float y, float scale) {
+    g(scale + " 0 0 " + scale + " " + x + " " + y + " cm");
+  }
+
+}