KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > render > ps > PSRenderer


1 /*
2  * $Id: PSRenderer.java,v 1.15.2.22 2003/07/04 19:07:43 jeremias 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.ps;
52
53 // FOP
54
import org.apache.fop.svg.SVGArea;
55 import org.apache.fop.render.AbstractRenderer;
56 import org.apache.fop.image.ImageArea;
57 import org.apache.fop.image.FopImage;
58 import org.apache.fop.image.FopImageException;
59 import org.apache.fop.image.JpegImage;
60 import org.apache.fop.layout.*;
61 import org.apache.fop.layout.inline.*;
62 import org.apache.fop.datatypes.*;
63 import org.apache.fop.fo.properties.*;
64 import org.apache.fop.fonts.Glyphs;
65 import org.apache.fop.render.pdf.Font;
66 import org.apache.fop.image.*;
67 import org.apache.fop.apps.FOPException;
68
69 import org.apache.batik.bridge.*;
70 import org.apache.batik.gvt.*;
71
72 // SVG
73
import org.w3c.dom.*;
74 import org.w3c.dom.svg.SVGDocument;
75 import org.w3c.dom.svg.SVGSVGElement;
76
77 // Java
78
import java.io.IOException;
79 import java.io.OutputStream;
80 import java.util.Iterator;
81 import java.util.List;
82 import java.util.Map;
83 import java.awt.geom.AffineTransform;
84
85 /*
86 PostScript renderer
87
88 Remarks:
89 - If anyone modifies this renderer please make sure to also follow the DSC to
90   make it simpler to programmatically modify the generated Postscript files
91   (ex. extract pages etc.).
92 - The filters in use are hardcoded at the moment.
93 - Modified by Mark Lillywhite mark-fop@inomial.com, to use the new
94   Renderer interface. This PostScript renderer appears to be the
95   most efficient at producing output.
96
97 TODO-List:
98 - Character size/spacing
99 - SVG Transcoder for Batik
100 - configuration
101 - move to PrintRenderer
102 - maybe improve filters (I'm not very proud of them)
103 - add a RunLengthEncode filter (useful for Level 2 Postscript)
104 - Improve DocumentProcessColors stuff (probably needs to be configurable, then maybe
105   add a color to grayscale conversion for bitmaps to make output smaller (See
106   PCLRenderer)
107 - enhanced font support and font embedding
108 - fix character encodings (Helvetica uses WinAnsiEncoding internally but is
109   encoded as ISOLatin1 in PS)
110 - try to implement image transparency
111 - Add PPD support
112 - fix border painting (see table.fo)
113
114 */

115
116 /**
117  * Renderer that renders to PostScript.
118  * <br>
119  * This class currently generates PostScript Level 2 code. The only exception
120  * is the FlateEncode filter which is a Level 3 feature. The PostScript code
121  * generated follows the Document Structuring Conventions (DSC) version 3.0.
122  *
123  * @author Jeremias Märki
124  */

125 public class PSRenderer extends AbstractRenderer {
126
127     /**
128      * the application producing the PostScript
129      */

130     protected String producer;
131
132     int imagecount = 0; // DEBUG
133
int pagecount = 0;
134
135     private boolean enableComments = true;
136     private boolean autoRotateLandscape = false;
137
138     /**
139      * the stream used to output the PostScript
140      */

141     protected PSStream out;
142     private boolean ioTrouble = false;
143
144     private String currentFontName;
145     private int currentFontSize;
146     private int pageHeight;
147     private int pageWidth;
148     private float currRed;
149     private float currGreen;
150     private float currBlue;
151
152     private FontInfo fontInfo;
153
154     private int psLevel = 3;
155
156     protected java.util.Map options;
157
158
159     /**
160      * set the document's producer
161      *
162      * @param producer string indicating application producing the PostScript
163      */

164     public void setProducer(String producer) {
165         this.producer = producer;
166     }
167
168
169     /**
170      * set up renderer options
171      */

172     public void setOptions(java.util.Map options) {
173         this.options = options;
174     }
175
176
177     /**
178      * Sets the PostScript Level to generate.
179      *
180      * @param level You can specify either 2 or 3 for the PostScript Level
181      */

182     public void setPSLevel(int level) {
183         switch (level) {
184             case 2:
185             case 3:
186                 this.psLevel = level;
187                 break;
188             default:
189                 throw new IllegalArgumentException("Only PostScript Levels 2 and 3 are supported");
190         }
191     }
192
193
194     public int getPSLevel() {
195         return this.psLevel;
196     }
197
198     public void setAutoRotateLandscape(boolean value) {
199         this.autoRotateLandscape = value;
200     }
201
202     public boolean isAutoRotateLandscape() {
203         return this.autoRotateLandscape;
204     }
205
206     /**
207      * write out a command
208      */

209     protected void write(String cmd) {
210         try {
211             out.write(cmd);
212         } catch (IOException e) {
213             if (!ioTrouble)
214                 e.printStackTrace();
215             ioTrouble = true;
216         }
217     }
218
219     /**
220      * write out a comment
221      */

222     protected void comment(String comment) {
223         if (this.enableComments)
224             write(comment);
225     }
226
227     protected void writeProcs() {
228         write("%%BeginResource: procset FOPprocs");
229         write("%%Title: Utility procedures");
230         write("/FOPprocs 20 dict dup begin");
231         write("/bd{bind def}bind def");
232         write("/ld{load def}bd");
233         write("/M/moveto ld");
234         write("/RM/rmoveto ld");
235         write("/t/show ld");
236         write("/A/ashow ld");
237         write("/cp/closepath ld");
238         write("/re {4 2 roll M"); //define rectangle
239
write("1 index 0 rlineto");
240         write("0 exch rlineto");
241         write("neg 0 rlineto");
242         write("cp } bd");
243
244         write("/ux 0.0 def");
245         write("/uy 0.0 def");
246
247         // <font> <size> F
248
write("/F {");
249         write(" /Tp exch def");
250         // write(" currentdict exch get");
251
write(" /Tf exch def");
252         write(" Tf findfont Tp scalefont setfont");
253         write(" /cf Tf def /cs Tp def /cw ( ) stringwidth pop def");
254         write("} bd");
255
256         write("/ULS {currentpoint /uy exch def /ux exch def} bd");
257         write("/ULE {");
258         write(" /Tcx currentpoint pop def");
259         write(" gsave");
260         write(" newpath");
261         write(" cf findfont cs scalefont dup");
262         write(" /FontMatrix get 0 get /Ts exch def /FontInfo get dup");
263         write(" /UnderlinePosition get Ts mul /To exch def");
264         write(" /UnderlineThickness get Ts mul /Tt exch def");
265         write(" ux uy To add moveto Tcx uy To add lineto");
266         write(" Tt setlinewidth stroke");
267         write(" grestore");
268         write("} bd");
269
270         write("/OLE {");
271         write(" /Tcx currentpoint pop def");
272         write(" gsave");
273         write(" newpath");
274         write(" cf findfont cs scalefont dup");
275         write(" /FontMatrix get 0 get /Ts exch def /FontInfo get dup");
276         write(" /UnderlinePosition get Ts mul /To exch def");
277         write(" /UnderlineThickness get Ts mul /Tt exch def");
278         write(" ux uy To add cs add moveto Tcx uy To add cs add lineto");
279         write(" Tt setlinewidth stroke");
280         write(" grestore");
281         write("} bd");
282
283         write("/SOE {");
284         write(" /Tcx currentpoint pop def");
285         write(" gsave");
286         write(" newpath");
287         write(" cf findfont cs scalefont dup");
288         write(" /FontMatrix get 0 get /Ts exch def /FontInfo get dup");
289         write(" /UnderlinePosition get Ts mul /To exch def");
290         write(" /UnderlineThickness get Ts mul /Tt exch def");
291         write(" ux uy To add cs 10 mul 26 idiv add moveto Tcx uy To add cs 10 mul 26 idiv add lineto");
292         write(" Tt setlinewidth stroke");
293         write(" grestore");
294         write("} bd");
295         write("end def");
296         write("%%EndResource");
297     }
298
299     protected void writeFontDict(FontInfo fontInfo) {
300         write("%%BeginResource: procset FOPFonts");
301         write("%%Title: Font setup (shortcuts) for this file");
302         write("/FOPFonts 100 dict dup begin");
303         // write("/gfF1{/Helvetica findfont} bd");
304
// write("/gfF3{/Helvetica-Bold findfont} bd");
305
Map fonts = fontInfo.getFonts();
306         Iterator enum = fonts.keySet().iterator();
307         while (enum.hasNext()) {
308             String key = (String)enum.next();
309             Font fm = (Font)fonts.get(key);
310             write("/" + key + " /" + fm.fontName() + " def");
311         }
312         write("end def");
313         write("%%EndResource");
314         defineWinAnsiEncoding();
315
316         //Rewrite font encodings
317
enum = fonts.keySet().iterator();
318         while (enum.hasNext()) {
319             String key = (String)enum.next();
320             Font fm = (Font)fonts.get(key);
321             if (null == fm.encoding()) {
322                 //ignore (ZapfDingbats and Symbol run through here
323
//TODO: ZapfDingbats and Symbol should get getEncoding() fixed!
324
} else if ("WinAnsiEncoding".equals(fm.encoding())) {
325                 write("/" + fm.fontName() + " findfont");
326                 write("dup length dict begin");
327                 write(" {1 index /FID ne {def} {pop pop} ifelse} forall");
328                 write(" /Encoding " + fm.encoding() + " def");
329                 write(" currentdict");
330                 write("end");
331                 write("/" + fm.fontName() + " exch definefont pop");
332             } else {
333                 log.warn("Only WinAnsiEncoding is supported. Font '"
334                     + fm.fontName() + "' asks for: " + fm.encoding());
335             }
336         }
337     }
338
339     private void defineWinAnsiEncoding() {
340         write("/WinAnsiEncoding [");
341         StringBuffer sb = new StringBuffer();
342         for (int i = 0; i < Glyphs.winAnsiEncoding.length; i++) {
343             if (i > 0) {
344                 if ((i % 5) == 0) {
345                     write(sb.toString());
346                     sb.setLength(0);
347                 } else {
348                     sb.append(" ");
349                 }
350             }
351             final char ch = Glyphs.winAnsiEncoding[i];
352             final String glyphname = Glyphs.charToGlyphName(ch);
353             if ("".equals(glyphname)) {
354                 sb.append("/" + Glyphs.notdef);
355             } else {
356                 sb.append("/");
357                 sb.append(glyphname);
358             }
359         }
360         write(sb.toString());
361         write("] def");
362     }
363
364
365
366     protected void movetoCurrPosition() {
367         write(this.currentXPosition + " " + this.currentYPosition + " M");
368     }
369
370     /**
371      * set up the font info
372      *
373      * @param fontInfo the font info object to set up
374      */

375     public void setupFontInfo(FontInfo fontInfo)
376         throws FOPException {
377         /* use PDF's font setup to get PDF metrics */
378         org.apache.fop.render.pdf.FontSetup.setup(fontInfo);
379         this.fontInfo = fontInfo;
380     }
381
382     protected void addFilledRect(int x, int y, int w, int h,
383                                  ColorType col) {
384         // XXX: cater for braindead, legacy -ve heights
385
if (h < 0) {
386            h = -h;
387         }
388
389             write("newpath");
390             write(x + " " + y + " " + w + " " + -h + " re");
391             /*
392             write(x + " " + y + " M");
393             write(w + " 0 rlineto");
394             write("0 " + (-h) + " rlineto");
395             write((-w) + " 0 rlineto");
396             write("0 " + h + " rlineto");
397             write("closepath");
398             */

399             useColor(col);
400             write("fill");
401     }
402
403     /**
404      * render a display space to PostScript
405      *
406      * @param space the space to render
407      */

408     public void renderDisplaySpace(DisplaySpace space) {
409         // write("% --- DisplaySpace size="+space.getSize());
410
this.currentYPosition -= space.getSize();
411     }
412
413     /**
414      * render a foreign object area
415      */

416     public void renderForeignObjectArea(ForeignObjectArea area) {
417         // if necessary need to scale and align the content
418
this.currentXPosition = this.currentXPosition + area.getXOffset();
419         int plOffset = 0;
420         Area parent = area.getParent();
421         if (parent instanceof LineArea) {
422             plOffset = ((LineArea)parent).getPlacementOffset();
423         }
424         this.currentYPosition += plOffset;
425         area.getObject().render(this);
426         this.currentXPosition += area.getEffectiveWidth();
427         this.currentYPosition -= plOffset;
428     }
429
430     /**
431      * render an SVG area to PostScript
432      *
433      * @param area the area to render
434      */

435     public void renderSVGArea(SVGArea area) {
436         int x = this.currentXPosition;
437         int y = this.currentYPosition;
438         renderSVGDocument(area.getSVGDocument(), x, y, area.getFontState());
439     }
440
441
442     /**
443      * render SVG document to PostScript
444      *
445      * @param doc the document to render
446      * @param x the x offset
447      * @param y the y offset
448      * @param fs the fontstate to use
449      */

450     protected void renderSVGDocument(Document doc, int x, int y,
451             FontState fs) {
452         org.apache.fop.svg.SVGUserAgent userAgent
453             = new org.apache.fop.svg.SVGUserAgent(new AffineTransform());
454         userAgent.setLogger(log);
455
456         GVTBuilder builder = new GVTBuilder();
457         BridgeContext ctx = new BridgeContext(userAgent);
458
459         GraphicsNode root;
460         try {
461             root = builder.build(ctx, doc);
462         } catch (Exception e) {
463             log.error("svg graphic could not be built: "
464                                    + e.getMessage(), e);
465             return;
466         }
467         // get the 'width' and 'height' attributes of the SVG document
468
float w = (float)ctx.getDocumentSize().getWidth() * 1000f;
469         float h = (float)ctx.getDocumentSize().getHeight() * 1000f;
470
471         //log.debug("drawing SVG image: "+x+"/"+y+" "+w+"/"+h);
472
SVGSVGElement svg = ((SVGDocument) doc).getRootElement();
473         AffineTransform at = ViewBox.getPreserveAspectRatioTransform(svg,
474                 w/1000f , h/1000f );
475
476         ctx = null;
477         builder = null;
478
479         float sx = 1, sy = -1;
480         int xOffset = x, yOffset = y;
481
482         comment("% --- SVG Area");
483         write("gsave");
484         if (w != 0 && h != 0) {
485             write("newpath");
486             write(x + " " + y + " M");
487             write((x + w) + " " + y + " rlineto");
488             write((x + w) + " " + (y - h) + " rlineto");
489             write(x + " " + (y - h) + " rlineto");
490             write("closepath");
491             write("clippath");
492         }
493         // transform so that the coordinates (0,0) is from the top left
494
// and positive is down and to the right. (0,0) is where the
495
// viewBox puts it.
496
write(xOffset + " " + yOffset + " translate");
497         write((at.getTranslateX() * 1000) + " "
498             + (-at.getTranslateY() * 1000) + " translate");
499         write(sx * at.getScaleX() + " " + sy * at.getScaleY() + " scale");
500
501         PSGraphics2D graphics = new PSGraphics2D(false, fs,
502                                 this, currentFontName,
503                                 currentFontSize,
504                                 currentXPosition,
505                                 currentYPosition);
506         graphics.setGraphicContext(new org.apache.batik.ext.awt.g2d.GraphicContext());
507         try {
508             root.paint(graphics);
509         } catch (Exception e) {
510             log.error("svg graphic could not be rendered: "
511                                    + e.getMessage(), e);
512         }
513
514         write("grestore");
515
516         comment("% --- SVG Area end");
517     }
518
519     /**
520      * Renders an image, scaling it to the given width and height.
521      * If the scaled width and height is the same intrinsic size
522      * of the image, the image is not scaled.
523      *
524      * @param x the x position of left edge in millipoints
525      * @param y the y position of top edge in millipoints
526      * @param w the width in millipoints
527      * @param h the height in millipoints
528      * @param image the image to be rendered
529      * @param fs the font state to use when rendering text
530      * in non-bitmapped images.
531      */

532     protected void drawImageScaled(int x, int y, int w, int h,
533                    FopImage image,
534                    FontState fs) {
535         //log.debug("drawing scaled image: "+x+"/"+y+" "+w+"/"+h);
536
if (image instanceof SVGImage) {
537             try {
538                 renderSVGDocument(((SVGImage)image).getSVGDocument(), x, y, fs);
539             } catch (FopImageException e) {
540                 log.error("Error rendering SVG image", e);
541             }
542         } else if (image instanceof EPSImage) {
543             renderEPS(image, x, y, w, h);
544         } else {
545             renderBitmap(image, x, y, w, h);
546         }
547     }
548
549     /**
550      * Renders an image, clipping it as specified.
551      *
552      * @param x the x position of left edge in millipoints.
553      * @param y the y position of top edge in millipoints.
554      * @param clipX the left edge of the clip in millipoints
555      * @param clipY the top edge of the clip in millipoints
556      * @param clipW the clip width in millipoints
557      * @param clipH the clip height in millipoints
558      * @param image the image to be rendered
559      * @param fs the font state to use when rendering text
560      * in non-bitmapped images.
561      */

562     protected void drawImageClipped(int x, int y,
563                     int clipX, int clipY,
564                     int clipW, int clipH,
565                     FopImage image,
566                     FontState fs) {
567         //log.debug("drawing clipped image: "+x+"/"+y+" "+clipX+"/"+clipY+" "+clipW+"/"+clipH);
568
write("gsave");
569         write(clipX + " " + clipY + " " + clipW + " " + clipH + " re");
570         write("clippath");
571
572         try {
573             int w = image.getWidth() * 1000;
574             int h = image.getHeight() * 1000;
575
576             drawImageScaled(x, y, w, h, image, fs);
577         } catch (FopImageException e) {
578             log.error("Error getting image extents", e);
579         }
580         write("grestore");
581     }
582
583     public void renderEPS(FopImage img, int x, int y, int w, int h) {
584         try {
585             EPSImage eimg = (EPSImage)img;
586             int[] bbox = eimg.getBBox();
587             int bboxw = bbox[2] - bbox[0];
588             int bboxh = bbox[3] - bbox[1];
589
590
591             write("%%BeginDocument: " + eimg.getDocName());
592             write("BeginEPSF");
593
594             write(x + " " + (y - h) + " translate");
595             write("0.0 rotate");
596             write((long)(w/bboxw) + " " + (long)(h/bboxh) + " scale");
597             write(-bbox[0] + " " + (-bbox[1]) + " translate");
598             write(bbox[0] + " " + bbox[1] + " " + bboxw + " " + bboxh + " rectclip");
599             write("newpath");
600             out.writeByteArr(img.getBitmaps());
601             write("%%EndDocument");
602             write("EndEPSF");
603             write("");
604         } catch (Exception e) {
605             e.printStackTrace();
606             log.error("PSRenderer.renderImageArea(): Error rendering bitmap ("
607                                    + e.getMessage() + ")", e);
608         }
609     }
610
611     public void renderBitmap(FopImage img, int x, int y, int w, int h) {
612         try {
613             boolean iscolor = img.getColorSpace().getColorSpace()
614                               != ColorSpace.DEVICE_GRAY;
615             byte[] imgmap = img.getBitmaps();
616
617             write("gsave");
618             if (img.getColorSpace().getColorSpace() == ColorSpace.DEVICE_CMYK)
619                 write("/DeviceCMYK setcolorspace");
620             else if (img.getColorSpace().getColorSpace() == ColorSpace.DEVICE_GRAY)
621                 write("/DeviceGray setcolorspace");
622             else
623                 write("/DeviceRGB setcolorspace");
624
625             write(x + " " + (y - h) + " translate");
626             write(w + " " + h + " scale");
627
628             write("{{");
629             // Template: (RawData is used for the EOF signal only)
630
// write("/RawData currentfile <first filter> filter def");
631
// write("/Data RawData <second filter> <third filter> [...] def");
632
if (img instanceof JpegImage) {
633                 write("/RawData currentfile /ASCII85Decode filter def");
634                 write("/Data RawData << >> /DCTDecode filter def");
635             } else {
636                 if (this.psLevel >= 3) {
637                     write("/RawData currentfile /ASCII85Decode filter def");
638                     write("/Data RawData /FlateDecode filter def");
639                 } else {
640                     write("/RawData currentfile /ASCII85Decode filter def");
641                     write("/Data RawData /RunLengthDecode filter def");
642                 }
643             }
644             write("<<");
645             write(" /ImageType 1");
646             write(" /Width " + img.getWidth());
647             write(" /Height " + img.getHeight());
648             write(" /BitsPerComponent 8");
649             if (img.getColorSpace().getColorSpace() == ColorSpace.DEVICE_CMYK) {
650                 if (img.invertImage())
651                     write(" /Decode [1 0 1 0 1 0 1 0]");
652                 else
653                     write(" /Decode [0 1 0 1 0 1 0 1]");
654             } else if (iscolor) {
655                 write(" /Decode [0 1 0 1 0 1]");
656             } else {
657                 write(" /Decode [0 1]");
658             }
659             // Setup scanning for left-to-right and top-to-bottom
660
write(" /ImageMatrix [" + img.getWidth() + " 0 0 -"
661                   + img.getHeight() + " 0 " + img.getHeight() + "]");
662
663             write(" /DataSource Data");
664             write(">>");
665             write("image");
666             /* the following two lines could be enabled if something still goes wrong
667              * write("Data closefile");
668              * write("RawData flushfile");
669              */

670             write("} stopped {handleerror} if");
671             write(" RawData flushfile");
672             write("} exec");
673
674             /*
675              * for (int y=0; y<img.getHeight(); y++) {
676              * int indx = y * img.getWidth();
677              * if (iscolor) indx*= 3;
678              * for (int x=0; x<img.getWidth(); x++) {
679              * if (iscolor) {
680              * writeASCIIHex(imgmap[indx++] & 0xFF);
681              * writeASCIIHex(imgmap[indx++] & 0xFF);
682              * writeASCIIHex(imgmap[indx++] & 0xFF);
683              * } else {
684              * writeASCIIHex(imgmap[indx++] & 0xFF);
685              * }
686              * }
687              * }
688              */

689             try {
690                 // imgmap[0] = 1;
691
OutputStream out = this.out;
692                 out = new ASCII85OutputStream(out);
693                 if (img instanceof JpegImage) {
694                     //nop
695
} else {
696                     if (this.psLevel >= 3) {
697                         out = new FlateEncodeOutputStream(out);
698                     } else {
699                         out = new RunLengthEncodeOutputStream(out);
700                     }
701                 }
702                 out.write(imgmap);
703                 if (out instanceof Finalizable) {
704                     ((Finalizable)out).finalizeStream();
705                 } else {
706                     out.flush();
707                 }
708             } catch (IOException e) {
709                 if (!ioTrouble)
710                     e.printStackTrace();
711                 ioTrouble = true;
712             }
713
714             write("");
715             write("grestore");
716         } catch (FopImageException e) {
717             log.error("PSRenderer.renderImageArea(): Error rendering bitmap ("
718                                    + e.getMessage() + ")", e);
719         }
720     }
721
722     /**
723      * Render an image area.
724      *
725      * @param area the image area to render
726      */

727     public void renderImageArea(ImageArea area) {
728         // adapted from contribution by BoBoGi
729
int x = this.currentXPosition + area.getXOffset();
730         int ploffset = 0;
731         if (area.getParent() instanceof LineArea) {
732             ploffset = ((LineArea)area.getParent()).getPlacementOffset();
733         }
734         int y = this.currentYPosition + ploffset;
735         int w = area.getContentWidth();
736         int h = area.getHeight();
737
738         //this.currentYPosition -= h;
739
this.currentXPosition += w;
740
741         FopImage img = area.getImage();
742
743         if (img == null) {
744             log.error("Error while loading image: area.getImage() is null");
745         } else {
746             drawImageScaled(x, y, w, h, img, area.getFontState());
747         }
748     }
749
750
751     /**
752      * render an image area to PostScript
753      *
754      * @param area the area to render
755      */

756      /*
757     public void renderImageArea(ImageArea area) {
758         int x = this.currentXPosition + area.getXOffset();
759         int ploffset = 0;
760         if (area.getParent() instanceof LineArea) {
761             ploffset = ((LineArea)area.getParent()).getPlacementOffset();
762         }
763         int y = this.currentYPosition + ploffset;
764         int w = area.getContentWidth();
765         int h = area.getHeight();
766         this.currentYPosition -= area.getHeight();
767
768         imagecount++;
769         // if (imagecount!=4) return;
770
771         comment("% --- ImageArea");
772         if (area.getImage() instanceof SVGImage) {
773             renderSVGDocument(((SVGImage)area.getImage()).getSVGDocument(), x, y, area.getFontState());
774         } else if (area.getImage() instanceof EPSImage) {
775             renderEPS(area.getImage(), x, y, w, h);
776         } else {
777             renderBitmap(area.getImage(), x, y, w, h);
778         }
779         comment("% --- ImageArea end");
780     }*/

781
782     /**
783      * render an inline area to PostScript
784      *
785      * @param area the area to render
786      */

787     public void renderWordArea(WordArea area) {
788         movetoCurrPosition();
789         FontState fs = area.getFontState();
790         String fontWeight = fs.getFontWeight();
791         StringBuffer sb = new StringBuffer();
792         String s = area.getText();
793         int l = s.length();
794
795         for (int i = 0; i < l; i++) {
796             char ch = s.charAt(i);
797             char mch = fs.mapChar(ch);
798
799             if (mch > 127) {
800                 sb = sb.append("\\" + Integer.toOctalString(mch));
801             } else {
802                 final String escape = "\\()[]{}";
803                 if (escape.indexOf(mch) >= 0) {
804                     sb.append("\\");
805                 }
806                 sb = sb.append(mch);
807             }
808         }
809
810         String psString = null;
811         if (area.getFontState().getLetterSpacing() > 0) {
812             //float f = area.getFontState().getLetterSpacing() * 1000 / this.currentFontSize;
813
float f = area.getFontState().getLetterSpacing();
814             psString = (new StringBuffer().append(f).append(" 0.0 (")
815               .append(sb.toString()).append(") A")).toString();
816         } else {
817             psString = (new StringBuffer("(").append(sb.toString())
818               .append(") t")).toString();
819         }
820
821
822         // System.out.println("["+s+"] --> ["+sb.toString()+"]");
823

824         // comment("% --- InlineArea font-weight="+fontWeight+": " + sb.toString());
825
useFont(fs.getFontName(), fs.getFontSize());
826         useColor(area.getRed(), area.getGreen(), area.getBlue());
827         if (area.getUnderlined() || area.getLineThrough()
828                 || area.getOverlined())
829             write("ULS");
830         write(psString);
831         if (area.getUnderlined())
832             write("ULE");
833         if (area.getLineThrough())
834             write("SOE");
835         if (area.getOverlined())
836             write("OLE");
837         this.currentXPosition += area.getContentWidth();
838     }
839
840     public void useFont(String name, int size) {
841         if ((currentFontName != name) || (currentFontSize != size)) {
842             write(name + " " + size + " F");
843             currentFontName = name;
844             currentFontSize = size;
845         }
846     }
847
848     /**
849      * render an inline space to PostScript
850      *
851      * @param space the space to render
852      */

853     public void renderInlineSpace(InlineSpace space) {
854         // write("% --- InlineSpace size="+space.getSize());
855
if (space.getUnderlined() || space.getLineThrough()
856                 || space.getOverlined()) {
857             //start textdeko
858
movetoCurrPosition();
859             write("ULS");
860
861             write(space.getSize() + " 0 RM");
862
863             //end textdeko
864
if (space.getUnderlined())
865                 write("ULE");
866             if (space.getLineThrough())
867                 write("SOE");
868             if (space.getOverlined())
869                 write("OLE");
870         }
871         this.currentXPosition += space.getSize();
872     }
873
874     /**
875      * render a line area to PostScript
876      *
877      * @param area the area to render
878      */

879     public void renderLineArea(LineArea area) {
880         int rx = this.currentAreaContainerXPosition + area.getStartIndent();
881         int ry = this.currentYPosition;
882         int w = area.getContentWidth();
883         int h = area.getHeight();
884
885         this.currentYPosition -= area.getPlacementOffset();
886         this.currentXPosition = rx;
887
888         int bl = this.currentYPosition;
889         // method is identical to super method except next line
890
movetoCurrPosition();
891
892         String fontWeight = area.getFontState().getFontWeight();
893         //comment("% --- LineArea begin font-weight="+fontWeight);
894
List children = area.getChildren();
895         for (int i = 0; i < children.size(); i++) {
896             Box b = (Box)children.get(i);
897             this.currentYPosition = ry - area.getPlacementOffset();
898             b.render(this);
899         }
900         //comment("% --- LineArea end");
901

902         this.currentYPosition = ry - h;
903         this.currentXPosition = rx;
904     }
905
906     /**
907      * render a page to PostScript
908      *
909      * @param page the page to render
910      */

911     public void renderPage(Page page) {
912         this.pagecount++;
913         this.idReferences = page.getIDReferences();
914
915         write("%%Page: " + page.getNumber() + " " + this.pagecount);
916
917         final long pagewidth = page.getWidth();
918         final long pageheight = page.getHeight();
919         final double pspagewidth = pagewidth / 1000f;
920         final double pspageheight = pageheight / 1000f;
921         boolean rotate = false;
922         if (isAutoRotateLandscape() && (pageheight < pagewidth)) {
923             rotate = true;
924             write("%%PageBoundingBox: 0 0 " +
925                     Math.round(pspageheight) + " " +
926                     Math.round(pspagewidth));
927             write("%%PageOrientation: Landscape");
928         } else {
929             write("%%PageBoundingBox: 0 0 " +
930                     Math.round(pspagewidth) + " " +
931                     Math.round(pspageheight));
932             if (isAutoRotateLandscape()) {
933                 write("%%PageOrientation: Portrait");
934             }
935         }
936
937         write("%%BeginPageSetup");
938         if (rotate) {
939             write(Math.round(pspageheight) + " 0 translate");
940             write("90 rotate");
941         }
942         write("0.001 0.001 scale");
943         write("%%EndPageSetup");
944         renderRegions(page);
945         write("showpage");
946         write("%%PageTrailer");
947         write("%%EndPage"); //This is non-standard, but used by Adobe.
948
}
949
950     /**
951      * render a leader area to PostScript
952      *
953      * @param area the area to render
954      */

955     public void renderLeaderArea(LeaderArea area) {
956         int rx = this.currentXPosition;
957         int ry = this.currentYPosition;
958         int w = area.getContentWidth();
959         int th = area.getRuleThickness();
960         int th2 = th / 2;
961         int th3 = th / 3;
962         int th4 = th / 4;
963
964         switch (area.getLeaderPattern()) {
965         case LeaderPattern.SPACE:
966             // NOP
967

968             break;
969         case LeaderPattern.RULE:
970             if (area.getRuleStyle() == RuleStyle.NONE)
971                 break;
972             useColor(area.getRed(), area.getGreen(), area.getBlue());
973             write("gsave");
974             write("0 setlinecap");
975             switch (area.getRuleStyle()) {
976             case RuleStyle.DOTTED:
977                 write("newpath");
978                 write("[1000 3000] 0 setdash");
979                 write(th + " setlinewidth");
980                 write(rx + " " + ry + " M");
981                 write(w + " 0 rlineto");
982                 useColor(area.getRed(), area.getGreen(), area.getBlue());
983                 write("stroke");
984                 break;
985             case RuleStyle.DASHED:
986                 write("newpath");
987                 write("[3000 3000] 0 setdash");
988                 write(th + " setlinewidth");
989                 write(rx + " " + ry + " M");
990                 write(w + " 0 rlineto");
991                 useColor(area.getRed(), area.getGreen(), area.getBlue());
992                 write("stroke");
993                 break;
994             case RuleStyle.SOLID:
995                 write("newpath");
996                 write(th + " setlinewidth");
997                 write(rx + " " + ry + " M");
998                 write(w + " 0 rlineto");
999                 useColor(area.getRed(), area.getGreen(), area.getBlue());
1000                write("stroke");
1001                break;
1002            case RuleStyle.DOUBLE:
1003                write("newpath");
1004                write(th3 + " setlinewidth");
1005                write(rx + " " + (ry - th3) + " M");
1006                write(w + " 0 rlineto");
1007                write(rx + " " + (ry + th3) + " M");
1008                write(w + " 0 rlineto");
1009                useColor(area.getRed(), area.getGreen(), area.getBlue());
1010                write("stroke");
1011                break;
1012            case RuleStyle.GROOVE:
1013                write(th2 + " setlinewidth");
1014                write("newpath");
1015                write(rx + " " + (ry - th4) + " M");
1016                write(w + " 0 rlineto");
1017                useColor(area.getRed(), area.getGreen(), area.getBlue());
1018                write("stroke");
1019                write("newpath");
1020                write(rx + " " + (ry + th4) + " M");
1021                write(w + " 0 rlineto");
1022                useColor(1, 1, 1); // white
1023
write("stroke");
1024                break;
1025            case RuleStyle.RIDGE:
1026                write(th2 + " setlinewidth");
1027                write("newpath");
1028                write(rx + " " + (ry - th4) + " M");
1029                write(w + " 0 rlineto");
1030                useColor(1, 1, 1); // white
1031
write("stroke");
1032                write("newpath");
1033                write(rx + " " + (ry + th4) + " M");
1034                write(w + " 0 rlineto");
1035                useColor(area.getRed(), area.getGreen(), area.getBlue());
1036                write("stroke");
1037                break;
1038            }
1039            write("grestore");
1040            break;
1041        case LeaderPattern.DOTS:
1042            comment("% --- Leader dots NYI");
1043            log.error("Leader dots: Not yet implemented");
1044            break;
1045        case LeaderPattern.USECONTENT:
1046            comment("% --- Leader use-content NYI");
1047            log.error("Leader use-content: Not yet implemented");
1048            break;
1049        }
1050        this.currentXPosition += area.getContentWidth();
1051        write(area.getContentWidth() + " 0 RM");
1052    }
1053
1054    protected void doFrame(Area area) {
1055        int w, h;
1056        int rx = this.currentAreaContainerXPosition;
1057        w = area.getContentWidth();
1058        BorderAndPadding bap = area.getBorderAndPadding();
1059
1060        if (area instanceof BlockArea)
1061            rx += ((BlockArea)area).getStartIndent();
1062
1063        h = area.getContentHeight();
1064        int ry = this.currentYPosition;
1065
1066        rx = rx - area.getPaddingLeft();
1067        ry = ry + area.getPaddingTop();
1068        w = w + area.getPaddingLeft() + area.getPaddingRight();
1069        h = h + area.getPaddingTop() + area.getPaddingBottom();
1070
1071        rx = rx - area.getBorderLeftWidth();
1072        ry = ry + area.getBorderTopWidth();
1073        w = w + area.getBorderLeftWidth() + area.getBorderRightWidth();
1074        h = h + area.getBorderTopWidth() + area.getBorderBottomWidth();
1075
1076        doBackground(area, rx, ry, w, h);
1077
1078        if (area.getBorderTopWidth() != 0) {
1079            write("newpath");
1080            write(rx + " " + ry + " M");
1081            write(w + " 0 rlineto");
1082            write(area.getBorderTopWidth() + " setlinewidth");
1083            write("0 setlinecap");
1084            useColor(bap.getBorderColor(BorderAndPadding.TOP));
1085            write("stroke");
1086        }
1087        if (area.getBorderLeftWidth() != 0) {
1088            write("newpath");
1089            write(rx + " " + ry + " M");
1090            write("0 " + (-h) + " rlineto");
1091            write(area.getBorderLeftWidth() + " setlinewidth");
1092            write("0 setlinecap");
1093            useColor(bap.getBorderColor(BorderAndPadding.LEFT));
1094            write("stroke");
1095        }
1096        if (area.getBorderRightWidth() != 0) {
1097            write("newpath");
1098            write((rx + w) + " " + ry + " M");
1099            write("0 " + (-h) + " rlineto");
1100            write(area.getBorderRightWidth() + " setlinewidth");
1101            write("0 setlinecap");
1102            useColor(bap.getBorderColor(BorderAndPadding.RIGHT));
1103            write("stroke");
1104        }
1105        if (area.getBorderBottomWidth() != 0) {
1106            write("newpath");
1107            write(rx + " " + (ry - h) + " M");
1108            write(w + " 0 rlineto");
1109            write(area.getBorderBottomWidth() + " setlinewidth");
1110            write("0 setlinecap");
1111            useColor(bap.getBorderColor(BorderAndPadding.BOTTOM));
1112            write("stroke");
1113        }
1114    }
1115
1116    private void useColor(ColorType col) {
1117        useColor(col.red(), col.green(), col.blue());
1118    }
1119
1120    private void useColor(float red, float green, float blue) {
1121        if ((red != currRed) || (green != currGreen) || (blue != currBlue)) {
1122            write(red + " " + green + " " + blue + " setrgbcolor");
1123            currRed = red;
1124            currGreen = green;
1125            currBlue = blue;
1126        }
1127    }
1128
1129    /**
1130      Default start renderer method. This would
1131      normally be overridden. (mark-fop@inomial.com).
1132    */

1133    public void startRenderer(OutputStream outputStream)
1134    throws IOException {
1135        log.debug("rendering areas to PostScript");
1136
1137        this.pagecount = 0;
1138        this.out = new PSStream(outputStream);
1139        write("%!PS-Adobe-3.0");
1140        if (this.producer == null) {
1141            this.producer = org.apache.fop.apps.Version.getVersion();
1142        }
1143        write("%%Creator: "+this.producer);
1144        write("%%Pages: (atend)");
1145        write("%%DocumentProcessColors: Black");
1146        write("%%DocumentSuppliedResources: procset FOPFonts");
1147        write("%%EndComments");
1148        write("%%BeginDefaults");
1149        write("%%EndDefaults");
1150        write("%%BeginProlog");
1151        write("%%EndProlog");
1152        write("%%BeginSetup");
1153        writeProcs();
1154        writeFontDict(fontInfo);
1155
1156        /* Write proc for including EPS */
1157        write("%%BeginResource: procset EPSprocs");
1158        write("%%Title: EPS encapsulation procs");
1159
1160        write("/BeginEPSF { %def");
1161        write("/b4_Inc_state save def % Save state for cleanup");
1162        write("/dict_count countdictstack def % Count objects on dict stack");
1163        write("/op_count count 1 sub def % Count objects on operand stack");
1164        write("userdict begin % Push userdict on dict stack");
1165        write("/showpage { } def % Redefine showpage, { } = null proc");
1166        write("0 setgray 0 setlinecap % Prepare graphics state");
1167        write("1 setlinewidth 0 setlinejoin");
1168        write("10 setmiterlimit [ ] 0 setdash newpath");
1169        write("/languagelevel where % If level not equal to 1 then");
1170        write("{pop languagelevel % set strokeadjust and");
1171        write("1 ne % overprint to their defaults.");
1172        write("{false setstrokeadjust false setoverprint");
1173        write("} if");
1174        write("} if");
1175        write("} bind def");
1176
1177        write("/EndEPSF { %def");
1178        write("count op_count sub {pop} repeat % Clean up stacks");
1179        write("countdictstack dict_count sub {end} repeat");
1180        write("b4_Inc_state restore");
1181        write("} bind def");
1182        write("%%EndResource");
1183
1184        write("FOPprocs begin");
1185        write("FOPFonts begin");
1186
1187        write("%%EndSetup");
1188    }
1189
1190    /**
1191      Default stop renderer method. This would
1192      normally be overridden. (mark-fop@inomial.com).
1193    */

1194    public void stopRenderer(OutputStream outputStream)
1195    throws IOException {
1196        write("%%Trailer");
1197        write("%%Pages: "+this.pagecount);
1198        write("%%EOF");
1199        this.out.flush();
1200        log.debug("written out PostScript");
1201    }
1202
1203    public void render(Page page, OutputStream outputStream) {
1204        this.renderPage(page);
1205    }
1206}
1207
Popular Tags