001: /*
002: Copyright © 2007 Stefano Chizzolini. http://clown.stefanochizzolini.it
003:
004: Contributors:
005: * Stefano Chizzolini (original code developer, http://www.stefanochizzolini.it):
006: contributed code is Copyright © 2007 by Stefano Chizzolini.
007:
008: This file should be part of the source code distribution of "PDF Clown library"
009: (the Program): see the accompanying README files for more info.
010:
011: This Program is free software; you can redistribute it and/or modify it under
012: the terms of the GNU General Public License as published by the Free Software
013: Foundation; either version 2 of the License, or (at your option) any later version.
014:
015: This Program is distributed in the hope that it will be useful, but WITHOUT ANY
016: WARRANTY, either expressed or implied; without even the implied warranty of
017: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more details.
018:
019: You should have received a copy of the GNU General Public License along with this
020: Program (see README files); if not, go to the GNU website (http://www.gnu.org/).
021:
022: Redistribution and use, with or without modification, are permitted provided that such
023: redistributions retain the above copyright notice, license and disclaimer, along with
024: this list of conditions.
025: */
026:
027: package it.stefanochizzolini.clown.documents.contents.composition;
028:
029: import it.stefanochizzolini.clown.bytes.IBuffer;
030: import it.stefanochizzolini.clown.documents.Document;
031: import it.stefanochizzolini.clown.documents.contents.ContentScanner;
032: import it.stefanochizzolini.clown.documents.contents.Fonts;
033: import it.stefanochizzolini.clown.documents.contents.IContentContext;
034: import it.stefanochizzolini.clown.documents.contents.LineCapEnum;
035: import it.stefanochizzolini.clown.documents.contents.LineJoinEnum;
036: import it.stefanochizzolini.clown.documents.contents.Resources;
037: import it.stefanochizzolini.clown.documents.contents.TextRenderModeEnum;
038: import it.stefanochizzolini.clown.documents.contents.XObjects;
039: import it.stefanochizzolini.clown.documents.contents.colorSpaces.Color;
040: import it.stefanochizzolini.clown.documents.contents.colorSpaces.ColorSpace;
041: import it.stefanochizzolini.clown.documents.contents.colorSpaces.DeviceCMYKColorSpace;
042: import it.stefanochizzolini.clown.documents.contents.colorSpaces.DeviceGrayColorSpace;
043: import it.stefanochizzolini.clown.documents.contents.colorSpaces.DeviceRGBColorSpace;
044: import it.stefanochizzolini.clown.documents.contents.fonts.Font;
045: import it.stefanochizzolini.clown.documents.contents.objects.CompositeObject;
046: import it.stefanochizzolini.clown.documents.contents.objects.ContentObject;
047: import it.stefanochizzolini.clown.documents.contents.objects.DrawRectangle;
048: import it.stefanochizzolini.clown.documents.contents.objects.Fill;
049: import it.stefanochizzolini.clown.documents.contents.objects.FillStroke;
050: import it.stefanochizzolini.clown.documents.contents.objects.LocalGraphicsState;
051: import it.stefanochizzolini.clown.documents.contents.objects.ModifyCTM;
052: import it.stefanochizzolini.clown.documents.contents.objects.PaintXObject;
053: import it.stefanochizzolini.clown.documents.contents.objects.SetCharSpace;
054: import it.stefanochizzolini.clown.documents.contents.objects.SetFillColor;
055: import it.stefanochizzolini.clown.documents.contents.objects.SetFillColorSpace;
056: import it.stefanochizzolini.clown.documents.contents.objects.SetFont;
057: import it.stefanochizzolini.clown.documents.contents.objects.SetLineCap;
058: import it.stefanochizzolini.clown.documents.contents.objects.SetLineDash;
059: import it.stefanochizzolini.clown.documents.contents.objects.SetLineJoin;
060: import it.stefanochizzolini.clown.documents.contents.objects.SetLineWidth;
061: import it.stefanochizzolini.clown.documents.contents.objects.SetMiterLimit;
062: import it.stefanochizzolini.clown.documents.contents.objects.SetStrokeColor;
063: import it.stefanochizzolini.clown.documents.contents.objects.SetStrokeColorSpace;
064: import it.stefanochizzolini.clown.documents.contents.objects.SetTextLead;
065: import it.stefanochizzolini.clown.documents.contents.objects.SetTextMatrix;
066: import it.stefanochizzolini.clown.documents.contents.objects.SetTextRenderMode;
067: import it.stefanochizzolini.clown.documents.contents.objects.SetTextRise;
068: import it.stefanochizzolini.clown.documents.contents.objects.SetTextScale;
069: import it.stefanochizzolini.clown.documents.contents.objects.SetWordSpace;
070: import it.stefanochizzolini.clown.documents.contents.objects.ShowText;
071: import it.stefanochizzolini.clown.documents.contents.objects.Stroke;
072: import it.stefanochizzolini.clown.documents.contents.objects.Text;
073: import it.stefanochizzolini.clown.documents.contents.objects.TranslateTextRelative;
074: import it.stefanochizzolini.clown.documents.contents.objects.TranslateTextToNextLine;
075: import it.stefanochizzolini.clown.documents.contents.xObjects.XObject;
076: import it.stefanochizzolini.clown.files.File;
077: import it.stefanochizzolini.clown.objects.IPdfNumber;
078: import it.stefanochizzolini.clown.objects.PdfDictionary;
079: import it.stefanochizzolini.clown.objects.PdfLiteral;
080: import it.stefanochizzolini.clown.objects.PdfName;
081: import it.stefanochizzolini.clown.objects.PdfReal;
082: import it.stefanochizzolini.clown.objects.PdfReference;
083: import it.stefanochizzolini.clown.util.NotImplementedException;
084:
085: import java.awt.Dimension;
086: import java.awt.geom.Dimension2D;
087: import java.awt.geom.Point2D;
088: import java.awt.geom.RectangularShape;
089: import java.util.Stack;
090:
091: /**
092: Content stream primitive filter. It provides the basic (primitive) operations described by the PDF
093: specification for graphics content composition.
094: <h3>Remarks</h3>
095: <p>This class supersedes lower-level ContentBuilder, leveraging the all-new object-oriented content
096: stream modelling infrastructure, which encompasses 1st-level content stream objects (operations),
097: 2nd-level content stream objects (graphics objects) and full graphics state support.</p>
098:
099: @author Stefano Chizzolini
100: @version 0.0.5
101: @since 0.0.4
102: */
103: public class PrimitiveFilter {
104: // <class>
105: // <dynamic>
106: // <fields>
107: private ContentScanner scanner;
108:
109: private ContentScanner currentLevel;
110:
111: // </fields>
112:
113: // <constructors>
114: public PrimitiveFilter(ContentScanner scanner) {
115: this .scanner = scanner;
116:
117: this .currentLevel = scanner.getLeafLevel();
118: }
119:
120: public PrimitiveFilter(IContentContext context) {
121: this (new ContentScanner(context.getContents()));
122: }
123:
124: // </constructors>
125:
126: // <interface>
127: // <public>
128: /**
129: Adds a content object.
130: @return The added content object.
131: @version 0.0.5
132: */
133: public ContentObject add(ContentObject object) {
134: currentLevel.insert(object);
135: currentLevel.moveNext();
136:
137: return object;
138: }
139:
140: /**
141: Applies a transformation to the coordinate system from user space to device space [PDF:1.6:4.3.3].
142: <h3>Remarks</h3>
143: <p>The transformation is applied to the current transformation matrix (CTM) by concatenation,
144: i.e. it doesn't replace it.</p>
145: @param a Item 0,0 of the matrix.
146: @param b Item 0,1 of the matrix.
147: @param c Item 1,0 of the matrix.
148: @param d Item 1,1 of the matrix.
149: @param e Item 2,0 of the matrix.
150: @param f Item 2,1 of the matrix.
151: @see #setMatrix(double,double,double,double,double,double)
152: */
153: public void applyMatrix(double a, double b, double c, double d,
154: double e, double f) {
155: add(new ModifyCTM(a, b, c, d, e, f));
156: }
157:
158: /**
159: Adds a composite object beginning it.
160: @return The added composite object.
161: @see #end()
162: */
163: public CompositeObject begin(CompositeObject object) {
164: // Insert the new object at the current level!
165: currentLevel.insert(object);
166: // The new object's children level is the new current level!
167: currentLevel = currentLevel.getChildLevel();
168:
169: return object;
170: }
171:
172: /**
173: Begins a new nested graphics state context [PDF:1.6:4.3.1].
174: @return The added local graphics state object.
175: @see #end()
176: */
177: public LocalGraphicsState beginLocalState() {
178: return (LocalGraphicsState) begin(new LocalGraphicsState());
179: }
180:
181: /**
182: Draws a rectangle [PDF:1.6:4.4.1].
183: @see #fill()
184: @see #fillStroke()
185: @see #stroke()
186: */
187: public void drawRectangle(RectangularShape location) {
188: add(new DrawRectangle(location.getX(), ((IPdfNumber) scanner
189: .getContentContext().getBox().get(3)).getNumberValue()
190: - location.getY() - location.getHeight(), location
191: .getWidth(), location.getHeight()));
192: }
193:
194: /**
195: Ends the current (innermostly-nested) composite object.
196: @see #begin(CompositeObject)
197: */
198: public void end() {
199: currentLevel = currentLevel.getParentLevel();
200: currentLevel.moveNext();
201: }
202:
203: /**
204: Fills the path using the current color [PDF:1.6:4.4.2].
205: @see #setFillColor(Color)
206: */
207: public void fill() {
208: add(Fill.Value);
209: }
210:
211: /**
212: Fills and then strokes the path using the current colors [PDF:1.6:4.4.2].
213: @see #setFillColor(Color)
214: @see #setStrokeColor(Color)
215: */
216: public void fillStroke() {
217: add(FillStroke.Value);
218: }
219:
220: /**
221: Serializes the contents into the content stream.
222: */
223: public void flush() {
224: scanner.getContents().flush();
225: }
226:
227: /**
228: Gets the content stream scanner.
229: */
230: public ContentScanner getScanner() {
231: return scanner;
232: }
233:
234: /**
235: Gets the current graphics state [PDF:1.6:4.3].
236: */
237: public ContentScanner.GraphicsState getState() {
238: return currentLevel.getState();
239: }
240:
241: /**
242: Applies a rotation to the coordinate system from user space to device space [PDF:1.6:4.2.2].
243: @param angle Rotational counterclockwise angle.
244: @see #applyMatrix(double,double,double,double,double,double)
245: */
246: public void rotate(double angle) {
247: double rad = angle * Math.PI / 180;
248: double cos = Math.cos(rad);
249: double sin = Math.sin(rad);
250:
251: applyMatrix(cos, sin, -sin, cos, 0, 0);
252: }
253:
254: /**
255: Applies a scaling to the coordinate system from user space to device space
256: [PDF:1.6:4.2.2].
257: @param ratioX Horizontal scaling ratio.
258: @param ratioY Vertical scaling ratio.
259: @see #applyMatrix(double,double,double,double,double,double)
260: */
261: public void scale(double ratioX, double ratioY) {
262: applyMatrix(ratioX, 0, 0, ratioY, 0, 0);
263: }
264:
265: /**
266: Sets the character spacing parameter [PDF:1.6:5.2.1].
267: */
268: public void setCharSpace(double value) {
269: add(new SetCharSpace(value));
270: }
271:
272: /**
273: Sets the nonstroking color value [PDF:1.6:4.5.7].
274: @see #setStrokeColor(Color)
275: */
276: public void setFillColor(Color value) {
277: if (currentLevel.getState().getFillColorSpace() != value
278: .getColorSpace()) {
279: // Set filling color space!
280: add(new SetFillColorSpace(getColorSpaceName(value
281: .getColorSpace())));
282: }
283:
284: add(new SetFillColor(value));
285: }
286:
287: /**
288: Sets the font [PDF:1.6:5.2].
289: @param name Resource identifier of the font.
290: @param size Scaling factor (points).
291: */
292: public void setFont(PdfName name, double size) {
293: // Doesn't the font exist in the context resources?
294: if (!scanner.getContentContext().getResources().getFonts()
295: .containsKey(name))
296: throw new IllegalArgumentException(
297: "No font resource associated to the given argument (name:'name'; value:'"
298: + name + "';)");
299:
300: add(new SetFont(name, size));
301: }
302:
303: /**
304: Sets the font [PDF:1.6:5.2].
305: <h3>Remarks</h3>
306: <p>The <code>value</code> is checked for presence in the current resource
307: dictionary: if it isn't available, it's automatically added. If you need to
308: avoid such a behavior, use {@link #setFont(PdfName,double) setFont(PdfName,double)}.</p>
309: @param value Font.
310: @param size Scaling factor (points).
311: @version 0.0.5
312: */
313: public void setFont(Font value, double size) {
314: setFont(getFontName(value), size);
315: }
316:
317: /**
318: Sets the text horizontal scaling [PDF:1.6:5.2.3].
319: */
320: public void setTextScale(double value) {
321: add(new SetTextScale(value));
322: }
323:
324: /**
325: Sets the text leading [PDF:1.6:5.2.4].
326: */
327: public void setTextLead(double value) {
328: add(new SetTextLead(value));
329: }
330:
331: /**
332: Sets the line cap style [PDF:1.6:4.3.2].
333: */
334: public void setLineCap(LineCapEnum value) {
335: add(new SetLineCap(value));
336: }
337:
338: /**
339: Sets the line dash pattern [PDF:1.6:4.3.2].
340: @param phase Distance into the dash pattern at which to start the dash.
341: @param unitsOn Length of evenly alternating dashes and gaps.
342: */
343: public void setLineDash(int phase, int unitsOn) {
344: setLineDash(phase, unitsOn, unitsOn);
345: }
346:
347: /**
348: Sets the line dash pattern [PDF:1.6:4.3.2].
349: @param phase Distance into the dash pattern at which to start the dash.
350: @param unitsOn Length of dashes.
351: @param unitsOff Length of gaps.
352: */
353: public void setLineDash(int phase, int unitsOn, int unitsOff) {
354: add(new SetLineDash(phase, unitsOn, unitsOff));
355: }
356:
357: /**
358: Sets the line join style [PDF:1.6:4.3.2].
359: */
360: public void setLineJoin(LineJoinEnum value) {
361: add(new SetLineJoin(value));
362: }
363:
364: /**
365: Sets the line width [PDF:1.6:4.3.2].
366: */
367: public void setLineWidth(double value) {
368: add(new SetLineWidth(value));
369: }
370:
371: /**
372: Sets the transformation of the coordinate system from user space to device space [PDF:1.6:4.3.3].
373: <h3>Remarks</h3>
374: <p>The transformation replaces the current transformation matrix (CTM).</p>
375: @param a Item 0,0 of the matrix.
376: @param b Item 0,1 of the matrix.
377: @param c Item 1,0 of the matrix.
378: @param d Item 1,1 of the matrix.
379: @param e Item 2,0 of the matrix.
380: @param f Item 2,1 of the matrix.
381: @see #applyMatrix(double,double,double,double,double,double)
382: */
383: public void setMatrix(double a, double b, double c, double d,
384: double e, double f) {
385: // Reset the CTM!
386: add(ModifyCTM.getResetCTM(scanner.getState().getCTM()));
387: // Apply the transformation!
388: add(new ModifyCTM(a, b, c, d, e, f));
389: }
390:
391: /**
392: Sets the miter limit [PDF:1.6:4.3.2].
393: */
394: public void setMiterLimit(double value) {
395: add(new SetMiterLimit(value));
396: }
397:
398: /**
399: Sets the stroking color value [PDF:1.6:4.5.7].
400: @see #setFillColor(Color)
401: */
402: public void setStrokeColor(Color value) {
403: if (currentLevel.getState().getStrokeColorSpace() != value
404: .getColorSpace()) {
405: // Set stroking color space!
406: add(new SetStrokeColorSpace(getColorSpaceName(value
407: .getColorSpace())));
408: }
409:
410: add(new SetStrokeColor(value));
411: }
412:
413: /**
414: Sets the text rendering mode [PDF:1.6:5.2.5].
415: */
416: public void setTextRenderMode(TextRenderModeEnum value) {
417: add(new SetTextRenderMode(value));
418: }
419:
420: /**
421: Sets the text rise [PDF:1.6:5.2.6].
422: */
423: public void setTextRise(double value) {
424: add(new SetTextRise(value));
425: }
426:
427: /**
428: Sets the word spacing [PDF:1.6:5.2.2].
429: */
430: public void setWordSpace(double value) {
431: add(new SetWordSpace(value));
432: }
433:
434: /**
435: Shows the specified text on the page at the current location [PDF:1.6:5.3.2].
436: @param value Text to show.
437: */
438: public void showText(String value) {
439: beginText();
440: add(new ShowText(value));
441: end();
442: }
443:
444: /**
445: Shows the specified text on the page at the specified location [PDF:1.6:5.3.2].
446: @param value Text to show.
447: @param location Position at which showing the text.
448: */
449: public void showText(String value, Point2D location) {
450: showText(value, location, AlignmentXEnum.Left,
451: AlignmentYEnum.Top, 0);
452: }
453:
454: /**
455: Shows the specified text on the page at the specified location [PDF:1.6:5.3.2].
456: @param value Text to show.
457: @param location Anchor position at which showing the text.
458: @param alignmentX Horizontal alignment.
459: @param alignmentY Vertical alignment.
460: @param rotation Rotational counterclockwise angle.
461: */
462: public void showText(String value, Point2D location,
463: AlignmentXEnum alignmentX, AlignmentYEnum alignmentY,
464: double rotation) {
465: ContentScanner.GraphicsState.TextState state = currentLevel
466: .getState().getText();
467: Font font = state.getFont();
468: double fontSize = state.getFontSize();
469: double x = location.getX();
470: double y = location.getY();
471:
472: if (alignmentX == AlignmentXEnum.Left
473: && alignmentY == AlignmentYEnum.Top) {
474: beginText();
475:
476: if (rotation == 0) {
477: translateText(x, ((IPdfNumber) scanner
478: .getContentContext().getBox().get(3))
479: .getNumberValue()
480: - y - font.getAscent(fontSize));
481: } else {
482: double rad = rotation * Math.PI / 180.0;
483: double cos = Math.cos(rad);
484: double sin = Math.sin(rad);
485:
486: setTextMatrix(cos, sin, -sin, cos, x,
487: ((IPdfNumber) scanner.getContentContext()
488: .getBox().get(3)).getNumberValue()
489: - y - font.getAscent(fontSize));
490: }
491:
492: add(new ShowText(value));
493: end();
494: } else {
495: beginLocalState();
496:
497: // Coordinates transformation.
498: double cos, sin;
499: if (rotation == 0) {
500: cos = 1;
501: sin = 0;
502: } else {
503: double rad = rotation * Math.PI / 180.0;
504: cos = Math.cos(rad);
505: sin = Math.sin(rad);
506: }
507: // Apply the transformation!
508: applyMatrix(cos, sin, -sin, cos, x, ((IPdfNumber) scanner
509: .getContentContext().getBox().get(3))
510: .getNumberValue()
511: - y);
512:
513: // Begin the text object!
514: beginText();
515:
516: // Text coordinates adjustment.
517: double width = font.getKernedWidth(value, fontSize);
518: switch (alignmentX) {
519: case Left:
520: x = 0;
521: break;
522: case Right:
523: x = -width;
524: break;
525: case Center:
526: case Justify:
527: x = -width / 2;
528: break;
529: }
530: switch (alignmentY) {
531: case Bottom:
532: y = font.getLineHeight(fontSize)
533: - font.getAscent(fontSize);
534: break;
535: case Middle:
536: y = font.getLineHeight(fontSize) / 2
537: - font.getAscent(fontSize);
538: break;
539: }
540: // Apply the text coordinates adjustment!
541: translateText(x, y);
542:
543: // Add the text!
544: add(new ShowText(value));
545: // Close the text object!
546: end();
547: // Close the local state!
548: end();
549: }
550: }
551:
552: /**
553: Shows the specified external object [PDF:1.6:4.7].
554: @param name Resource identifier of the external object.
555: */
556: public void showXObject(PdfName name) {
557: add(new PaintXObject(name));
558: }
559:
560: /**
561: Shows the specified external object [PDF:1.6:4.7].
562: <h3>Remarks</h3>
563: <p>The <code>value</code> is checked for presence in the current resource
564: dictionary: if it isn't available, it's automatically added. If you need to
565: avoid such a behavior, use {@link #showXObject(PdfName) #showXObject(PdfName)}.</p>
566: @param value External object.
567: @version 0.0.5
568: @since 0.0.5
569: */
570: public void showXObject(XObject value) {
571: showXObject(getXObjectName(value));
572: }
573:
574: /**
575: Shows the specified external object at the specified position [PDF:1.6:4.7].
576: @param name Resource identifier of the external object.
577: @param location Position at which showing the external object.
578: @version 0.0.5
579: */
580: public void showXObject(PdfName name, Point2D location) {
581: showXObject(name, location, new Dimension(0, 0));
582: }
583:
584: /**
585: Shows the specified external object at the specified position [PDF:1.6:4.7].
586: <h3>Remarks</h3>
587: <p>The <code>value</code> is checked for presence in the current resource
588: dictionary: if it isn't available, it's automatically added. If you need to
589: avoid such a behavior, use {@link #showXObject(PdfName,Point2D) #showXObject(PdfName,Point2D)}.</p>
590: @param value External object.
591: @param location Position at which showing the external object.
592: @version 0.0.5
593: @since 0.0.5
594: */
595: public void showXObject(XObject value, Point2D location) {
596: showXObject(getXObjectName(value), location);
597: }
598:
599: /**
600: Shows the specified external object at the specified position [PDF:1.6:4.7].
601: @param name Resource identifier of the external object.
602: @param location Position at which showing the external object.
603: @param size Size of the external object.
604: @version 0.0.5
605: @since 0.0.5
606: */
607: public void showXObject(PdfName name, Point2D location,
608: Dimension2D size) {
609: showXObject(name, location, size, AlignmentXEnum.Left,
610: AlignmentYEnum.Top, 0);
611: }
612:
613: /**
614: Shows the specified external object at the specified position [PDF:1.6:4.7].
615: <h3>Remarks</h3>
616: <p>The <code>value</code> is checked for presence in the current resource
617: dictionary: if it isn't available, it's automatically added. If you need to
618: avoid such a behavior, use {@link #showXObject(PdfName,Point2D,Dimension2D) #showXObject(PdfName,Point2D,Dimension2D)}.</p>
619: @param value External object.
620: @param location Position at which showing the external object.
621: @param size Size of the external object.
622: @version 0.0.5
623: @since 0.0.5
624: */
625: public void showXObject(XObject value, Point2D location,
626: Dimension2D size) {
627: showXObject(getXObjectName(value), location, size);
628: }
629:
630: /**
631: Shows the specified external object at the specified position [PDF:1.6:4.7].
632: @param name Resource identifier of the external object.
633: @param location Position at which showing the external object.
634: @param size Size of the external object.
635: @param alignmentX Horizontal alignment.
636: @param alignmentY Vertical alignment.
637: @param rotation Rotational counterclockwise angle.
638: @version 0.0.5
639: */
640: public void showXObject(PdfName name, Point2D location,
641: Dimension2D size, AlignmentXEnum alignmentX,
642: AlignmentYEnum alignmentY, double rotation) {
643: XObject xObject = scanner.getContentContext().getResources()
644: .getXObjects().get(name);
645:
646: // Adjusting default dimensions...
647: /*
648: NOTE: Zero-valued dimensions represent default proportional dimensions.
649: */
650: Dimension2D xObjectSize = xObject.getSize();
651: if (size.getWidth() == 0) {
652: if (size.getHeight() == 0) {
653: size.setSize(xObjectSize);
654: } else {
655: size.setSize(size.getHeight() * xObjectSize.getWidth()
656: / xObjectSize.getHeight(), size.getHeight());
657: }
658: } else if (size.getHeight() == 0) {
659: size.setSize(size.getWidth(), size.getWidth()
660: * xObjectSize.getHeight() / xObjectSize.getWidth());
661: }
662:
663: // Scaling.
664: double[] matrix = xObject.getMatrix();
665: double scaleX, scaleY;
666: scaleX = size.getWidth() / (xObjectSize.getWidth() * matrix[0]);
667: scaleY = size.getHeight()
668: / (xObjectSize.getHeight() * matrix[3]);
669:
670: // Alignment.
671: double locationOffsetX, locationOffsetY;
672: switch (alignmentX) {
673: case Left:
674: locationOffsetX = 0;
675: break;
676: case Right:
677: locationOffsetX = size.getWidth();
678: break;
679: case Center:
680: case Justify:
681: default:
682: locationOffsetX = size.getWidth() / 2;
683: break;
684: }
685: switch (alignmentY) {
686: case Top:
687: locationOffsetY = size.getHeight();
688: break;
689: case Bottom:
690: locationOffsetY = 0;
691: break;
692: case Middle:
693: default:
694: locationOffsetY = size.getHeight() / 2;
695: break;
696: }
697:
698: beginLocalState();
699: translate(location.getX(), ((IPdfNumber) scanner
700: .getContentContext().getBox().get(3)).getNumberValue()
701: - location.getY());
702: if (rotation != 0) {
703: rotate(rotation);
704: }
705: applyMatrix(scaleX, 0, 0, scaleY, -locationOffsetX,
706: -locationOffsetY);
707: showXObject(name);
708: end();
709: }
710:
711: /**
712: Shows the specified external object at the specified position [PDF:1.6:4.7].
713: <h3>Remarks</h3>
714: <p>The <code>value</code> is checked for presence in the current resource
715: dictionary: if it isn't available, it's automatically added. If you need to
716: avoid such a behavior, use {@link #showXObject(PdfName,Point2D,Dimension2D,AlignmentXEnum,AlignmentYEnum,double) #showXObject(PdfName,Point2D,Dimension2D,AlignmentXEnum,AlignmentYEnum,double)}.</p>
717: @param value External object.
718: @param location Position at which showing the external object.
719: @param size Size of the external object.
720: @param alignmentX Horizontal alignment.
721: @param alignmentY Vertical alignment.
722: @param rotation Rotational counterclockwise angle.
723: @version 0.0.5
724: @since 0.0.5
725: */
726: public void showXObject(XObject value, Point2D location,
727: Dimension2D size, AlignmentXEnum alignmentX,
728: AlignmentYEnum alignmentY, double rotation) {
729: showXObject(getXObjectName(value), location, size, alignmentX,
730: alignmentY, rotation);
731: }
732:
733: /**
734: Strokes the path using the current color [PDF:1.6:4.4.2].
735: @see #setStrokeColor(Color)
736: */
737: public void stroke() {
738: add(Stroke.Value);
739: }
740:
741: /**
742: Applies a translation to the coordinate system from user space
743: to device space [PDF:1.6:4.2.2].
744: @param distanceX Horizontal distance.
745: @param distanceY Vertical distance.
746: @see #applyMatrix(double,double,double,double,double,double)
747: */
748: public void translate(double distanceX, double distanceY) {
749: applyMatrix(1, 0, 0, 1, distanceX, distanceY);
750: }
751:
752: // </public>
753:
754: // <private>
755: /**
756: Begins a text object [PDF:1.6:5.3].
757: @see #end()
758: */
759: private Text beginText() {
760: return (Text) begin(new Text());
761: }
762:
763: private PdfName getFontName(Font value) {
764: // Ensuring that the font exists within the context resources...
765: Resources resources = scanner.getContentContext()
766: .getResources();
767: Fonts fonts = resources.getFonts();
768: // No font resources collection?
769: if (fonts == null) {
770: // Create the font resources collection!
771: fonts = new Fonts(scanner.getContents().getDocument());
772: resources.setFonts(fonts);
773: resources.update();
774: }
775: // Get the key associated to the font!
776: PdfName name = fonts.getBaseDataObject().getKey(
777: value.getBaseObject());
778: // No key found?
779: if (name == null) {
780: // Insert the font within the resources!
781: int fontIndex = fonts.size();
782: do {
783: name = new PdfName(String.valueOf(++fontIndex));
784: } while (fonts.containsKey(name));
785: fonts.put(name, value);
786: fonts.update();
787: }
788:
789: return name;
790: }
791:
792: private PdfName getXObjectName(XObject value) {
793: // Ensuring that the external object exists within the context resources...
794: Resources resources = scanner.getContentContext()
795: .getResources();
796: XObjects xObjects = resources.getXObjects();
797: // No external object resources collection?
798: if (xObjects == null) {
799: // Create the external object resources collection!
800: xObjects = new XObjects(scanner.getContents().getDocument());
801: resources.setXObjects(xObjects);
802: resources.update();
803: }
804: // Get the key associated to the external object!
805: PdfName name = xObjects.getBaseDataObject().getKey(
806: value.getBaseObject());
807: // No key found?
808: if (name == null) {
809: // Insert the external object within the resources!
810: int xObjectIndex = xObjects.size();
811: do {
812: name = new PdfName(String.valueOf(++xObjectIndex));
813: } while (xObjects.containsKey(name));
814: xObjects.put(name, value);
815: xObjects.update();
816: }
817:
818: return name;
819: }
820:
821: private PdfName getColorSpaceName(ColorSpace value) {
822: if (value instanceof DeviceGrayColorSpace) {
823: return PdfName.DeviceGray;
824: } else if (value instanceof DeviceRGBColorSpace) {
825: return PdfName.DeviceRGB;
826: } else if (value instanceof DeviceCMYKColorSpace) {
827: return PdfName.DeviceCMYK;
828: } else
829: throw new NotImplementedException(
830: "colorSpace MUST be converted to its associated name; you need to implement a method in PdfDictionary that, given a PdfDirectObject, returns its associated key.");
831: }
832:
833: /**
834: Applies a rotation to the coordinate system from text space to user space [PDF:1.6:4.2.2].
835: @param angle Rotational counterclockwise angle.
836: */
837: private void rotateText(double angle) {
838: double rad = angle * Math.PI / 180;
839: double cos = Math.cos(rad);
840: double sin = Math.sin(rad);
841:
842: setTextMatrix(cos, sin, -sin, cos, 0, 0);
843: }
844:
845: /**
846: Applies a scaling to the coordinate system from text space to user space
847: [PDF:1.6:4.2.2].
848: @param ratioX Horizontal scaling ratio.
849: @param ratioY Vertical scaling ratio.
850: */
851: private void scaleText(double ratioX, double ratioY) {
852: setTextMatrix(ratioX, 0, 0, ratioY, 0, 0);
853: }
854:
855: /**
856: Sets the transformation of the coordinate system from text space to user space [PDF:1.6:5.3.1].
857: <h3>Remarks</h3>
858: <p>The transformation replaces the current text matrix.</p>
859: @param a Item 0,0 of the matrix.
860: @param b Item 0,1 of the matrix.
861: @param c Item 1,0 of the matrix.
862: @param d Item 1,1 of the matrix.
863: @param e Item 2,0 of the matrix.
864: @param f Item 2,1 of the matrix.
865: */
866: private void setTextMatrix(double a, double b, double c, double d,
867: double e, double f) {
868: add(new SetTextMatrix(a, b, c, d, e, f));
869: }
870:
871: /**
872: Applies a translation to the coordinate system from text space
873: to user space [PDF:1.6:4.2.2].
874: @param distanceX Horizontal distance.
875: @param distanceY Vertical distance.
876: */
877: private void translateText(double distanceX, double distanceY) {
878: setTextMatrix(1, 0, 0, 1, distanceX, distanceY);
879: }
880:
881: /**
882: Applies a translation to the coordinate system from text space to user space,
883: relative to the start of the current line [PDF:1.6:5.3.1].
884: @param offsetX Horizontal offset.
885: @param offsetY Vertical offset.
886: */
887: private void translateTextRelative(double offsetX, double offsetY) {
888: add(new TranslateTextRelative(offsetX, -offsetY));
889: }
890:
891: /**
892: Applies a translation to the coordinate system from text space to user space,
893: moving to the start of the next line [PDF:1.6:5.3.1].
894: */
895: private void translateTextToNextLine() {
896: add(TranslateTextToNextLine.Value);
897: }
898: // </private>
899: // </interface>
900: // </dynamic>
901: // </class>
902: }
|