View Javadoc

1   // Copyright 2001 freehep
2   package org.freehep.graphicsio.pdf;
3   
4   import java.awt.GradientPaint;
5   import java.awt.Paint;
6   import java.awt.TexturePaint;
7   import java.awt.geom.AffineTransform;
8   import java.awt.geom.Point2D;
9   import java.awt.image.BufferedImage;
10  import java.io.IOException;
11  import java.util.LinkedList;
12  import java.util.List;
13  import java.util.ListIterator;
14  
15  /**
16   * Delay <tt>Paint</tt> objects (gradient/texture, not color) for writing
17   * pattern/shading/function dictionaries to the pdf file when the pageStream is
18   * complete.<br>
19   * TODO: - reuse pattern dictionaries if possible - cyclic function not working
20   * yet (ps calculation)
21   * 
22   * @author Simon Fischer
23   * @version $Id: PDFPaintDelayQueue.java 10270 2007-01-09 18:18:57Z duns $
24   */
25  public class PDFPaintDelayQueue {
26  
27      private static int currentNumber = 0;
28  
29      private class Entry {
30          private Paint paint;
31  
32          private String name;
33  
34          private AffineTransform trafo;
35  
36          private String writeAs;
37  
38          private boolean written;
39  
40          private Entry(Paint paint, AffineTransform trafo, String writeAs) {
41              this.paint = paint;
42              this.trafo = trafo;
43              this.writeAs = writeAs;
44              this.name = "Paint" + (currentNumber++);
45              this.written = false;
46          }
47      }
48  
49      private List paintList;
50  
51      private PDFWriter pdf;
52  
53  //    private PDFImageDelayQueue imageDelayQueue;
54  
55      private AffineTransform pageMatrix;
56  
57      /** Don't forget to call <tt>setPageMatrix()</tt>. */
58      public PDFPaintDelayQueue(PDFWriter pdf, PDFImageDelayQueue imageDelayQueue) {
59          this.pdf = pdf;
60          this.paintList = new LinkedList();
61  //        this.imageDelayQueue = imageDelayQueue;
62          this.pageMatrix = new AffineTransform();
63      }
64  
65      /**
66       * Call this method in order to inform this class about the transformation
67       * that is necessary to map the pattern's coordinate space to the default
68       * coordinate system of the parent's content stream (in our case the
69       * flipping of the page).
70       */
71      public void setPageMatrix(AffineTransform t) {
72          pageMatrix = new AffineTransform(t);
73      }
74  
75      public PDFName delayPaint(Paint paint, AffineTransform transform,
76              String writeAs) {
77          Entry e = new Entry(paint, transform, writeAs);
78          paintList.add(e);
79          return pdf.name(e.name);
80      }
81  
82      /** Creates a stream for every delayed image. */
83      public void processAll() throws IOException {
84          ListIterator i = paintList.listIterator();
85          while (i.hasNext()) {
86              Entry e = (Entry) i.next();
87  
88              if (!e.written) {
89                  e.written = true;
90                  if (e.paint instanceof GradientPaint) {
91                      addGradientPaint(e);
92                  } else if (e.paint instanceof TexturePaint) {
93                      addTexturePaint(e);
94                  } else {
95                      System.err.println("PDFWriter: Paint of class '"
96                              + e.paint.getClass() + "' not supported.");
97                      // FIXME, we could write a color here, to keep the file
98                      // valid.
99                  }
100             }
101         }
102     }
103 
104     /**
105      * Adds all names to the dictionary which should be the value of the
106      * resources dicionrary's /Pattern entry.
107      */
108     public int addPatterns() throws IOException {
109         if (paintList.size() > 0) {
110             PDFDictionary patterns = pdf.openDictionary("Pattern");
111             ListIterator i = paintList.listIterator();
112             while (i.hasNext()) {
113                 Entry e = (Entry) i.next();
114                 patterns.entry(e.name, pdf.ref(e.name));
115             }
116             pdf.close(patterns);
117         }
118         return paintList.size();
119     }
120 
121     private void addGradientPaint(Entry e) throws IOException {
122         GradientPaint gp = (GradientPaint) e.paint;
123 
124         // open the pattern dictionary
125         PDFDictionary pattern = pdf.openDictionary(e.name);
126         pattern.entry("Type", pdf.name("Pattern"));
127         pattern.entry("PatternType", 2);
128         setMatrix(pattern, e, 0, 0);
129 
130         // the shading subdictionary
131         PDFDictionary shading = pattern.openDictionary("Shading");
132         shading.entry("ShadingType", 2);
133         shading.entry("ColorSpace", pdf.name("DeviceRGB"));
134         Point2D p1 = gp.getPoint1();
135         Point2D p2 = gp.getPoint2();
136         shading.entry("Coords", new double[] { p1.getX(), p1.getY(), p2.getX(),
137                 p2.getY() });
138         double[] domain = new double[] { 0, 1 };
139         shading.entry("Domain", domain);
140 
141         // the function subdictionary (necessarily by reference, because
142         // the type 4 function is a stream)
143         String functionRef = e.name + "Function";
144         shading.entry("Function", pdf.ref(functionRef));
145         shading.entry("Extend", new boolean[] { true, true });
146 
147         pattern.close(shading);
148 
149         pdf.close(pattern);
150 
151         // get color components and generate the functions
152         float[] col0 = new float[3];
153         gp.getColor1().getRGBColorComponents(col0);
154         double c0[] = new double[] { col0[0], col0[1], col0[2] };
155         float[] col1 = new float[3];
156         gp.getColor2().getRGBColorComponents(col1);
157         double c1[] = new double[] { col1[0], col1[1], col1[2] };
158         if (gp.isCyclic()) {
159             // FIXME: cyclic function only eveluated between 0 and 1 for short
160             // range, not repetitive.
161             // addCyclicFunction(functionRef, c0, c1, domain);
162             addLinearFunction(functionRef, c0, c1, domain);
163         } else {
164             addLinearFunction(functionRef, c0, c1, domain);
165         }
166     }
167 
168     /** Writes a type 2 (exponential) function (exponent N=1) to the pdf file. */
169     private void addLinearFunction(String functionRef, double[] c0,
170             double[] c1, double[] dom) throws IOException {
171         PDFDictionary function = pdf.openDictionary(functionRef);
172         function.entry("FunctionType", 2);
173         function.entry("Domain", dom);
174         function.entry("Range", new double[] { 0., 1., 0., 1., 0., 1. });
175         function.entry("C0", c0);
176         function.entry("C1", c1);
177         function.entry("N", 1);
178         pdf.close(function);
179     }
180 
181     /**
182      * Writes a type 4 (PostScript calculator) function that describes a
183      * triangle function to the pdf file.
184      */
185     // NOT USED
186     protected void addCyclicFunction(String functionRef, double[] c0,
187             double[] c1, double[] dom) throws IOException {
188         // PDFStream function = pdf.openStream(functionRef, new String[] {
189         // "Flate", "ASCII85" });
190         PDFStream function = pdf.openStream(functionRef);
191         function.entry("FunctionType", 4);
192         // function.entry("Domain", new double[] {-1000.0, 1000.0} );
193         function.entry("Domain", dom);
194         function.entry("Range", new double[] { 0., 1., 0., 1., 0., 1. });
195         function.println("{");
196         /*
197          * function.println("2 mod"); function.println("1 sub");
198          * function.println("abs"); function.println("1 exch sub");
199          */
200         // function.println("sin");
201         for (int i = 0; i < 3; i++) {
202             if (i < 2)
203                 function.println("dup");
204             function.println((c1[i] - c0[i]) + " mul");
205             function.println(c0[i] + " add");
206             if (i < 2)
207                 function.println("exch");
208         }
209 
210         // function.println("pop 0.8 0.0 0.0");
211 
212         function.println("}");
213         pdf.close(function);
214     }
215 
216     private void addTexturePaint(Entry e) throws IOException {
217         TexturePaint tp = (TexturePaint) e.paint;
218 
219         // open the pattern stream that also holds the inlined picture
220         PDFStream pattern = pdf.openStream(e.name, null); // image is encoded.
221         pattern.entry("Type", pdf.name("Pattern"));
222         pattern.entry("PatternType", 1);
223         pattern.entry("PaintType", 1);
224         BufferedImage image = tp.getImage();
225         pattern.entry("TilingType", 1);
226         double width = tp.getAnchorRect().getWidth();
227         double height = tp.getAnchorRect().getHeight();
228         double offsX = tp.getAnchorRect().getX();
229         double offsY = tp.getAnchorRect().getY();
230         pattern.entry("BBox", new double[] { 0, 0, width, height });
231         pattern.entry("XStep", width);
232         pattern.entry("YStep", height);
233         PDFDictionary resources = pattern.openDictionary("Resources");
234         resources.entry("ProcSet", new Object[] { pdf.name("PDF"),
235                 pdf.name("ImageC") });
236         pattern.close(resources);
237         setMatrix(pattern, e, offsX, offsY);
238 
239         // scale the tiling image to the correct size
240         pattern.matrix(width, 0, 0, -height, 0, height);
241 
242         pattern.inlineImage(image, null, e.writeAs);
243         pdf.close(pattern);
244     }
245 
246     /**
247      * Sets both the page (flip) matrix and the actual transformation matrix
248      * plus a translation offset (which may of course be be 0).
249      */
250     private void setMatrix(PDFDictionary dict, Entry e, double translX,
251             double translY) throws IOException {
252         // concatenate the page matrix and the actual transformation matrix
253         AffineTransform trafo = new AffineTransform(pageMatrix);
254         trafo.concatenate(e.trafo);
255         trafo.translate(translX, translY);
256         double[] matrix = new double[6];
257         trafo.getMatrix(matrix);
258         dict.entry("Matrix", new double[] { matrix[0], matrix[1], matrix[2],
259                 matrix[3], matrix[4], matrix[5] });
260     }
261 }