KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > render > pdf > PDFRenderer


1 /*
2  * $Id: PDFRenderer.java,v 1.91.2.15 2003/03/05 18:58:16 pietsch Exp $
3  * ============================================================================
4  * The Apache Software License, Version 1.1
5  * ============================================================================
6  *
7  * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without modifica-
10  * tion, are permitted provided that the following conditions are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  *
19  * 3. The end-user documentation included with the redistribution, if any, must
20  * include the following acknowledgment: "This product includes software
21  * developed by the Apache Software Foundation (http://www.apache.org/)."
22  * Alternately, this acknowledgment may appear in the software itself, if
23  * and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. The names "FOP" and "Apache Software Foundation" must not be used to
26  * endorse or promote products derived from this software without prior
27  * written permission. For written permission, please contact
28  * apache@apache.org.
29  *
30  * 5. Products derived from this software may not be called "Apache", nor may
31  * "Apache" appear in their name, without prior written permission of the
32  * Apache Software Foundation.
33  *
34  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
35  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
36  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
37  * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
38  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
39  * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
40  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
41  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
42  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
43  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44  * ============================================================================
45  *
46  * This software consists of voluntary contributions made by many individuals
47  * on behalf of the Apache Software Foundation and was originally created by
48  * James Tauber <jtauber@jtauber.com>. For more information on the Apache
49  * Software Foundation, please see <http://www.apache.org/>.
50  */

51 package org.apache.fop.render.pdf;
52
53 // FOP
54
import org.apache.fop.render.PrintRenderer;
55 import org.apache.fop.image.FopImage;
56 import org.apache.fop.image.FopImageException;
57 import org.apache.fop.apps.FOPException;
58 import org.apache.fop.fo.properties.*;
59 import org.apache.fop.layout.inline.*;
60 import org.apache.fop.svg.*;
61 import org.apache.fop.pdf.*;
62 import org.apache.fop.layout.*;
63 import org.apache.fop.image.*;
64 import org.apache.fop.extensions.*;
65 import org.apache.fop.render.pdf.fonts.LazyFont;
66
67 import org.apache.batik.bridge.*;
68 import org.apache.batik.gvt.*;
69 import org.apache.batik.gvt.renderer.*;
70
71 import org.w3c.dom.*;
72 import org.w3c.dom.svg.*;
73
74 // Java
75
import java.io.IOException JavaDoc;
76 import java.io.OutputStream JavaDoc;
77 import java.util.List JavaDoc;
78 import java.util.Map JavaDoc;
79 import java.awt.geom.AffineTransform JavaDoc;
80
81 /**
82  * <p>
83  *
84  * Renderer that renders areas to PDF.</p> <p>
85  *
86  * Modified by Mark Lillywhite, mark-fop@inomial.com to use the new Renderer
87  * interface. The PDF renderer is by far the trickiest renderer and the best
88  * supported by FOP. It also required some reworking in the way that Pages,
89  * Catalogs and the Root object were written to the stream. The output document
90  * should now still be a 100% compatible PDF document, but the order of the
91  * document writing is significantly different. See also the changes to
92  * PDFPage, PDFPages and PDFRoot.</p>
93  */

94 public class PDFRenderer extends PrintRenderer {
95
96     /**
97      * the PDF Document being created
98      */

99     protected PDFDocument pdfDoc;
100
101     /**
102      * the /Resources object of the PDF document being created
103      */

104     protected PDFResources pdfResources;
105
106     /**
107      * the current stream to add PDF commands to
108      */

109     PDFStream currentStream;
110
111     /**
112      * the current annotation list to add annotations to
113      */

114     PDFAnnotList currentAnnotList;
115
116     /**
117      * the current page to add annotations to
118      */

119     PDFPage currentPage;
120
121     PDFColor currentColor;
122
123     float currentLetterSpacing = Float.MAX_VALUE;
124
125     /**
126      * true if a TJ command is left to be written
127      */

128     boolean textOpen = false;
129
130     /**
131      * the previous Y coordinate of the last word written. Used to decide if we
132      * can draw the next word on the same line.
133      */

134     int prevWordY = 0;
135
136     /**
137      * the previous X coordinate of the last word written. used to calculate
138      * how much space between two words
139      */

140     int prevWordX = 0;
141
142     /**
143      * The width of the previous word. Used to calculate space between
144      */

145     int prevWordWidth = 0;
146
147     /**
148      * reusable word area string buffer to reduce memory usage
149      */

150     private StringBuffer JavaDoc _wordAreaPDF = new StringBuffer JavaDoc();
151
152     /**
153      * options
154      */

155     protected java.util.Map JavaDoc options;
156
157     protected List JavaDoc extensions = null;
158
159     /**
160      * create the PDF renderer
161      */

162     public PDFRenderer() {
163         this.pdfDoc = new PDFDocument();
164     }
165
166     /**
167      * set up renderer options
168      *
169      * @param options Options for the renderer
170      */

171     public void setOptions(java.util.Map JavaDoc options) {
172         this.options = options;
173
174         // Process encryption options, if any exist
175
boolean encrypt = false;
176         String JavaDoc oPassword = "";
177         String JavaDoc uPassword = "";
178         boolean allowPrint = true;
179         boolean allowCopyContent = true;
180         boolean allowEditContent = true;
181         boolean allowEditAnnotations = true;
182         String JavaDoc option;
183
184         option = (String JavaDoc) options.get("ownerPassword");
185         if (option != null) {
186             encrypt = true;
187             oPassword = option;
188         }
189         option = (String JavaDoc) options.get("userPassword");
190         if (option != null) {
191             encrypt = true;
192             uPassword = option;
193         }
194         option = (String JavaDoc) options.get("allowPrint");
195         if (option != null) {
196             encrypt = true;
197             allowPrint = option.equals("TRUE");
198         }
199         option = (String JavaDoc) options.get("allowCopyContent");
200         if (option != null) {
201             encrypt = true;
202             allowCopyContent = option.equals("TRUE");
203         }
204         option = (String JavaDoc) options.get("allowEditContent");
205         if (option != null) {
206             encrypt = true;
207             allowEditContent = option.equals("TRUE");
208         }
209         option = (String JavaDoc) options.get("allowEditAnnotations");
210         if (option != null) {
211             encrypt = true;
212             allowEditAnnotations = option.equals("TRUE");
213         }
214         if (encrypt) {
215             this.pdfDoc.setEncryption(oPassword,uPassword,allowPrint,allowCopyContent,
216                                       allowEditContent, allowEditAnnotations);
217         }
218     }
219
220     /**
221      * set the PDF document's producer
222      *
223      * @param producer string indicating application producing PDF
224      */

225     public void setProducer(String JavaDoc producer) {
226         this.pdfDoc.setProducer(producer);
227     }
228
229     /**
230      * Starts the renderer
231      *
232      * @param stream OutputStream to be written to
233      * @exception IOException In case of an IO problem
234      */

235     public void startRenderer(OutputStream JavaDoc stream)
236         throws IOException JavaDoc {
237         pdfDoc.outputHeader(stream);
238     }
239
240     /**
241      * Called when the renderer has finished its work
242      *
243      * @param stream OutputStream to be written to
244      * @exception IOException In cas of an IO problem
245      */

246     public void stopRenderer(OutputStream JavaDoc stream)
247         throws IOException JavaDoc {
248         renderRootExtensions(extensions);
249         FontSetup.addToResources(this.pdfDoc, fontInfo);
250         pdfDoc.outputTrailer(stream);
251
252         // this frees up memory and makes the renderer reusable
253
this.pdfDoc = new PDFDocument();
254         this.pdfResources = null;
255         extensions = null;
256         currentStream = null;
257         currentAnnotList = null;
258         currentPage = null;
259         currentColor = null;
260         super.stopRenderer(stream);
261     }
262
263     /**
264      * add a line to the current stream
265      *
266      * @param x1 the start x location in millipoints
267      * @param y1 the start y location in millipoints
268      * @param x2 the end x location in millipoints
269      * @param y2 the end y location in millipoints
270      * @param th the thickness in millipoints
271      * @param stroke the stroke color/gradient
272      */

273     protected void addLine(int x1, int y1, int x2, int y2, int th,
274             PDFPathPaint stroke) {
275         closeText();
276
277         currentStream.add("ET\nq\n" + stroke.getColorSpaceOut(false)
278                 + (x1 / 1000f) + " " + (y1 / 1000f) + " m "
279                 + (x2 / 1000f) + " " + (y2 / 1000f) + " l "
280                 + (th / 1000f) + " w S\n" + "Q\nBT\n");
281     }
282
283     /**
284      * add a line to the current stream
285      *
286      * @param x1 the start x location in millipoints
287      * @param y1 the start y location in millipoints
288      * @param x2 the end x location in millipoints
289      * @param y2 the end y location in millipoints
290      * @param th the thickness in millipoints
291      * @param rs the rule style
292      * @param stroke the stroke color/gradient
293      */

294     protected void addLine(int x1, int y1, int x2, int y2, int th, int rs,
295             PDFPathPaint stroke) {
296         closeText();
297         currentStream.add("ET\nq\n" + stroke.getColorSpaceOut(false)
298                 + setRuleStylePattern(rs) + (x1 / 1000f) + " "
299                 + (y1 / 1000f) + " m " + (x2 / 1000f) + " "
300                 + (y2 / 1000f) + " l " + (th / 1000f) + " w S\n"
301                 + "Q\nBT\n");
302     }
303
304     /**
305      * add a rectangle to the current stream
306      *
307      * @param x the x position of left edge in millipoints
308      * @param y the y position of top edge in millipoints
309      * @param w the width in millipoints
310      * @param h the height in millipoints
311      * @param stroke the stroke color/gradient
312      */

313     protected void addRect(int x, int y, int w, int h, PDFPathPaint stroke) {
314         closeText();
315         currentStream.add("ET\nq\n" + stroke.getColorSpaceOut(false)
316                 + (x / 1000f) + " " + (y / 1000f) + " "
317                 + (w / 1000f) + " " + (h / 1000f) + " re s\n"
318                 + "Q\nBT\n");
319     }
320
321     /**
322      * add a filled rectangle to the current stream
323      *
324      * @param x the x position of left edge in millipoints
325      * @param y the y position of top edge in millipoints
326      * @param w the width in millipoints
327      * @param h the height in millipoints
328      * @param fill the fill color/gradient
329      * @param stroke the stroke color/gradient
330      */

331     protected void addRect(int x, int y, int w, int h, PDFPathPaint stroke,
332             PDFPathPaint fill) {
333         closeText();
334         currentStream.add("ET\nq\n" + fill.getColorSpaceOut(true)
335                 + stroke.getColorSpaceOut(false) + (x / 1000f)
336                 + " " + (y / 1000f) + " " + (w / 1000f) + " "
337                 + (h / 1000f) + " re b\n" + "Q\nBT\n");
338     }
339
340     /**
341      * add a filled rectangle to the current stream
342      *
343      * @param x the x position of left edge in millipoints
344      * @param y the y position of top edge in millipoints
345      * @param w the width in millipoints
346      * @param h the height in millipoints
347      * @param fill the fill color/gradient
348      */

349     protected void addFilledRect(int x, int y, int w, int h,
350             PDFPathPaint fill) {
351         closeText();
352         currentStream.add("ET\nq\n" + fill.getColorSpaceOut(true)
353                 + (x / 1000f) + " " + (y / 1000f) + " "
354                 + (w / 1000f) + " " + (h / 1000f) + " re f\n"
355                 + "Q\nBT\n");
356     }
357
358     /**
359      * Renders an image, scaling it to the given width and height. If the
360      * scaled width and height is the same intrinsic size of the image, the
361      * image is not scaled.
362      *
363      * @param x the x position of left edge in millipoints
364      * @param y the y position of top edge in millipoints
365      * @param w the width in millipoints
366      * @param h the height in millipoints
367      * @param image the image to be rendered
368      * @param fs the font state to use when rendering text in non-bitmapped
369      * images.
370      */

371     protected void drawImageScaled(int x, int y, int w, int h,
372             FopImage image,
373             FontState fs) {
374         if (image instanceof SVGImage) {
375             try {
376                 closeText();
377
378                 SVGDocument svg = ((SVGImage) image).getSVGDocument();
379                 currentStream.add("ET\nq\n");
380                 renderSVGDocument(svg, x, y, fs);
381                 currentStream.add("Q\nBT\n");
382             } catch (FopImageException e) {}
383
384         } else {
385             int xObjectNum = this.pdfDoc.addImage(image);
386             closeText();
387             currentStream.add("ET\nq\n" + (((float) w) / 1000f) + " 0 0 "
388                     + (((float) h) / 1000f) + " "
389                     + (((float) x) / 1000f) + " "
390                     + (((float) y - h) / 1000f) + " cm\n" + "/Im"
391                     + xObjectNum + " Do\nQ\nBT\n");
392         }
393     }
394
395     /**
396      * Renders an image, clipping it as specified.
397      *
398      * @param x the x position of left edge in millipoints.
399      * @param y the y position of top edge in millipoints.
400      * @param clipX the left edge of the clip in millipoints
401      * @param clipY the top edge of the clip in millipoints
402      * @param clipW the clip width in millipoints
403      * @param clipH the clip height in millipoints
404      * @param fs the font state to use when rendering text in non-bitmapped
405      * images.
406      * @param image the image to be painted
407      */

408     protected void drawImageClipped(int x, int y,
409             int clipX, int clipY,
410             int clipW, int clipH,
411             FopImage image,
412             FontState fs) {
413
414         float cx1 = ((float) x) / 1000f;
415         float cy1 = ((float) y - clipH) / 1000f;
416
417         float cx2 = ((float) x + clipW) / 1000f;
418         float cy2 = ((float) y) / 1000f;
419
420         int imgX = x - clipX;
421         int imgY = y - clipY;
422
423         int imgW;
424         int imgH;
425         try {
426             // XXX: do correct unit conversion here..
427
imgW = image.getWidth() * 1000;
428             imgH = image.getHeight() * 1000;
429         } catch (FopImageException fie) {
430             log.error("Error obtaining image width and height", fie);
431             return;
432         }
433
434         if (image instanceof SVGImage) {
435             try {
436                 closeText();
437
438                 SVGDocument svg = ((SVGImage) image).getSVGDocument();
439                 currentStream.add("ET\nq\n" +
440                 // clipping
441
cx1 + " " + cy1 + " m\n" +
442                         cx2 + " " + cy1 + " l\n" +
443                         cx2 + " " + cy2 + " l\n" +
444                         cx1 + " " + cy2 + " l\n" +
445                         "W\n" +
446                         "n\n");
447                 renderSVGDocument(svg, imgX, imgY, fs);
448                 currentStream.add("Q\nBT\n");
449             } catch (FopImageException e) {}
450
451         } else {
452             int xObjectNum = this.pdfDoc.addImage(image);
453             closeText();
454             currentStream.add("ET\nq\n" +
455             // clipping
456
cx1 + " " + cy1 + " m\n" +
457                     cx2 + " " + cy1 + " l\n" +
458                     cx2 + " " + cy2 + " l\n" +
459                     cx1 + " " + cy2 + " l\n" +
460                     "W\n" +
461                     "n\n" +
462             // image matrix
463
(((float) imgW) / 1000f) + " 0 0 " +
464                     (((float) imgH) / 1000f) + " " +
465                     (((float) imgX) / 1000f) + " " +
466                     (((float) imgY - imgH) / 1000f) + " cm\n" +
467                     "s\n" +
468             // the image itself
469
"/Im" + xObjectNum + " Do\nQ\nBT\n");
470         }
471     }
472
473     /**
474      * render a foreign object area
475      *
476      * @param area the foreign object area to be rendered
477      */

478     public void renderForeignObjectArea(ForeignObjectArea area) {
479         // if necessary need to scale and align the content
480
this.currentXPosition = this.currentXPosition + area.getXOffset();
481         this.currentYPosition = this.currentYPosition;
482         switch (area.getAlign()) {
483         case TextAlign.START:
484             break;
485         case TextAlign.END:
486             break;
487         case TextAlign.CENTER:
488         case TextAlign.JUSTIFY:
489             break;
490         }
491         switch (area.getVerticalAlign()) {
492         case VerticalAlign.BASELINE:
493             break;
494         case VerticalAlign.MIDDLE:
495             break;
496         case VerticalAlign.SUB:
497             break;
498         case VerticalAlign.SUPER:
499             break;
500         case VerticalAlign.TEXT_TOP:
501             break;
502         case VerticalAlign.TEXT_BOTTOM:
503             break;
504         case VerticalAlign.TOP:
505             break;
506         case VerticalAlign.BOTTOM:
507             break;
508         }
509         closeText();
510
511         // in general the content will not be text
512
currentStream.add("ET\n");
513         // align and scale
514
currentStream.add("q\n");
515         switch (area.scalingMethod()) {
516         case Scaling.UNIFORM:
517             break;
518         case Scaling.NON_UNIFORM:
519             break;
520         }
521         // if the overflow is auto (default), scroll or visible
522
// then the contents should not be clipped, since this
523
// is considered a printing medium.
524
switch (area.getOverflow()) {
525         case Overflow.VISIBLE:
526         case Overflow.SCROLL:
527         case Overflow.AUTO:
528             break;
529         case Overflow.HIDDEN:
530             break;
531         }
532
533         area.getObject().render(this);
534         currentStream.add("Q\n");
535         currentStream.add("BT\n");
536         this.currentXPosition += area.getEffectiveWidth();
537         // this.currentYPosition -= area.getEffectiveHeight();
538
}
539
540     /**
541      * render SVG area to PDF
542      *
543      * @param area the SVG area to render
544      */

545     public void renderSVGArea(SVGArea area) {
546         // place at the current instream offset
547
int x = this.currentXPosition;
548         int y = this.currentYPosition;
549         renderSVGDocument(area.getSVGDocument(), x, y, area.getFontState());
550     }
551
552     /**
553      * render SVG document to PDF
554      *
555      * @param doc the document to render
556      * @param x the x offset
557      * @param y the y offset
558      * @param fs the fontstate to use
559      */

560     protected void renderSVGDocument(Document doc, int x, int y,
561             FontState fs) {
562         float sx = 1;
563         float sy = -1;
564         int xOffset = x;
565         int yOffset = y;
566
567         org.apache.fop.svg.SVGUserAgent userAgent
568                  = new org.apache.fop.svg.SVGUserAgent(new AffineTransform JavaDoc());
569         userAgent.setLogger(log);
570
571         GVTBuilder builder = new GVTBuilder();
572         BridgeContext ctx = new BridgeContext(userAgent);
573         TextPainter textPainter = null;
574         Boolean JavaDoc bl =
575                 org.apache.fop.configuration.Configuration.getBooleanValue("strokeSVGText");
576         if (bl == null || bl.booleanValue()) {
577             textPainter = new StrokingTextPainter();
578         } else {
579             textPainter = new PDFTextPainter(fs);
580         }
581         ctx.setTextPainter(textPainter);
582
583         PDFAElementBridge aBridge = new PDFAElementBridge();
584         aBridge.setCurrentTransform(new AffineTransform JavaDoc(sx, 0, 0,
585                 sy, xOffset / 1000f, yOffset / 1000f));
586         ctx.putBridge(aBridge);
587
588         GraphicsNode root;
589         try {
590             root = builder.build(ctx, doc);
591         } catch (Exception JavaDoc e) {
592             log.error("svg graphic could not be built: "
593                     + e.getMessage(), e);
594             return;
595         }
596         // get the 'width' and 'height' attributes of the SVG document
597
float w = (float) ctx.getDocumentSize().getWidth() * 1000f;
598         float h = (float) ctx.getDocumentSize().getHeight() * 1000f;
599         ctx = null;
600         builder = null;
601
602         /*
603          * Clip to the svg area.
604          * Note: To have the svg overlay (under) a text area then use
605          * an fo:block-container
606          */

607         currentStream.add("q\n");
608         if (w != 0 && h != 0) {
609             currentStream.add(x / 1000f + " " + y / 1000f + " m\n");
610             currentStream.add((x + w) / 1000f + " " + y / 1000f + " l\n");
611             currentStream.add((x + w) / 1000f + " " + (y - h) / 1000f
612                     + " l\n");
613             currentStream.add(x / 1000f + " " + (y - h) / 1000f + " l\n");
614             currentStream.add("h\n");
615             currentStream.add("W\n");
616             currentStream.add("n\n");
617         }
618         // transform so that the coordinates (0,0) is from the top left
619
// and positive is down and to the right. (0,0) is where the
620
// viewBox puts it.
621
currentStream.add(sx + " 0 0 " + sy + " " + xOffset / 1000f + " "
622                 + yOffset / 1000f + " cm\n");
623
624         SVGSVGElement svg = ((SVGDocument) doc).getRootElement();
625         AffineTransform JavaDoc at = ViewBox.getPreserveAspectRatioTransform(svg,
626                 w / 1000f, h / 1000f);
627         if (!at.isIdentity()) {
628             double[] vals = new double[6];
629             at.getMatrix(vals);
630             currentStream.add(PDFNumber.doubleOut(vals[0], 8) + " "
631                     + PDFNumber.doubleOut(vals[1], 8) + " "
632                     + PDFNumber.doubleOut(vals[2], 8) + " "
633                     + PDFNumber.doubleOut(vals[3], 8) + " "
634                     + PDFNumber.doubleOut(vals[4], 8) + " "
635                     + PDFNumber.doubleOut(vals[5], 8) + " cm\n");
636         }
637
638         PDFGraphics2D graphics = new PDFGraphics2D(true, fs, pdfDoc,
639                 currentFontName,
640                 currentFontSize,
641                 currentXPosition,
642                 currentYPosition);
643         graphics.setGraphicContext(
644                 new org.apache.batik.ext.awt.g2d.GraphicContext());
645
646         try {
647             root.paint(graphics);
648             currentStream.add(graphics.getString());
649         } catch (Exception JavaDoc e) {
650             log.error("svg graphic could not be rendered: "
651                     + e.getMessage(), e);
652         }
653
654         currentAnnotList = graphics.getAnnotList();
655
656         currentStream.add("Q\n");
657     }
658
659     /**
660      * render inline area to PDF
661      *
662      * @param area inline area to render
663      */

664     public void renderWordArea(WordArea area) {
665         synchronized (_wordAreaPDF) {
666             StringBuffer JavaDoc pdf = _wordAreaPDF;
667             pdf.setLength(0);
668
669             Map JavaDoc kerning = null;
670             boolean kerningAvailable = false;
671
672             kerning = area.getFontState().getKerning();
673             if (kerning != null && !kerning.isEmpty()) {
674                 kerningAvailable = true;
675             }
676
677             String JavaDoc name = area.getFontState().getFontName();
678             int size = area.getFontState().getFontSize();
679
680             // This assumes that *all* CIDFonts use a /ToUnicode mapping
681
boolean useMultiByte = false;
682             Font f = (Font) area.getFontState().
683                     getFontInfo().getFonts().get(name);
684             if (f instanceof LazyFont) {
685                 if (((LazyFont) f).getRealFont() instanceof CIDFont) {
686                     useMultiByte = true;
687                 }
688             } else if (f instanceof CIDFont) {
689                 useMultiByte = true;
690             }
691             // String startText = useMultiByte ? "<FEFF" : "(";
692
String JavaDoc startText = useMultiByte ? "<" : "(";
693             String JavaDoc endText = useMultiByte ? "> " : ") ";
694
695             if ((!name.equals(this.currentFontName))
696                     || (size != this.currentFontSize)) {
697                 closeText();
698
699                 this.currentFontName = name;
700                 this.currentFontSize = size;
701                 pdf = pdf.append("/" + name + " " + ((float)size / 1000) + " Tf\n");
702             }
703
704             //Do letter spacing (must be outside of [..] TJ)
705
float letterspacing =
706                     ((float) area.getFontState().getLetterSpacing()) / 1000;
707             if (letterspacing != this.currentLetterSpacing) {
708                 this.currentLetterSpacing = letterspacing;
709                 closeText();
710                 pdf.append(letterspacing).append(" Tc\n");
711             }
712
713             PDFColor areaColor = null;
714             if (this.currentFill instanceof PDFColor) {
715                 areaColor = (PDFColor) this.currentFill;
716             }
717
718             if (areaColor == null || areaColor.red() != (double) area.getRed()
719                     || areaColor.green() != (double) area.getGreen()
720                     || areaColor.blue() != (double) area.getBlue()) {
721
722                 areaColor = new PDFColor((double) area.getRed(),
723                         (double) area.getGreen(),
724                         (double) area.getBlue());
725
726                 closeText();
727                 this.currentFill = areaColor;
728                 pdf.append(this.currentFill.getColorSpaceOut(true));
729             }
730
731             int rx = this.currentXPosition;
732             int bl = this.currentYPosition;
733
734             addWordLines(area, rx, bl, size, areaColor);
735
736             if (!textOpen || bl != prevWordY) {
737                 closeText();
738
739                 pdf.append("1 0 0 1 " + (rx / 1000f) + " " + (bl / 1000f)
740                         + " Tm [" + startText);
741                 prevWordY = bl;
742                 textOpen = true;
743             } else {
744                 // express the space between words in thousandths of an em
745
int space = prevWordX - rx + prevWordWidth;
746                 float emDiff = (float) space / (float) currentFontSize * 1000f;
747                 // this prevents a problem in Acrobat Reader where large
748
// numbers cause text to disappear or default to a limit
749
if (emDiff < -33000) {
750                     closeText();
751
752                     pdf.append("1 0 0 1 " + (rx / 1000f) + " " + (bl / 1000f)
753                             + " Tm [" + startText);
754                     textOpen = true;
755                 } else {
756                     pdf.append(Float.toString(emDiff));
757                     pdf.append(" ");
758                     pdf.append(startText);
759                 }
760             }
761             prevWordWidth = area.getContentWidth();
762             prevWordX = rx;
763
764             String JavaDoc s = area.getText();
765             int l = s.length();
766
767             for (int i = 0; i < l; i++) {
768                 char ch = area.getFontState().mapChar(s.charAt(i));
769
770                 if (!useMultiByte) {
771                     if (ch > 127) {
772                         pdf.append("\\");
773                         pdf.append(Integer.toOctalString((int) ch));
774
775                     } else {
776                         switch (ch) {
777                         case '(':
778                         case ')':
779                         case '\\':
780                             pdf.append("\\");
781                             break;
782                         }
783                         pdf.append(ch);
784                     }
785                 } else {
786                     pdf.append(getUnicodeString(ch));
787                 }
788
789                 if (kerningAvailable && (i + 1) < l) {
790                     addKerning(pdf, (new Integer JavaDoc((int) ch)),
791                             (new Integer JavaDoc((int) area.getFontState().mapChar(s.charAt(i + 1)))),
792                             kerning, startText, endText);
793                 }
794             }
795             pdf.append(endText);
796
797             currentStream.add(pdf.toString());
798
799             this.currentXPosition += area.getContentWidth();
800
801         }
802     }
803
804     /**
805      * Convert a char to a multibyte hex representation
806      *
807      * @param c character to be converted
808      * @return the string representation of the character
809      */

810     private String JavaDoc getUnicodeString(char c) {
811
812         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(4);
813
814         byte[] uniBytes = null;
815         try {
816             char[] a = {c};
817             uniBytes = new String JavaDoc(a).getBytes("UnicodeBigUnmarked");
818         } catch (Exception JavaDoc e) {
819             // This should never fail
820
throw new org.apache.avalon.framework.CascadingRuntimeException("Incompatible VM", e);
821         }
822
823         for (int i = 0; i < uniBytes.length; i++) {
824             int b = (uniBytes[i] < 0) ? (int) (256 + uniBytes[i])
825                     : (int) uniBytes[i];
826
827             String JavaDoc hexString = Integer.toHexString(b);
828             if (hexString.length() == 1) {
829                 buf = buf.append("0" + hexString);
830             } else {
831                 buf = buf.append(hexString);
832             }
833         }
834
835         return buf.toString();
836     }
837
838     /**
839      * Checks to see if we have some text rendering commands open still and
840      * writes out the TJ command to the stream if we do
841      */

842     private void closeText() {
843         if (textOpen) {
844             currentStream.add("] TJ\n");
845             textOpen = false;
846             prevWordX = 0;
847             prevWordY = 0;
848         }
849     }
850
851     private void addKerning(StringBuffer JavaDoc buf, Integer JavaDoc ch1, Integer JavaDoc ch2,
852             Map JavaDoc kerning, String JavaDoc startText,
853             String JavaDoc endText) {
854         Map JavaDoc kernPair = (Map JavaDoc) kerning.get(ch1);
855
856         if (kernPair != null) {
857             Integer JavaDoc width = (Integer JavaDoc) kernPair.get(ch2);
858             if (width != null) {
859                 buf.append(endText).append(-(width.intValue())).
860                         append(' ').append(startText);
861             }
862         }
863     }
864
865
866     /**
867      * render page to PDF
868      *
869      * @param page the page render
870      * @param outputStream the target OutputStream
871      * @exception FOPException in case of an internal problem
872      * @exception IOException in case of an IO problem
873      */

874     public void render(Page page, OutputStream JavaDoc outputStream)
875         throws FOPException, IOException JavaDoc {
876         // log.debug("rendering single page to PDF");
877
this.idReferences = page.getIDReferences();
878         this.pdfResources = this.pdfDoc.getResources();
879         this.pdfDoc.setIDReferences(idReferences);
880         this.renderPage(page);
881
882         List JavaDoc exts = page.getExtensions();
883         if (exts != null) {
884             extensions = exts;
885         }
886
887         // log.debug("writing out PDF");
888
this.pdfDoc.output(outputStream);
889     }
890
891     /**
892      * render page into PDF
893      *
894      * @param page page to render
895      */

896     public void renderPage(Page page) {
897         currentStream = this.pdfDoc.makeStream();
898
899         this.currentFontName = "";
900         this.currentFontSize = 0;
901
902         currentStream.add("BT\n");
903
904         renderRegions(page);
905
906         closeText();
907
908         float w = page.getWidth();
909         float h = page.getHeight();
910         currentStream.add("ET\n");
911
912         currentPage = this.pdfDoc.makePage(this.pdfResources, currentStream,
913                 Math.round(w / 1000),
914                 Math.round(h / 1000), page);
915
916         if (page.hasLinks() || currentAnnotList != null) {
917             if (currentAnnotList == null) {
918                 currentAnnotList = this.pdfDoc.makeAnnotList();
919             }
920             currentPage.setAnnotList(currentAnnotList);
921
922             List JavaDoc linkSets = page.getLinkSets();
923             for (int i = 0; i < linkSets.size(); i++) {
924                 LinkSet linkSet = (LinkSet)linkSets.get(i);
925
926                 linkSet.align();
927                 String JavaDoc dest = linkSet.getDest();
928                 int linkType = linkSet.getLinkType();
929                 List JavaDoc linkRects = linkSet.getRects();
930                 for (int j = 0; j < linkRects.size(); j++) {
931                     LinkedRectangle lrect = (LinkedRectangle)linkRects.get(j);
932                     currentAnnotList.addLink(
933                             this.pdfDoc.makeLink(lrect.getRectangle(),
934                             dest, linkType));
935                 }
936             }
937             currentAnnotList = null;
938         } else {
939             // just to be on the safe side
940
currentAnnotList = null;
941         }
942
943         // ensures that color is properly reset for blocks that carry over pages
944
this.currentFill = null;
945     }
946
947     /**
948      * defines a string containing dashArray and dashPhase for the rule style
949      *
950      * @param style the rule style
951      * @return PDF code to setup the rule style
952      */

953     private String JavaDoc setRuleStylePattern(int style) {
954         String JavaDoc rs = "";
955         switch (style) {
956         case org.apache.fop.fo.properties.RuleStyle.SOLID:
957             rs = "[] 0 d ";
958             break;
959         case org.apache.fop.fo.properties.RuleStyle.DASHED:
960             rs = "[3 3] 0 d ";
961             break;
962         case org.apache.fop.fo.properties.RuleStyle.DOTTED:
963             rs = "[1 3] 0 d ";
964             break;
965         case org.apache.fop.fo.properties.RuleStyle.DOUBLE:
966             rs = "[] 0 d ";
967             break;
968         default:
969             rs = "[] 0 d ";
970         }
971         return rs;
972     }
973
974     /**
975      * render root extensions such as outlines
976      *
977      * @param exts the list of root extensions to process
978      */

979     protected void renderRootExtensions(List JavaDoc extensions) {
980         if (extensions != null) {
981             for (int i = 0; i < extensions.size(); i++) {
982                 ExtensionObj ext = (ExtensionObj) extensions.get(i);
983                 if (ext instanceof Outline) {
984                     renderOutline((Outline) ext);
985                 } else if (ext instanceof Destination) {
986                     Destination d = (Destination)ext;
987                     pdfDoc.addDestination(d.getDestinationName(), d.getInternalDestination());
988                 }
989             }
990         }
991     }
992
993     private void renderOutline(Outline outline) {
994         PDFOutline outlineRoot = pdfDoc.getOutlineRoot();
995         PDFOutline pdfOutline = null;
996         Outline parent = outline.getParentOutline();
997         if (parent == null) {
998             pdfOutline =
999                     this.pdfDoc.makeOutline(outlineRoot,
1000                    outline.getLabel().toString(),
1001                    outline.getInternalDestination());
1002        } else {
1003            PDFOutline pdfParentOutline =
1004                    (PDFOutline) parent.getRendererObject();
1005            if (pdfParentOutline == null) {
1006                log.error("pdfParentOutline is null");
1007            } else {
1008                pdfOutline =
1009                        this.pdfDoc.makeOutline(pdfParentOutline,
1010                        outline.getLabel().toString(),
1011                        outline.getInternalDestination());
1012            }
1013
1014        }
1015        outline.setRendererObject(pdfOutline);
1016
1017        // handle sub outlines
1018
List JavaDoc v = outline.getOutlines();
1019        for (int i = 0; i < v.size(); i++) {
1020            renderOutline((Outline) v.get(i));
1021        }
1022    }
1023
1024}
1025
Popular Tags