KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > it > stefanochizzolini > clown > documents > contents > composition > ContentBuilder


1 /*
2   Copyright © 2006,2007 Stefano Chizzolini. http://clown.stefanochizzolini.it
3
4   Contributors:
5     * Stefano Chizzolini (original code developer, info@stefanochizzolini.it):
6       contributed code is Copyright © 2006,2007 by Stefano Chizzolini.
7
8   This file should be part of the source code distribution of "PDF Clown library"
9   (the Program): see the accompanying README files for more info.
10
11   This Program is free software; you can redistribute it and/or modify it under
12   the terms of the GNU General Public License as published by the Free Software
13   Foundation; either version 2 of the License, or (at your option) any later version.
14
15   This Program is distributed in the hope that it will be useful, but WITHOUT ANY
16   WARRANTY, either expressed or implied; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more details.
18
19   You should have received a copy of the GNU General Public License along with this
20   Program (see README files); if not, go to the GNU website (http://www.gnu.org/).
21
22   Redistribution and use, with or without modification, are permitted provided that such
23   redistributions retain the above copyright notice, license and disclaimer, along with
24   this list of conditions.
25 */

26
27 package it.stefanochizzolini.clown.documents.contents.composition;
28
29 import it.stefanochizzolini.clown.bytes.IBuffer;
30 import it.stefanochizzolini.clown.documents.Document;
31 import it.stefanochizzolini.clown.documents.contents.ContentStream;
32 import it.stefanochizzolini.clown.documents.contents.LineCapEnum;
33 import it.stefanochizzolini.clown.documents.contents.LineJoinEnum;
34 import it.stefanochizzolini.clown.documents.contents.TextRenderingModeEnum;
35 import it.stefanochizzolini.clown.documents.contents.colorSpaces.Color;
36 import it.stefanochizzolini.clown.documents.contents.colorSpaces.ColorSpace;
37 import it.stefanochizzolini.clown.documents.contents.fonts.Font;
38 import it.stefanochizzolini.clown.files.File;
39 import it.stefanochizzolini.clown.objects.IPdfNumber;
40 import it.stefanochizzolini.clown.objects.PdfDictionary;
41 import it.stefanochizzolini.clown.objects.PdfLiteral;
42 import it.stefanochizzolini.clown.objects.PdfName;
43 import it.stefanochizzolini.clown.objects.PdfReal;
44 import it.stefanochizzolini.clown.objects.PdfReference;
45 import it.stefanochizzolini.clown.util.NotImplementedException;
46
47 import java.awt.geom.Dimension2D JavaDoc;
48 import java.awt.geom.Point2D JavaDoc;
49 import java.awt.geom.RectangularShape JavaDoc;
50 import java.util.Stack JavaDoc;
51
52 /**
53   Content stream fragment builder.
54 */

55 public class ContentBuilder
56 {
57   // <class>
58
// <classes>
59
public static class GraphicsState
60     implements Cloneable JavaDoc
61   {
62     /*
63       TODO:IMPL Graphics state parameters to be implemented (spacing, scaling, leading...)!!!
64       TODO:IMPL It'd be nice to support such state object on sequential parsing of existing content streams: it'd reveal parameter changes, coordinate matrices' current state, and so on...
65     */

66     private Font font;
67     private double fontSize;
68
69     public GraphicsState clone(
70       )
71     {
72       try
73       {return (GraphicsState)super.clone();}
74       catch(CloneNotSupportedException JavaDoc e)
75       {throw new RuntimeException JavaDoc("Unable to clone.",e);}
76     }
77
78     public Font getFont(
79       )
80     {return font;}
81
82     public double getFontSize(
83       )
84     {return fontSize;}
85
86     void setFont(
87       Font value
88       )
89     {font = value;}
90
91     void setFontSize(
92       double value
93       )
94     {fontSize = value;}
95   }
96   // </classes>
97

98   // <dynamic>
99
// <fields>
100
/**
101     Current graphics state.
102   */

103   private GraphicsState state = new GraphicsState();
104   private IBuffer buffer;
105   private ContentStream context;
106   /**
107     As <code>state</code> caches the current graphics state only, we need to define
108     a state stack in order to keep track of context switching [PDF:1.6:4.3.1].
109   */

110   private Stack JavaDoc<GraphicsState> stateStack = new Stack JavaDoc<GraphicsState>();
111   // </fields>
112

113   // <constructors>
114
public ContentBuilder(
115     ContentStream context,
116     IBuffer buffer
117     )
118   {
119     this.context = context;
120     this.buffer = buffer;
121   }
122   // </constructors>
123

124   // <interface>
125
// <public>
126
/**
127     Gets the inner buffer.
128   */

129   public IBuffer getBuffer(
130     )
131   {return buffer;}
132
133   /**
134     Gets the context of the content stream fragment builder.
135   */

136   public ContentStream getContext(
137     )
138   {return context;}
139
140   /**
141     Begins a new nested graphics state context [PDF:1.6:4.3.1].
142     <h3>Remarks</h3>
143     <p>This action saves the current graphics state.</p>
144   */

145   public void beginLocalState(
146     )
147   {
148     stateStack.push(state);
149     state = state.clone();
150
151     buffer.append("q\r");
152   }
153
154   /**
155     Begins a text block [PDF:1.6:5.3].
156   */

157   public void beginText(
158     )
159   {buffer.append("BT\r");}
160
161   /**
162     Draws a rectangle.
163   */

164   public void drawRectangle(
165     RectangularShape JavaDoc location
166     )
167   {
168     buffer.append(
169       PdfReal.toPdf(location.getX()) + " "
170         + PdfReal.toPdf(((IPdfNumber)context.getStreamContext().getContextBox().get(3)).getNumberValue() - location.getY() - location.getHeight()) + " "
171         + PdfReal.toPdf(location.getWidth()) + " "
172         + PdfReal.toPdf(location.getHeight()) + " "
173         + "re\r"
174       );
175   }
176
177   /**
178     Ends the current (innermostly-nested) graphics state context [PDF:1.6:4.3.1].
179     <h3>Remarks</h3>
180     <p>This action restores the previous (outer) graphics state.</p>
181   */

182   public void endLocalState(
183     )
184   {
185     buffer.append("Q\r");
186
187     state = stateStack.pop();
188   }
189
190   /**
191     Ends a text block [PDF:1.6:5.3].
192   */

193   public void endText(
194     )
195   {buffer.append("ET\r");}
196
197   /**
198     Fills the path using the current color [PDF:1.6:4.4.2].
199   */

200   public void fill(
201     )
202   {buffer.append("f\r");}
203
204   /**
205     Fills and then strokes the path using the current colors [PDF:1.6:4.4.2].
206   */

207   public void fillStroke(
208     )
209   {buffer.append("B\r");}
210
211   public GraphicsState getState(
212     )
213   {return state;}
214
215   /**
216     Applies a rotation to the coordinate system from user space to device space
217     [PDF:1.6:4.2.2].
218     @param angle Rotational counterclockwise angle.
219   */

220   public void rotate(
221     double angle
222     )
223   {
224     double rad = angle * Math.PI / 180;
225     double cos = Math.cos(rad);
226     double sin = Math.sin(rad);
227
228     setMatrix(cos, sin, -sin, cos, 0, 0);
229   }
230
231   /**
232     Applies a rotation to the coordinate system from text space to user space
233     [PDF:1.6:4.2.2].
234     @param angle Rotational counterclockwise angle.
235   */

236   public void rotateText(
237     double angle
238     )
239   {
240     double rad = angle * Math.PI / 180;
241     double cos = Math.cos(rad);
242     double sin = Math.sin(rad);
243
244     setTextMatrix(cos, sin, -sin, cos, 0, 0);
245   }
246
247   /**
248     Applies a scaling to the coordinate system from user space to device space
249     [PDF:1.6:4.2.2].
250     @param ratioX Horizontal scaling ratio.
251     @param ratioY Vertical scaling ratio.
252   */

253   public void scale(
254     double ratioX,
255     double ratioY
256     )
257   {setMatrix(ratioX, 0, 0, ratioY, 0, 0);}
258
259   /**
260     Applies a scaling to the coordinate system from text space to user space
261     [PDF:1.6:4.2.2].
262     @param ratioX Horizontal scaling ratio.
263     @param ratioY Vertical scaling ratio.
264   */

265   public void scaleText(
266     double ratioX,
267     double ratioY
268     )
269   {setTextMatrix(ratioX, 0, 0, ratioY, 0, 0);}
270
271   /**
272     Sets the character spacing parameter [PDF:1.6:5.2.1].
273   */

274   public void setCharacterSpacing(
275     double value
276     )
277   {buffer.append(PdfReal.toPdf(value) + " Tc\r");}
278
279   /**
280     Sets the nonstroking color value [PDF:1.6:4.5.7].
281   */

282   public void setFillColor(
283     Color value
284     )
285   {buffer.append(value.toPdf() + " " + value.getFillOperator() + "\r");}
286
287   /**
288     Sets the nonstroking color space [PDF:1.6:4.5.7].
289     @param name Resource identifier of the color space.
290   */

291   public void setFillColorSpace(
292     PdfName name
293     )
294   {//TODO:IMPL Get the associated colorspace from Resources dictionary!!!
295
//TODO:IMPL operator MUST be retrieve from colorspace.getFillOperator()
296
buffer.append(name.toPdf() + " cs\r");
297   }
298
299   /**
300     Sets the nonstroking color space [PDF:1.6:4.5.7].
301     <h3>Remarks</h3>
302     <p>The <code>value</code> is checked for presence in the current resource
303     dictionary: if it isn't available, it's automatically added. If you need to
304     avoid such a behavior, use {@link #setFillColorSpace(PdfName)
305     setFillColorSpace(PdfName)}.</p>
306   */

307   public void setFillColorSpace(
308     ColorSpace value
309     )
310   {
311     throw new NotImplementedException("colorSpace MUST be converted to its associated name; you need to implement a method in PdfDictionary that, given a PdfDirectObject, returns its associated key.");
312   }
313
314   /**
315     Sets flatness tolerance [PDF:1.6:6.5.1].
316   */

317   public void setFlatness(
318     double value
319     )
320   {
321     // <validation>
322
if(value < 0 || value > 100)
323       throw new IllegalArgumentException JavaDoc("'value' MUST be between 0 and 100.");
324     // </validation>
325

326     buffer.append(PdfReal.toPdf(value) + " i\r");
327   }
328
329   /**
330     Sets the font [PDF:1.6:5.2].
331     @param name Resource identifier of the font.
332     @param size Scaling factor.
333   */

334   public void setFont(
335     PdfName name,
336     double size
337     )
338   {
339     // Get the associated font from the context resources!
340
state.font = Font.wrap((PdfReference)((PdfDictionary)File.resolve(context.getStreamContext().getContextResources().get(PdfName.Font))).get(name));
341     if(state.font == null)
342     {throw new IllegalArgumentException JavaDoc("No font resource associated to the given argument (name:'name'; value:'" + name + "';)");}
343     state.fontSize = size;
344
345     buffer.append(name.toPdf() + " " + PdfReal.toPdf(size) + " Tf\r");
346   }
347
348   /**
349     Sets the font [PDF:1.6:5.2].
350     <h3>Remarks</h3>
351     <p>The <code>value</code> is checked for presence in the current resource
352     dictionary: if it isn't available, it's automatically added. If you need to
353     avoid such a behavior, use {@link #setFont(PdfName,double) setFont(PdfName,double)}.</p>
354     @param value Font.
355     @param size Scaling factor.
356   */

357   public void setFont(
358     Font value,
359     double size
360     )
361   {
362     throw new NotImplementedException("font MUST be converted to its associated name; you need to implement a method in PdfDictionary that, given a PdfDirectObject, returns its associated key.");
363     /*SetFont(
364       ((PdfDictionary)File.Resolve(streamContext.Resources[PdfName.Font])).GetKey(font),
365       size
366       );*/

367   }
368
369   /**
370     Sets the text horizontal scaling [PDF:1.6:5.2.3].
371   */

372   public void setHorizontalScaling(
373     double value
374     )
375   {buffer.append(PdfReal.toPdf(value) + " Tz\r");}
376
377   /**
378     Sets the text leading [PDF:1.6:5.2.4].
379   */

380   public void setLeading(
381     double value
382     )
383   {buffer.append(PdfReal.toPdf(value) + " TL\r");}
384
385   /**
386     Sets the line cap style [PDF:1.6:4.3.2].
387   */

388   public void setLineCap(
389     LineCapEnum value
390     )
391   {buffer.append(Integer.toString(value.getCode()) + " J\r");}
392
393   /**
394     Sets the line dash pattern [PDF:1.6:4.3.2].
395     @param phase Distance into the dash pattern at which to start the dash.
396   */

397   public void setLineDash(
398     int phase
399     )
400   {buffer.append("[] " + phase + " d\r");}
401
402   /**
403     Sets the line dash pattern [PDF:1.6:4.3.2].
404     @param phase Distance into the dash pattern at which to start the dash.
405     @param unitsOn Length of evenly alternating dashes and gaps.
406   */

407   public void setLineDash(
408     int phase,
409     int unitsOn
410     )
411   {buffer.append("[" + unitsOn + "] " + phase + " d\r");}
412
413   /**
414     Sets the line dash pattern [PDF:1.6:4.3.2].
415     @param phase Distance into the dash pattern at which to start the dash.
416     @param unitsOn Length of dashes.
417     @param unitsOff Length of gaps.
418   */

419   public void setLineDash(
420     int phase,
421     int unitsOn,
422     int unitsOff
423     )
424   {buffer.append("[" + unitsOn + " " + unitsOff + "] " + phase + " d\r");}
425
426   /**
427     Sets the line join style [PDF:1.6:4.3.2].
428   */

429   public void setLineJoin(
430     LineJoinEnum value
431     )
432   {buffer.append(Integer.toString(value.getCode()) + " j\r");}
433
434   /**
435     Sets the line width [PDF:1.6:4.3.2].
436   */

437   public void setLineWidth(
438     double value
439     )
440   {buffer.append(PdfReal.toPdf(value) + " w\r");}
441
442   /**
443     Applies a transformation to the coordinate system from user space
444     to device space [PDF:1.6:4.3.3].
445     @param a Item 1,1 of the matrix.
446     @param b Item 1,2 of the matrix.
447     @param c Item 2,1 of the matrix.
448     @param d Item 2,2 of the matrix.
449     @param e Item 3,1 of the matrix.
450     @param f Item 3,2 of the matrix.
451   */

452   public void setMatrix(
453     double a,
454     double b,
455     double c,
456     double d,
457     double e,
458     double f
459     )
460   {
461     buffer.append(
462       PdfReal.toPdf(a) + " "
463         + PdfReal.toPdf(b) + " "
464         + PdfReal.toPdf(c) + " "
465         + PdfReal.toPdf(d) + " "
466         + PdfReal.toPdf(e) + " "
467         + PdfReal.toPdf(f) + " "
468         + "cm\r"
469       );
470   }
471
472   /**
473     Sets the miter limit [PDF:1.6:4.3.2].
474   */

475   public void setMiterLimit(
476     double value
477     )
478   {buffer.append(PdfReal.toPdf(value) + " M\r");}
479
480   /**
481     Sets the stroking color value [PDF:1.6:4.5.7].
482   */

483   public void setStrokeColor(
484     Color value
485     )
486   {buffer.append(value.toPdf() + " " + value.getStrokeOperator() + "\r");}
487
488   /**
489     Sets the stroking color space [PDF:1.6:4.5.7].
490     @param name Resource identifier of the color space.
491   */

492   public void setStrokeColorSpace(
493     PdfName name
494     )
495   {//TODO:IMPL Get the associated colorspace from Resources dictionary!!!
496
//TODO:IMPL operator MUST be retrieve from colorspace.getStrokeOperator()
497
buffer.append(name.toPdf() + " CS\r");
498   }
499
500   /**
501     Sets the stroking color space [PDF:1.6:4.5.7].
502     <h3>Remarks</h3>
503     <p>The <code>value</code> is checked for presence in the current resource
504     dictionary: if it isn't available, it's automatically added. If you need to
505     avoid such a behavior, use {@link #setStrokeColorSpace(PdfName)
506     setStrokeColorSpace(PdfName)}.</p>
507   */

508   public void setStrokeColorSpace(
509     ColorSpace value
510     )
511   {
512     throw new NotImplementedException("colorSpace MUST be converted to its associated name; you need to implement a method in PdfDictionary that, given a PdfDirectObject, returns its associated key.");
513   }
514
515   /**
516     Applies a transformation to the coordinate system from text space
517     to user space [PDF:1.6:5.3.1].
518     @param a Item 1,1 of the matrix.
519     @param b Item 1,2 of the matrix.
520     @param c Item 2,1 of the matrix.
521     @param d Item 2,2 of the matrix.
522     @param e Item 3,1 of the matrix.
523     @param f Item 3,2 of the matrix.
524   */

525   public void setTextMatrix(
526     double a,
527     double b,
528     double c,
529     double d,
530     double e,
531     double f
532     )
533   {
534     buffer.append(
535       PdfReal.toPdf(a) + " "
536         + PdfReal.toPdf(b) + " "
537         + PdfReal.toPdf(c) + " "
538         + PdfReal.toPdf(d) + " "
539         + PdfReal.toPdf(e) + " "
540         + PdfReal.toPdf(f) + " "
541         + "Tm\r"
542       );
543   }
544
545   /**
546     Sets the text rendering mode [PDF:1.6:5.2.5].
547   */

548   public void setTextRenderingMode(
549     TextRenderingModeEnum value
550     )
551   {buffer.append(Integer.toString(value.getCode()) + " Tr\r");}
552
553   /**
554     Sets the text rise [PDF:1.6:5.2.6].
555   */

556   public void setTextRise(
557     double value
558     )
559   {buffer.append(value + " Ts\r");}
560
561   /**
562     Sets the word spacing [PDF:1.6:5.2.2].
563   */

564   public void setWordSpacing(
565     double value
566     )
567   {buffer.append(value + " Tw\r");}
568
569   /**
570     Shows the specified text on the page at the current location [PDF:1.6:5.3.2].
571     @param value Text to show.
572   */

573   public void showText(
574     String JavaDoc value
575     )
576   {buffer.append(PdfLiteral.toPdf(value) + " Tj\r");}
577
578   /**
579     Shows the specified text on the page at the specified location [PDF:1.6:5.3.2].
580     @param value Text to show.
581     @param location Position at which showing the text.
582   */

583   public void showText(
584     String JavaDoc value,
585     Point2D JavaDoc location
586     )
587   {
588     showText(
589       value,
590       location,
591       AlignmentXEnum.Left,
592       AlignmentYEnum.Top,
593       0
594       );
595   }
596
597   /**
598     Shows the specified text on the page at the specified location [PDF:1.6:5.3.2].
599     @param value Text to show.
600     @param location Anchor position at which showing the text.
601     @param alignmentX Horizontal alignment (relative to the location).
602     @param alignmentY Vertical alignment (relative to the location).
603     @param rotation Rotational counterclockwise angle.
604   */

605   public void showText(
606     String JavaDoc value,
607     Point2D JavaDoc location,
608     AlignmentXEnum alignmentX,
609     AlignmentYEnum alignmentY,
610     double rotation
611     )
612   {
613     double width = state.font.getKernedWidth(value,state.fontSize);
614     double x = location.getX();
615     double y = location.getY();
616     switch(alignmentX)
617     {
618       case Left:
619       case Justify:
620         // OK.
621
break;
622       case Right:
623         x -= width;
624         break;
625       case Center:
626         x -= width / 2;
627         break;
628     }
629     switch(alignmentY)
630     {
631       case Top:
632         // OK.
633
break;
634       case Bottom:
635         y -= state.font.getLineHeight(state.fontSize);
636         break;
637       case Middle:
638         y -= state.font.getLineHeight(state.fontSize) / 2;
639         break;
640     }
641
642     if(rotation == 0)
643     {
644       translateText(
645         x,
646         ((IPdfNumber)context.getStreamContext().getContextBox().get(3)).getNumberValue() - y - state.font.getAscent(state.fontSize)
647         );
648     }
649     else
650     {
651       double rad = rotation * Math.PI / 180.0;
652       double cos = Math.cos(rad);
653       double sin = Math.sin(rad);
654
655       setTextMatrix(
656         cos,
657         sin,
658         -sin,
659         cos,
660         x,
661         ((IPdfNumber)context.getStreamContext().getContextBox().get(3)).getNumberValue() - y - state.font.getAscent(state.fontSize)
662         );
663     }
664     buffer.append(PdfLiteral.toPdf(value) + " Tj\r");
665   }
666
667   /**
668     Shows the specified external object [PDF:1.6:4.7].
669     @param name Resource identifier of the external object.
670   */

671   public void showXObject(
672     PdfName name
673     )
674   {buffer.append(name.toPdf() + " Do\r");}
675
676   /**
677     Shows the specified external object at the specified position [PDF:1.6:4.7].
678     @param name Resource identifier of the external object.
679     @param location Position at which showing the external object.
680   */

681   public void showXObject(
682     PdfName name,
683     RectangularShape JavaDoc location
684     )
685   {
686     showXObject(
687       name,
688       location,
689       0
690       );
691   }
692
693   /**
694     Shows the specified external object at the specified position [PDF:1.6:4.7].
695     @param name Resource identifier of the external object.
696     @param location Position at which showing the external object.
697     @param rotation Rotational counterclockwise angle.
698   */

699   public void showXObject(
700     PdfName name,
701     RectangularShape JavaDoc location,
702     double rotation
703     )
704   {
705   // TODO:IMPL!!!
706
// name is the name of the XObject resource previously imported.
707
//TODO:IMPL rotation!!!
708
beginLocalState();
709     setMatrix(
710       location.getWidth(),
711       0,
712       0,
713       location.getHeight(),
714       location.getX(),
715       ((IPdfNumber)context.getStreamContext().getContextBox().get(3)).getNumberValue() - location.getY() - location.getHeight()
716       );
717     showXObject(name);
718     endLocalState();
719   }
720
721   /**
722     Strokes the path using the current color [PDF:1.6:4.4.2].
723   */

724   public void stroke(
725     )
726   {buffer.append("S\r");}
727
728   /**
729     Applies a translation to the coordinate system from user space
730     to device space [PDF:1.6:4.2.2].
731     @param distanceX Horizontal distance.
732     @param distanceY Vertical distance.
733   */

734   public void translate(
735     double distanceX,
736     double distanceY
737     )
738   {setMatrix(1, 0, 0, 1, distanceX, distanceY);}
739
740   /**
741     Applies a translation to the coordinate system from text space
742     to user space [PDF:1.6:4.2.2].
743     @param distanceX Horizontal distance.
744     @param distanceY Vertical distance.
745   */

746   public void translateText(
747     double distanceX,
748     double distanceY
749     )
750   {setTextMatrix(1, 0, 0, 1, distanceX, distanceY);}
751
752   /**
753     Applies a translation to the coordinate system from text space
754     to user space, relative to the start of the current line [PDF:1.6:5.3.1].
755     @param offsetX Horizontal offset.
756     @param offsetY Vertical offset.
757   */

758   public void translateTextRelative(
759     double offsetX,
760     double offsetY
761     )
762   {
763     buffer.append(
764       PdfReal.toPdf(offsetX) + " "
765         + PdfReal.toPdf(-offsetY) + " "
766         + "Td\r"
767       );
768   }
769
770   /**
771     Applies a translation to the coordinate system from text space
772     to user space, moving to the start of the next line [PDF:1.6:5.3.1].
773   */

774   public void translateTextToNextLine(
775     )
776   {buffer.append("T*\r");}
777   // </public>
778
// </interface>
779
// </dynamic>
780
// </class>
781
}
Popular Tags