KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > render > afp > AFPRenderer


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 /* $Id: AFPRenderer.java 454018 2006-10-07 21:00:13Z pietsch $ */
19
20 package org.apache.fop.render.afp;
21
22 import java.awt.Color JavaDoc;
23 import java.awt.Rectangle JavaDoc;
24 import java.awt.geom.Rectangle2D JavaDoc;
25 import java.awt.image.BufferedImage JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.io.OutputStream JavaDoc;
28 import java.io.UnsupportedEncodingException JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.HashMap JavaDoc;
31 import java.util.HashSet JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.List JavaDoc;
34 import java.util.Map JavaDoc;
35
36 import org.apache.avalon.framework.configuration.Configuration;
37 import org.apache.avalon.framework.configuration.ConfigurationException;
38 import org.apache.commons.io.output.ByteArrayOutputStream;
39 import org.apache.fop.apps.FOUserAgent;
40 import org.apache.fop.apps.MimeConstants;
41 import org.apache.fop.area.Block;
42 import org.apache.fop.area.BlockViewport;
43 import org.apache.fop.area.BodyRegion;
44 import org.apache.fop.area.CTM;
45 import org.apache.fop.area.OffDocumentItem;
46 import org.apache.fop.area.PageViewport;
47 import org.apache.fop.area.RegionReference;
48 import org.apache.fop.area.RegionViewport;
49 import org.apache.fop.area.Trait;
50 import org.apache.fop.area.inline.Leader;
51 import org.apache.fop.area.inline.Image;
52 import org.apache.fop.area.inline.SpaceArea;
53 import org.apache.fop.area.inline.TextArea;
54 import org.apache.fop.area.inline.WordArea;
55 import org.apache.fop.fo.Constants;
56 import org.apache.fop.fo.extensions.ExtensionAttachment;
57 import org.apache.fop.fonts.FontInfo;
58 import org.apache.fop.fonts.FontMetrics;
59 import org.apache.fop.fonts.FontTriplet;
60 import org.apache.fop.fonts.FontUtil;
61 import org.apache.fop.fonts.Typeface;
62 import org.apache.fop.fonts.base14.Courier;
63 import org.apache.fop.fonts.base14.Helvetica;
64 import org.apache.fop.fonts.base14.TimesRoman;
65 import org.apache.fop.image.FopImage;
66 import org.apache.fop.image.ImageFactory;
67 import org.apache.fop.image.TIFFImage;
68 import org.apache.fop.image.XMLImage;
69 import org.apache.fop.render.AbstractPathOrientedRenderer;
70 import org.apache.fop.render.Graphics2DAdapter;
71 import org.apache.fop.render.RendererContext;
72 import org.apache.fop.render.afp.extensions.AFPElementMapping;
73 import org.apache.fop.render.afp.extensions.AFPPageSetup;
74 import org.apache.fop.render.afp.fonts.AFPFontInfo;
75 import org.apache.fop.render.afp.fonts.AFPFont;
76 import org.apache.fop.render.afp.fonts.CharacterSet;
77 import org.apache.fop.render.afp.fonts.FopCharacterSet;
78 import org.apache.fop.render.afp.fonts.OutlineFont;
79 import org.apache.fop.render.afp.fonts.RasterFont;
80 import org.apache.fop.render.afp.modca.AFPConstants;
81 import org.apache.fop.render.afp.modca.AFPDataStream;
82 import org.apache.fop.render.afp.modca.ImageObject;
83 import org.apache.fop.render.afp.modca.PageObject;
84 import org.w3c.dom.Document JavaDoc;
85
86
87 /**
88  * This is an implementation of a FOP Renderer that renders areas to AFP.
89  * <p>
90  * A renderer is primarily designed to convert a given area tree into the output
91  * document format. It should be able to produce pages and fill the pages with
92  * the text and graphical content. Usually the output is sent to an output
93  * stream. Some output formats may support extra information that is not
94  * available from the area tree or depends on the destination of the document.
95  * Each renderer is given an area tree to render to its output format. The area
96  * tree is simply a representation of the pages and the placement of text and
97  * graphical objects on those pages.
98  * </p>
99  * <p>
100  * The renderer will be given each page as it is ready and an output stream to
101  * write the data out. All pages are supplied in the order they appear in the
102  * document. In order to save memory it is possble to render the pages out of
103  * order. Any page that is not ready to be rendered is setup by the renderer
104  * first so that it can reserve a space or reference for when the page is ready
105  * to be rendered.The renderer is responsible for managing the output format and
106  * associated data and flow.
107  * </p>
108  * <p>
109  * Each renderer is totally responsible for its output format. Because font
110  * metrics (and therefore layout) are obtained in two different ways depending
111  * on the renderer, the renderer actually sets up the fonts being used. The font
112  * metrics are used during the layout process to determine the size of
113  * characters.
114  * </p>
115  * <p>
116  * The render context is used by handlers. It contains information about the
117  * current state of the renderer, such as the page, the position, and any other
118  * miscellanous objects that are required to draw into the page.
119  * </p>
120  * <p>
121  * A renderer is created by implementing the Renderer interface. However, the
122  * AbstractRenderer does most of what is needed, including iterating through the
123  * tree parts, so it is this that is extended. This means that this object only
124  * need to implement the basic functionality such as text, images, and lines.
125  * AbstractRenderer's methods can easily be overridden to handle things in a
126  * different way or do some extra processing.
127  * </p>
128  * <p>
129  * The relevent AreaTree structures that will need to be rendered are Page,
130  * Viewport, Region, Span, Block, Line, Inline. A renderer implementation
131  * renders each individual page, clips and aligns child areas to a viewport,
132  * handle all types of inline area, text, image etc and draws various lines and
133  * rectangles.
134  * </p>
135  *
136  * Note: There are specific extensions that have been added to the
137  * FO. They are specific to their location within the FO and have to be
138  * processed accordingly (ie. at the start or end of the page).
139  *
140  */

141 public class AFPRenderer extends AbstractPathOrientedRenderer {
142
143     /**
144      * AFP factor for a 240 resolution = 72000/240 = 300
145      */

146     private static final int DPI_CONVERSION_FACTOR_240 = 300;
147
148     /**
149      * The afp data stream object responsible for generating afp data
150      */

151     private AFPDataStream _afpDataStream = null;
152
153     /**
154      * The map of afp root extensions
155      */

156     private HashMap JavaDoc _rootExtensionMap = null;
157
158     /**
159      * The map of page segments
160      */

161     private HashMap JavaDoc _pageSegmentsMap = null;
162
163     /**
164      * The fonts on the current page
165      */

166     private HashMap JavaDoc _currentPageFonts = null;
167
168     /**
169      * The current color object
170      */

171     private Color JavaDoc _currentColor = null;
172
173     /**
174      * The page font number counter, used to determine the next font reference
175      */

176     private int _pageFontCounter = 0;
177
178     /**
179      * The current font family
180      */

181     private String JavaDoc _currentFontFamily = "";
182
183     /**
184      * The current font size
185      */

186     private int _currentFontSize = 0;
187
188     /**
189      * The Options to be set on the AFPRenderer
190      */

191     private Map JavaDoc _afpOptions = null;
192
193     /**
194      * The page width
195      */

196     private int _pageWidth = 0;
197
198     /**
199      * The page height
200      */

201     private int _pageHeight = 0;
202
203     /**
204      * The current page sequence id
205      */

206     private String JavaDoc _pageSequenceId = null;
207
208     /**
209      * The portrait rotation
210      */

211     private int _portraitRotation = 0;
212
213     /**
214      * The landscape rotation
215      */

216     private int _landscapeRotation = 270;
217
218     /**
219      * The line cache, avoids drawing duplicate lines in tables.
220      */

221     private HashSet JavaDoc _lineCache = null;
222
223     /**
224      * The current x position for line drawing
225      */

226     private float _x;
227
228     /**
229      * The current y position for line drawing
230      */

231     private float _y;
232
233     /**
234      * The map of saved incomplete pages
235      */

236     private Map JavaDoc _pages = null;
237
238     /**
239      * Flag to the set the output object type for images
240      */

241     private boolean colorImages = false;
242
243     /**
244      * Default value for image depth
245      */

246     private int bitsPerPixel = 8;
247
248     /**
249      * Constructor for AFPRenderer.
250      */

251     public AFPRenderer() {
252         super();
253     }
254
255     /**
256      * Set up the font info
257      *
258      * @param inFontInfo font info to set up
259      */

260     public void setupFontInfo(FontInfo inFontInfo) {
261         this.fontInfo = inFontInfo;
262         int num = 1;
263         if (this.fontList != null && this.fontList.size() > 0) {
264             for (Iterator JavaDoc it = this.fontList.iterator(); it.hasNext(); ) {
265                 AFPFontInfo afi = (AFPFontInfo)it.next();
266                 AFPFont bf = (AFPFont)afi.getAFPFont();
267                 for (Iterator JavaDoc it2 = afi.getFontTriplets().iterator(); it2.hasNext(); ) {
268                     FontTriplet ft = (FontTriplet)it2.next();
269                     this.fontInfo.addFontProperties("F" + num, ft.getName()
270                                                     , ft.getStyle(), ft.getWeight());
271                     this.fontInfo.addMetrics("F" + num, bf);
272                     num++;
273                 }
274             }
275         } else {
276             log.warn("No AFP fonts configured - using default setup");
277         }
278         if (this.fontInfo.fontLookup("sans-serif", "normal", 400) == null) {
279             CharacterSet cs = new FopCharacterSet("T1V10500", "Cp500", "CZH200 ", 1, new Helvetica());
280             AFPFont bf = new OutlineFont("Helvetica", cs);
281             this.fontInfo.addFontProperties("F" + num, "sans-serif", "normal", 400);
282             this.fontInfo.addMetrics("F" + num, bf);
283             num++;
284         }
285         if (this.fontInfo.fontLookup("serif", "normal", 400) == null) {
286             CharacterSet cs = new FopCharacterSet("T1V10500", "Cp500", "CZN200 ", 1, new TimesRoman());
287             AFPFont bf = new OutlineFont("Helvetica", cs);
288             this.fontInfo.addFontProperties("F" + num, "serif", "normal", 400);
289             this.fontInfo.addMetrics("F" + num, bf);
290             num++;
291         }
292         if (this.fontInfo.fontLookup("monospace", "normal", 400) == null) {
293             CharacterSet cs = new FopCharacterSet("T1V10500", "Cp500", "CZ4200 ", 1, new Courier());
294             AFPFont bf = new OutlineFont("Helvetica", cs);
295             this.fontInfo.addFontProperties("F" + num, "monospace", "normal", 400);
296             this.fontInfo.addMetrics("F" + num, bf);
297             num++;
298         }
299         if (this.fontInfo.fontLookup("any", "normal", 400) == null) {
300             FontTriplet ft = this.fontInfo.fontLookup("sans-serif", "normal", 400);
301             this.fontInfo.addFontProperties(this.fontInfo.getInternalFontKey(ft), "any", "normal", 400);
302         }
303     }
304
305     /**
306      */

307     private AFPFontInfo buildFont(Configuration fontCfg, String JavaDoc _path)
308         throws ConfigurationException {
309
310         Configuration[] triple = fontCfg.getChildren("font-triplet");
311         List JavaDoc tripleList = new java.util.ArrayList JavaDoc();
312         if (triple.length == 0) {
313             log.error("Mandatory font configuration element '<font-triplet...' is missing");
314             return null;
315         }
316         for (int j = 0; j < triple.length; j++) {
317             int weight = FontUtil.parseCSS2FontWeight(triple[j].getAttribute("weight"));
318             tripleList.add(new FontTriplet(triple[j].getAttribute("name"),
319                                            triple[j].getAttribute("style"),
320                                            weight));
321         }
322
323         //build the fonts
324
Configuration afpFontCfg = fontCfg.getChild("afp-font");
325         if (afpFontCfg == null) {
326             log.error("Mandatory font configuration element '<afp-font...' is missing");
327             return null;
328         }
329         String JavaDoc path = afpFontCfg.getAttribute("path", _path);
330         String JavaDoc type = afpFontCfg.getAttribute("type");
331         if (type == null) {
332             log.error("Mandatory afp-font configuration attribute 'type=' is missing");
333             return null;
334         }
335         String JavaDoc codepage = afpFontCfg.getAttribute("codepage");
336         if (codepage == null) {
337             log.error("Mandatory afp-font configuration attribute 'code=' is missing");
338             return null;
339         }
340         String JavaDoc encoding = afpFontCfg.getAttribute("encoding");
341         if (encoding == null) {
342             log.error("Mandatory afp-font configuration attribute 'encoding=' is missing");
343             return null;
344         }
345
346         if ("raster".equalsIgnoreCase(type)) {
347
348             String JavaDoc name = afpFontCfg.getAttribute("name", "Unknown");
349
350             // Create a new font object
351
RasterFont font = new RasterFont(name);
352
353             Configuration[] rasters = afpFontCfg.getChildren("afp-raster-font");
354             if (rasters.length == 0) {
355                 log.error("Mandatory font configuration elements '<afp-raster-font...' are missing");
356                 return null;
357             }
358             for (int j = 0; j < rasters.length; j++) {
359                 Configuration rasterCfg = rasters[j];
360
361                 String JavaDoc characterset = rasterCfg.getAttribute("characterset");
362                 if (characterset == null) {
363                     log.error("Mandatory afp-raster-font configuration attribute 'characterset=' is missing");
364                     return null;
365                 }
366                 int size = rasterCfg.getAttributeAsInteger("size");
367                 String JavaDoc base14 = rasterCfg.getAttribute("base14-font", null);
368
369                 if (base14 != null) {
370                     try {
371                         Class JavaDoc clazz = Class.forName("org.apache.fop.fonts.base14."
372                             + base14);
373                         try {
374                             Typeface tf = (Typeface)clazz.newInstance();
375                             font.addCharacterSet(size, new FopCharacterSet(
376                                 codepage, encoding, characterset, size, tf));
377                         } catch (Exception JavaDoc ie) {
378                             String JavaDoc msg = "The base 14 font class " + clazz.getName()
379                                 + " could not be instantiated";
380                             log.error(msg);
381                         }
382                     } catch (ClassNotFoundException JavaDoc cnfe) {
383                         String JavaDoc msg = "The base 14 font class for " + characterset
384                             + " could not be found";
385                         log.error(msg);
386                     }
387                 } else {
388                     font.addCharacterSet(size, new CharacterSet(
389                         codepage, encoding, characterset, path));
390                 }
391             }
392             return new AFPFontInfo(font, tripleList);
393
394         } else if ("outline".equalsIgnoreCase(type)) {
395
396             String JavaDoc characterset = afpFontCfg.getAttribute("characterset");
397             if (characterset == null) {
398                 log.error("Mandatory afp-font configuration attribute 'characterset=' is missing");
399                 return null;
400             }
401             String JavaDoc name = afpFontCfg.getAttribute("name", characterset);
402
403             CharacterSet characterSet = null;
404
405             String JavaDoc base14 = afpFontCfg.getAttribute("base14-font", null);
406
407             if (base14 != null) {
408                 try {
409                     Class JavaDoc clazz = Class.forName("org.apache.fop.fonts.base14."
410                         + base14);
411                     try {
412                         Typeface tf = (Typeface)clazz.newInstance();
413                         characterSet = new FopCharacterSet(
414                                 codepage, encoding, characterset, 1, tf);
415                     } catch (Exception JavaDoc ie) {
416                         String JavaDoc msg = "The base 14 font class " + clazz.getName()
417                             + " could not be instantiated";
418                         log.error(msg);
419                     }
420                 } catch (ClassNotFoundException JavaDoc cnfe) {
421                     String JavaDoc msg = "The base 14 font class for " + characterset
422                         + " could not be found";
423                     log.error(msg);
424                 }
425             } else {
426                 characterSet = new CharacterSet(codepage, encoding, characterset, path);
427             }
428             // Create a new font object
429
OutlineFont font = new OutlineFont(name, characterSet);
430             return new AFPFontInfo(font, tripleList);
431         } else {
432             log.error("No or incorrect type attribute");
433         }
434         return null;
435     }
436
437     /**
438      * Builds a list of AFPFontInfo objects for use with the setup() method.
439      * @param cfg Configuration object
440      * @return List the newly created list of fonts
441      * @throws ConfigurationException if something's wrong with the config data
442      */

443     public List JavaDoc buildFontListFromConfiguration(Configuration cfg)
444             throws ConfigurationException {
445         List JavaDoc fontList = new java.util.ArrayList JavaDoc();
446         Configuration[] font = cfg.getChild("fonts").getChildren("font");
447         for (int i = 0; i < font.length; i++) {
448             AFPFontInfo afi = buildFont(font[i], null);
449             if (afi != null) {
450                 if (log.isDebugEnabled()) {
451                     log.debug("Adding font " + afi.getAFPFont().getFontName());
452                     for (int j = 0; j < afi.getFontTriplets().size(); ++j) {
453                         FontTriplet triplet = (FontTriplet) afi.getFontTriplets().get(j);
454                         log.debug("Font triplet "
455                                   + triplet.getName() + ", "
456                                   + triplet.getStyle() + ", "
457                                   + triplet.getWeight());
458                     }
459                 }
460
461                 fontList.add(afi);
462             }
463         }
464         return fontList;
465     }
466
467     /**
468      * Configure the AFP renderer.
469      * Get the configuration to be used for fonts etc.
470      * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
471      */

472     public void configure(Configuration cfg) throws ConfigurationException {
473         //Font configuration
474
this.fontList = buildFontListFromConfiguration(cfg);
475         Configuration images = cfg.getChild("images");
476         if (!"color".equalsIgnoreCase(images.getAttribute("mode", "b+w"))) {
477             bitsPerPixel = images.getAttributeAsInteger("bits-per-pixel", 8);
478             switch (bitsPerPixel) {
479                 case 1:
480                 case 4:
481                 case 8:
482                     break;
483                 default:
484                     log.warn("Invalid bits_per_pixel value, must be 1, 4 or 8.");
485                     bitsPerPixel = 8;
486                     break;
487             }
488         } else {
489             colorImages = true;
490         }
491
492     }
493
494     /**
495      * @see org.apache.fop.render.Renderer#setUserAgent(FOUserAgent)
496      */

497     public void setUserAgent(FOUserAgent agent) {
498         super.setUserAgent(agent);
499     }
500
501     /**
502      * @see org.apache.fop.render.Renderer#startRenderer(java.io.OutputStream)
503      */

504     public void startRenderer(OutputStream JavaDoc outputStream) throws IOException JavaDoc {
505         _currentPageFonts = new HashMap JavaDoc();
506         _currentColor = new Color JavaDoc(255, 255, 255);
507         _afpDataStream = new AFPDataStream();
508         _afpDataStream.setPortraitRotation(_portraitRotation);
509         _afpDataStream.setLandscapeRotation(_landscapeRotation);
510         _afpDataStream.startDocument(outputStream);
511     }
512
513     /**
514      * @see org.apache.fop.render.Renderer#stopRenderer()
515      */

516     public void stopRenderer() throws IOException JavaDoc {
517         _afpDataStream.endDocument();
518     }
519
520     /**
521      * @see org.apache.fop.render.Renderer#supportsOutOfOrder()
522      */

523     public boolean supportsOutOfOrder() {
524         //return false;
525
return true;
526     }
527
528     /**
529      * Prepare a page for rendering. This is called if the renderer supports
530      * out of order rendering. The renderer should prepare the page so that a
531      * page further on in the set of pages can be rendered. The body of the
532      * page should not be rendered. The page will be rendered at a later time
533      * by the call to render page.
534      *
535      * @see org.apache.fop.render.Renderer#preparePage(PageViewport)
536      */

537     public void preparePage(PageViewport page) {
538         // initializeRootExtensions(page);
539

540         _currentFontFamily = "";
541         _currentFontSize = 0;
542         _pageFontCounter = 0;
543         _currentPageFonts.clear();
544         _lineCache = new HashSet JavaDoc();
545
546         Rectangle2D JavaDoc bounds = page.getViewArea();
547
548         _pageWidth = mpts2units(bounds.getWidth());
549         _pageHeight = mpts2units(bounds.getHeight());
550
551         // renderPageGroupExtensions(page);
552

553         _afpDataStream.startPage(_pageWidth, _pageHeight, 0);
554
555         renderPageObjectExtensions(page);
556
557         if (_pages == null) {
558             _pages = new HashMap JavaDoc();
559         }
560         _pages.put(page, _afpDataStream.savePage());
561
562     }
563
564     /**
565      * @see org.apache.fop.render.Renderer#processOffDocumentItem(OffDocumentItem)
566      */

567     public void processOffDocumentItem(OffDocumentItem odi) {
568         // TODO
569
}
570
571     /** @see org.apache.fop.render.Renderer#getGraphics2DAdapter() */
572     public Graphics2DAdapter getGraphics2DAdapter() {
573         return new AFPGraphics2DAdapter();
574     }
575
576     /**
577      * @see org.apache.fop.render.AbstractRenderer#startVParea(CTM, Rectangle2D)
578      */

579     public void startVParea(CTM ctm, Rectangle2D JavaDoc clippingRect) {
580         // dummy not used
581
}
582
583     /**
584      * @see org.apache.fop.render.AbstractRenderer#endVParea()
585      */

586     public void endVParea() {
587         // dummy not used
588
}
589
590     /**
591      * Renders a region viewport. <p>
592      *
593      * The region may clip the area and it establishes a position from where
594      * the region is placed.</p>
595      *
596      * @param port The region viewport to be rendered
597      */

598     public void renderRegionViewport(RegionViewport port) {
599         if (port != null) {
600             Rectangle2D JavaDoc view = port.getViewArea();
601             // The CTM will transform coordinates relative to
602
// this region-reference area into page coords, so
603
// set origin for the region to 0,0.
604
currentBPPosition = 0;
605             currentIPPosition = 0;
606
607             RegionReference regionReference = port.getRegionReference();
608             handleRegionTraits(port);
609
610             /*
611             _afpDataStream.startOverlay(mpts2units(view.getX())
612                 , mpts2units(view.getY())
613                 , mpts2units(view.getWidth())
614                 , mpts2units(view.getHeight())
615                 , rotation);
616              */

617
618             pushViewPortPos(new ViewPortPos(view, regionReference.getCTM()));
619
620             if (regionReference.getRegionClass() == FO_REGION_BODY) {
621                 renderBodyRegion((BodyRegion) regionReference);
622             } else {
623                 renderRegion(regionReference);
624             }
625             /*
626             _afpDataStream.endOverlay();
627              */

628             popViewPortPos();
629         }
630     }
631
632     /**
633      * @see org.apache.fop.render.AbstractRenderer#renderBlockViewport(BlockViewport, List)
634      */

635     protected void renderBlockViewport(BlockViewport bv, List JavaDoc children) {
636         // clip and position viewport if necessary
637

638         // save positions
639
int saveIP = currentIPPosition;
640         int saveBP = currentBPPosition;
641         //String saveFontName = currentFontName;
642

643         CTM ctm = bv.getCTM();
644         int borderPaddingStart = bv.getBorderAndPaddingWidthStart();
645         int borderPaddingBefore = bv.getBorderAndPaddingWidthBefore();
646         float x, y;
647         x = (float)(bv.getXOffset() + containingIPPosition) / 1000f;
648         y = (float)(bv.getYOffset() + containingBPPosition) / 1000f;
649         //This is the content-rect
650
float width = (float)bv.getIPD() / 1000f;
651         float height = (float)bv.getBPD() / 1000f;
652
653
654         if (bv.getPositioning() == Block.ABSOLUTE
655                 || bv.getPositioning() == Block.FIXED) {
656
657             currentIPPosition = bv.getXOffset();
658             currentBPPosition = bv.getYOffset();
659
660             //For FIXED, we need to break out of the current viewports to the
661
//one established by the page. We save the state stack for restoration
662
//after the block-container has been painted. See below.
663
List JavaDoc breakOutList = null;
664             if (bv.getPositioning() == Block.FIXED) {
665                 breakOutList = breakOutOfStateStack();
666             }
667
668             CTM tempctm = new CTM(containingIPPosition, containingBPPosition);
669             ctm = tempctm.multiply(ctm);
670
671             //Adjust for spaces (from margin or indirectly by start-indent etc.
672
x += bv.getSpaceStart() / 1000f;
673             currentIPPosition += bv.getSpaceStart();
674
675             y += bv.getSpaceBefore() / 1000f;
676             currentBPPosition += bv.getSpaceBefore();
677
678             float bpwidth = (borderPaddingStart + bv.getBorderAndPaddingWidthEnd()) / 1000f;
679             float bpheight = (borderPaddingBefore + bv.getBorderAndPaddingWidthAfter()) / 1000f;
680
681             drawBackAndBorders(bv, x, y, width + bpwidth, height + bpheight);
682
683             //Now adjust for border/padding
684
currentIPPosition += borderPaddingStart;
685             currentBPPosition += borderPaddingBefore;
686
687             Rectangle2D JavaDoc clippingRect = null;
688             clippingRect = new Rectangle JavaDoc(currentIPPosition, currentBPPosition,
689                     bv.getIPD(), bv.getBPD());
690
691             // startVParea(ctm, clippingRect);
692
pushViewPortPos(new ViewPortPos(clippingRect, ctm));
693             currentIPPosition = 0;
694             currentBPPosition = 0;
695             renderBlocks(bv, children);
696             //endVParea();
697
popViewPortPos();
698
699             if (breakOutList != null) {
700                 restoreStateStackAfterBreakOut(breakOutList);
701             }
702
703             currentIPPosition = saveIP;
704             currentBPPosition = saveBP;
705         } else {
706
707             currentBPPosition += bv.getSpaceBefore();
708
709             //borders and background in the old coordinate system
710
handleBlockTraits(bv);
711
712             //Advance to start of content area
713
currentIPPosition += bv.getStartIndent();
714
715             CTM tempctm = new CTM(containingIPPosition, currentBPPosition);
716             ctm = tempctm.multiply(ctm);
717
718             //Now adjust for border/padding
719
currentBPPosition += borderPaddingBefore;
720
721             Rectangle2D JavaDoc clippingRect = null;
722             clippingRect = new Rectangle JavaDoc(currentIPPosition, currentBPPosition,
723                     bv.getIPD(), bv.getBPD());
724
725             //startVParea(ctm, clippingRect);
726
pushViewPortPos(new ViewPortPos(clippingRect, ctm));
727
728             currentIPPosition = 0;
729             currentBPPosition = 0;
730             renderBlocks(bv, children);
731             //endVParea();
732
popViewPortPos();
733
734             currentIPPosition = saveIP;
735             currentBPPosition = saveBP;
736
737             currentBPPosition += (int)(bv.getAllocBPD());
738         }
739         //currentFontName = saveFontName;
740
}
741
742     /**
743      * @see org.apache.fop.render.Renderer#renderPage(PageViewport)
744      */

745     public void renderPage(PageViewport page) {
746
747         // initializeRootExtensions(page);
748

749         _currentFontFamily = "";
750         _currentFontSize = 0;
751         _pageFontCounter = 0;
752         _currentPageFonts.clear();
753         _lineCache = new HashSet JavaDoc();
754
755         Rectangle2D JavaDoc bounds = page.getViewArea();
756
757         _pageWidth = mpts2units(bounds.getWidth());
758         _pageHeight = mpts2units(bounds.getHeight());
759
760         if (_pages != null && _pages.containsKey(page)) {
761
762             _afpDataStream.restorePage((PageObject)_pages.remove(page));
763
764         } else {
765             // renderPageGroupExtensions(page);
766

767             _afpDataStream.startPage(_pageWidth, _pageHeight, 0);
768
769             renderPageObjectExtensions(page);
770
771         }
772
773         pushViewPortPos(new ViewPortPos());
774
775         renderPageAreas(page.getPage());
776
777         Iterator JavaDoc i = _currentPageFonts.values().iterator();
778         while (i.hasNext()) {
779             AFPFontAttributes afpFontAttributes = (AFPFontAttributes) i.next();
780
781             _afpDataStream.createFont(
782                 afpFontAttributes.getFontReference(),
783                 afpFontAttributes.getFont(),
784                 afpFontAttributes.getPointSize());
785
786         }
787
788         try {
789             _afpDataStream.endPage();
790         } catch (IOException JavaDoc ioex) {
791             // TODO What shall we do?
792
}
793
794         popViewPortPos();
795
796     }
797
798     /**
799      * Clip using the current path.
800      * @see org.apache.fop.render.AbstractPathOrientedRenderer#clip
801      */

802     public void clip() {
803         // TODO
804
}
805
806     /**
807      * Clip using a rectangular area.
808      * @see org.apache.fop.render.AbstractPathOrientedRenderer#clipRect(float, float, float, float)
809      */

810     public void clipRect(float x, float y, float width, float height) {
811         // TODO
812
}
813
814     /**
815      * Moves the current point to (x, y), omitting any connecting line segment.
816      * @see org.apache.fop.render.AbstractPathOrientedRenderer#moveTo(float, float)
817      */

818     public void moveTo(float x, float y) {
819         // TODO
820
}
821
822     /**
823      * Appends a straight line segment from the current point to (x, y). The
824      * new current point is (x, y).
825      * @see org.apache.fop.render.AbstractPathOrientedRenderer#lineTo(float, float)
826      */

827     public void lineTo(float x, float y) {
828         // TODO
829
}
830
831     /**
832      * Closes the current subpath by appending a straight line segment from
833      * the current point to the starting point of the subpath.
834      * @see org.apache.fop.render.AbstractPathOrientedRenderer#closePath
835      */

836     public void closePath() {
837         // TODO
838
}
839
840     /**
841      * Fill a rectangular area.
842      * @see org.apache.fop.render.AbstractPathOrientedRenderer#fillRect(float, float, float, float)
843      */

844     public void fillRect(float x, float y, float width, float height) {
845         /*
846         _afpDataStream.createShading(
847             pts2units(x),
848             pts2units(y),
849             pts2units(width),
850             pts2units(height),
851             _currentColor.getRed(),
852             _currentColor.getGreen(),
853             _currentColor.getBlue());
854          */

855         _afpDataStream.createLine(
856             pts2units(x),
857             pts2units(y),
858             pts2units(x + width),
859             pts2units(y),
860             pts2units(height),
861             _currentColor);
862     }
863
864     /**
865      * Draw a border segment of an XSL-FO style border.
866      * @see org.apache.fop.render.AbstractPathOrientedRenderer#drawBorderLine(float, float, float, float,
867      * boolean, boolean, int, Color)
868      */

869     public void drawBorderLine(float x1, float y1, float x2, float y2,
870             boolean horz, boolean startOrBefore, int style, Color JavaDoc col) {
871         float w = x2 - x1;
872         float h = y2 - y1;
873         if ((w < 0) || (h < 0)) {
874             log.error("Negative extent received. Border won't be painted.");
875             return;
876         }
877         switch (style) {
878             case Constants.EN_DOUBLE:
879                 if (horz) {
880                     float h3 = h / 3;
881                     float ym1 = y1;
882                     float ym2 = ym1 + h3 + h3;
883                     _afpDataStream.createLine(
884                         pts2units(x1),
885                         pts2units(ym1),
886                         pts2units(x2),
887                         pts2units(ym1),
888                         pts2units(h3),
889                         col
890                     );
891                     _afpDataStream.createLine(
892                         pts2units(x1),
893                         pts2units(ym2),
894                         pts2units(x2),
895                         pts2units(ym2),
896                         pts2units(h3),
897                         col
898                     );
899                 } else {
900                     float w3 = w / 3;
901                     float xm1 = x1;
902                     float xm2 = xm1 + w3 + w3;
903                     _afpDataStream.createLine(
904                         pts2units(xm1),
905                         pts2units(y1),
906                         pts2units(xm1),
907                         pts2units(y2),
908                         pts2units(w3),
909                         col
910                     );
911                     _afpDataStream.createLine(
912                         pts2units(xm2),
913                         pts2units(y1),
914                         pts2units(xm2),
915                         pts2units(y2),
916                         pts2units(w3),
917                         col
918                     );
919                 }
920                 break;
921             case Constants.EN_DASHED:
922                 if (horz) {
923                     float w2 = 2 * h;
924                     while (x1 + w2 < x2) {
925                         _afpDataStream.createLine(
926                             pts2units(x1),
927                             pts2units(y1),
928                             pts2units(x1 + w2),
929                             pts2units(y1),
930                             pts2units(h),
931                             col
932                         );
933                         x1 += 2 * w2;
934                     }
935                 } else {
936                     float h2 = 2 * w;
937                     while (y1 + h2 < y2) {
938                         _afpDataStream.createLine(
939                             pts2units(x1),
940                             pts2units(y1),
941                             pts2units(x1),
942                             pts2units(y1 + h2),
943                             pts2units(w),
944                             col
945                         );
946                         y1 += 2 * h2;
947                     }
948                 }
949                 break;
950             case Constants.EN_DOTTED:
951                 if (horz) {
952                     while (x1 + h < x2) {
953                         _afpDataStream.createLine(
954                             pts2units(x1),
955                             pts2units(y1),
956                             pts2units(x1 + h),
957                             pts2units(y1),
958                             pts2units(h),
959                             col
960                         );
961                         x1 += 2 * h;
962                     }
963                 } else {
964                     while (y1 + w < y2) {
965                         _afpDataStream.createLine(
966                             pts2units(x1),
967                             pts2units(y1),
968                             pts2units(x1),
969                             pts2units(y1 + w),
970                             pts2units(w),
971                             col
972                         );
973                         y1 += 2 * w;
974                     }
975                 }
976                 break;
977             case Constants.EN_GROOVE:
978             case Constants.EN_RIDGE:
979             {
980                 float colFactor = (style == EN_GROOVE ? 0.4f : -0.4f);
981                 if (horz) {
982                     Color JavaDoc uppercol = lightenColor(col, -colFactor);
983                     Color JavaDoc lowercol = lightenColor(col, colFactor);
984                     float h3 = h / 3;
985                     float ym1 = y1;
986                     _afpDataStream.createLine(
987                         pts2units(x1),
988                         pts2units(ym1),
989                         pts2units(x2),
990                         pts2units(ym1),
991                         pts2units(h3),
992                         uppercol
993                     );
994                     _afpDataStream.createLine(
995                         pts2units(x1),
996                         pts2units(ym1 + h3),
997                         pts2units(x2),
998                         pts2units(ym1 + h3),
999                         pts2units(h3),
1000                        col
1001                    );
1002                    _afpDataStream.createLine(
1003                        pts2units(x1),
1004                        pts2units(ym1 + h3 + h3),
1005                        pts2units(x2),
1006                        pts2units(ym1 + h3 + h3),
1007                        pts2units(h3),
1008                        lowercol
1009                    );
1010                } else {
1011                    Color JavaDoc leftcol = lightenColor(col, -colFactor);
1012                    Color JavaDoc rightcol = lightenColor(col, colFactor);
1013                    float w3 = w / 3;
1014                    float xm1 = x1 + (w3 / 2);
1015                    _afpDataStream.createLine(
1016                        pts2units(xm1),
1017                        pts2units(y1),
1018                        pts2units(xm1),
1019                        pts2units(y2),
1020                        pts2units(w3),
1021                        leftcol
1022                    );
1023                    _afpDataStream.createLine(
1024                        pts2units(xm1 + w3),
1025                        pts2units(y1),
1026                        pts2units(xm1 + w3),
1027                        pts2units(y2),
1028                        pts2units(w3),
1029                        col
1030                    );
1031                    _afpDataStream.createLine(
1032                        pts2units(xm1 + w3 + w3),
1033                        pts2units(y1),
1034                        pts2units(xm1 + w3 + w3),
1035                        pts2units(y2),
1036                        pts2units(w3),
1037                        rightcol
1038                    );
1039                }
1040                break;
1041            }
1042            case Constants.EN_HIDDEN:
1043                break;
1044            case Constants.EN_INSET:
1045            case Constants.EN_OUTSET:
1046            default:
1047                _afpDataStream.createLine(
1048                    pts2units(x1),
1049                    pts2units(y1),
1050                    pts2units(horz ? x2 : x1),
1051                    pts2units(horz ? y1 : y2),
1052                    pts2units(Math.abs(horz ? (y2 - y1) : (x2 - x1))),
1053                    col
1054                );
1055        }
1056    }
1057
1058    /**
1059     * @see org.apache.fop.render.PrintRenderer#createRendererContext(
1060     * int, int, int, int, java.util.Map)
1061     */

1062    protected RendererContext createRendererContext(int x, int y, int width, int height, Map JavaDoc foreignAttributes) {
1063        RendererContext context;
1064        context = super.createRendererContext(x, y, width, height, foreignAttributes);
1065        context.setProperty(AFPRendererContextConstants.AFP_GRAYSCALE,
1066                new Boolean JavaDoc(!this.colorImages));
1067        return context;
1068    }
1069
1070    /**
1071     * Draw an image at the indicated location.
1072     * @see org.apache.fop.render.AbstractPathOrientedRenderer#drawImage(String, Rectangle2D, Map)
1073     */

1074    public void drawImage(String JavaDoc url, Rectangle2D JavaDoc pos, Map JavaDoc foreignAttributes) {
1075        String JavaDoc name = null;
1076        if (_pageSegmentsMap != null) {
1077            name = (String JavaDoc)_pageSegmentsMap.get(url);
1078        }
1079        if (name != null) {
1080            int x = mpts2units(pos.getX() + currentIPPosition);
1081            int y = mpts2units(pos.getY() + currentBPPosition);
1082            _afpDataStream.createIncludePageSegment(name, x, y);
1083        } else {
1084            url = ImageFactory.getURL(url);
1085            ImageFactory fact = userAgent.getFactory().getImageFactory();
1086            FopImage fopimage = fact.getImage(url, userAgent);
1087            if (fopimage == null) {
1088                return;
1089            }
1090            if (!fopimage.load(FopImage.DIMENSIONS)) {
1091                return;
1092            }
1093            String JavaDoc mime = fopimage.getMimeType();
1094            if ("text/xml".equals(mime) || MimeConstants.MIME_SVG.equals(mime)) {
1095                if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
1096                    return;
1097                }
1098                Document JavaDoc doc = ((XMLImage) fopimage).getDocument();
1099                String JavaDoc ns = ((XMLImage) fopimage).getNameSpace();
1100
1101                renderDocument(doc, ns, pos, foreignAttributes);
1102            } else if (MimeConstants.MIME_EPS.equals(mime)) {
1103                log.warn("EPS images are not supported by this renderer");
1104            /*
1105            } else if (MimeConstants.MIME_JPEG.equals(mime)) {
1106                if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
1107                    return;
1108                }
1109                fact.releaseImage(url, userAgent);
1110
1111                int x = mpts2units(pos.getX() + currentIPPosition);
1112                int y = mpts2units(pos.getY() + currentBPPosition);
1113                int w = mpts2units(pos.getWidth());
1114                int h = mpts2units(pos.getHeight());
1115                ImageObject io = _afpDataStream.getImageObject();
1116                io.setImageViewport(x, y, w, h);
1117                io.setImageParameters(
1118                    (int)(fopimage.getHorizontalResolution() * 10),
1119                    (int)(fopimage.getVerticalResolution() * 10),
1120                    fopimage.getWidth(),
1121                    fopimage.getHeight()
1122                );
1123                io.setImageIDESize((byte)fopimage.getBitsPerPixel());
1124                io.setImageEncoding((byte)0x83);
1125                io.setImageData(fopimage.getRessourceBytes());
1126            */

1127            } else if (MimeConstants.MIME_TIFF.equals(mime)
1128                        && fopimage instanceof TIFFImage) {
1129                TIFFImage tiffImage = (TIFFImage) fopimage;
1130                int x = mpts2units(pos.getX() + currentIPPosition);
1131                int y = mpts2units(pos.getY() + currentBPPosition);
1132                int w = mpts2units(pos.getWidth());
1133                int h = mpts2units(pos.getHeight());
1134                ImageObject io = _afpDataStream.getImageObject(x, y, w, h);
1135                io.setImageParameters(
1136                    (int)(fopimage.getHorizontalResolution() * 10),
1137                    (int)(fopimage.getVerticalResolution() * 10),
1138                    fopimage.getWidth(),
1139                    fopimage.getHeight()
1140                );
1141                if (tiffImage.getStripCount() == 1) {
1142                    int comp = tiffImage.getCompression();
1143                    if (comp == 3) {
1144                        if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
1145                            return;
1146                        }
1147                        io.setImageEncoding((byte)0x81);
1148                        io.setImageData(fopimage.getRessourceBytes());
1149                    } else if (comp == 4) {
1150                        if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
1151                            return;
1152                        }
1153                        io.setImageEncoding((byte)0x82);
1154                        io.setImageData(fopimage.getRessourceBytes());
1155                    } else {
1156                        if (!fopimage.load(FopImage.BITMAP)) {
1157                            return;
1158                        }
1159                        convertToGrayScaleImage(io, fopimage.getBitmaps());
1160                    }
1161                } else {
1162                    if (!fopimage.load(FopImage.BITMAP)) {
1163                        return;
1164                    }
1165                    convertToGrayScaleImage(io, fopimage.getBitmaps());
1166                }
1167            } else {
1168                if (!fopimage.load(FopImage.BITMAP)) {
1169                    return;
1170                }
1171                fact.releaseImage(url, userAgent);
1172
1173                int x = mpts2units(pos.getX() + currentIPPosition);
1174                int y = mpts2units(pos.getY() + currentBPPosition);
1175                int w = mpts2units(pos.getWidth());
1176                int h = mpts2units(pos.getHeight());
1177                ImageObject io = _afpDataStream.getImageObject(x, y, w, h);
1178                io.setImageParameters(
1179                    (int)(fopimage.getHorizontalResolution() * 10),
1180                    (int)(fopimage.getVerticalResolution() * 10),
1181                    fopimage.getWidth(),
1182                    fopimage.getHeight()
1183                );
1184                if (colorImages) {
1185                    io.setImageIDESize((byte)24);
1186                    io.setImageData(fopimage.getBitmaps());
1187                } else {
1188                    convertToGrayScaleImage(io, fopimage.getBitmaps());
1189                }
1190            }
1191        }
1192    }
1193
1194    /**
1195     * Writes a BufferedImage to an OutputStream as raw sRGB bitmaps.
1196     * @param img the BufferedImage
1197     * @param out the OutputStream
1198     * @throws IOException In case of an I/O error.
1199     */

1200    public static void writeImage(BufferedImage JavaDoc img, OutputStream JavaDoc out) throws IOException JavaDoc {
1201        int w = img.getWidth();
1202        int h = img.getHeight();
1203        int[] tmpMap = img.getRGB(0, 0, w, h, null, 0, w);
1204        for (int i = 0; i < h; i++) {
1205            for (int j = 0; j < w; j++) {
1206                int p = tmpMap[i * w + j];
1207                int r = (p >> 16) & 0xFF;
1208                int g = (p >> 8) & 0xFF;
1209                int b = (p) & 0xFF;
1210                out.write((byte)(r & 0xFF));
1211                out.write((byte)(g & 0xFF));
1212                out.write((byte)(b & 0xFF));
1213            }
1214        }
1215    }
1216    
1217    /**
1218     * Draws a BufferedImage to AFP.
1219     * @param bi the BufferedImage
1220     * @param resolution the resolution of the BufferedImage
1221     * @param x the x coordinate (in mpt)
1222     * @param y the y coordinate (in mpt)
1223     * @param w the width of the viewport (in mpt)
1224     * @param h the height of the viewport (in mpt)
1225     */

1226    public void drawBufferedImage(BufferedImage JavaDoc bi, int resolution, int x, int y, int w, int h) {
1227        int afpx = mpts2units(x);
1228        int afpy = mpts2units(y);
1229        int afpw = mpts2units(w);
1230        int afph = mpts2units(h);
1231        ByteArrayOutputStream baout = new ByteArrayOutputStream();
1232        try {
1233            //Serialize image
1234
writeImage(bi, baout);
1235            byte[] buf = baout.toByteArray();
1236            
1237            //Generate image
1238
ImageObject io = _afpDataStream.getImageObject(afpx, afpy, afpw, afph);
1239            io.setImageParameters(
1240                resolution, resolution,
1241                bi.getWidth(),
1242                bi.getHeight()
1243            );
1244            if (colorImages) {
1245                io.setImageIDESize((byte)24);
1246                io.setImageData(buf);
1247            } else {
1248                //TODO Teach it how to handle grayscale BufferedImages directly
1249
//because this is pretty inefficient
1250
convertToGrayScaleImage(io, buf);
1251            }
1252        } catch (IOException JavaDoc ioe) {
1253            log.error("Error while serializing bitmap: " + ioe.getMessage(), ioe);
1254        }
1255    }
1256    
1257    /**
1258     * Establishes a new foreground or fill color.
1259     * @see org.apache.fop.render.AbstractPathOrientedRenderer#updateColor(Color, boolean)
1260     */

1261    public void updateColor(Color JavaDoc col, boolean fill) {
1262        if (fill) {
1263            _currentColor = col;
1264        }
1265    }
1266
1267    /**
1268     * Restores the state stack after a break out.
1269     * @param breakOutList the state stack to restore.
1270     */

1271    public void restoreStateStackAfterBreakOut(List JavaDoc breakOutList) {
1272
1273    }
1274
1275    /**
1276     * Breaks out of the state stack to handle fixed block-containers.
1277     * @return the saved state stack to recreate later
1278     */

1279    public List JavaDoc breakOutOfStateStack() {
1280        return null;
1281    }
1282
1283    /** Saves the graphics state of the rendering engine. */
1284    public void saveGraphicsState() {
1285
1286    }
1287
1288    /** Restores the last graphics state of the rendering engine. */
1289    public void restoreGraphicsState() {
1290
1291    }
1292
1293    /** Indicates the beginning of a text object. */
1294    public void beginTextObject() {
1295
1296    }
1297
1298    /** Indicates the end of a text object. */
1299    public void endTextObject() {
1300
1301    }
1302
1303    /**
1304     * @see org.apache.fop.render.AbstractRenderer#renderImage(Image, Rectangle2D)
1305     */

1306    public void renderImage(Image JavaDoc image, Rectangle2D JavaDoc pos) {
1307        String JavaDoc url = image.getURL();
1308        drawImage(url, pos);
1309    }
1310
1311    /**
1312     * @see org.apache.fop.render.AbstractRenderer#renderText(TextArea)
1313     */

1314    public void renderText(TextArea text) {
1315        renderInlineAreaBackAndBorders(text);
1316
1317        String JavaDoc name = getInternalFontNameForArea(text);
1318        _currentFontSize = ((Integer JavaDoc) text.getTrait(Trait.FONT_SIZE)).intValue();
1319        AFPFont tf = (AFPFont) fontInfo.getFonts().get(name);
1320
1321        Color JavaDoc col = (Color JavaDoc) text.getTrait(Trait.COLOR);
1322
1323        int vsci = mpts2units(tf.getWidth(' ', _currentFontSize) / 1000
1324                                + text.getTextWordSpaceAdjust()
1325                                + text.getTextLetterSpaceAdjust());
1326
1327        // word.getOffset() = only height of text itself
1328
// currentBlockIPPosition: 0 for beginning of line; nonzero
1329
// where previous line area failed to take up entire allocated space
1330
int rx = currentIPPosition + text.getBorderAndPaddingWidthStart();
1331        int bl = currentBPPosition + text.getOffset() + text.getBaselineOffset();
1332
1333        // Set letterSpacing
1334
//float ls = fs.getLetterSpacing() / this.currentFontSize;
1335

1336        String JavaDoc worddata = text.getText();
1337
1338        // Create an AFPFontAttributes object from the current font details
1339
AFPFontAttributes afpFontAttributes =
1340            new AFPFontAttributes(name, tf, _currentFontSize);
1341
1342        if (!_currentPageFonts.containsKey(afpFontAttributes.getFontKey())) {
1343            // Font not found on current page, so add the new one
1344
_pageFontCounter++;
1345            afpFontAttributes.setFontReference(_pageFontCounter);
1346            _currentPageFonts.put(
1347                afpFontAttributes.getFontKey(),
1348                afpFontAttributes);
1349
1350        } else {
1351            // Use the previously stored font attributes
1352
afpFontAttributes =
1353                (AFPFontAttributes) _currentPageFonts.get(
1354                afpFontAttributes.getFontKey());
1355        }
1356
1357        // Try and get the encoding to use for the font
1358
String JavaDoc encoding = null;
1359
1360        try {
1361            encoding = tf.getCharacterSet(_currentFontSize).getEncoding();
1362        } catch (Throwable JavaDoc ex) {
1363            encoding = AFPConstants.EBCIDIC_ENCODING;
1364            log.warn(
1365                "renderText():: Error getting encoding for font "
1366                + " - using default encoding "
1367                + encoding);
1368        }
1369
1370        try {
1371            _afpDataStream.createText(
1372                afpFontAttributes.getFontReference(),
1373                mpts2units(rx),
1374                mpts2units(bl),
1375                col,
1376                vsci,
1377                mpts2units(text.getTextLetterSpaceAdjust()),
1378                worddata.getBytes(encoding));
1379        } catch (UnsupportedEncodingException JavaDoc usee) {
1380            log.error(
1381                "renderText:: Font "
1382                + afpFontAttributes.getFontKey()
1383                + " caused UnsupportedEncodingException");
1384        }
1385
1386        super.renderText(text);
1387
1388        renderTextDecoration(tf, _currentFontSize, text, bl, rx);
1389    }
1390
1391    /**
1392     * @see org.apache.fop.render.AbstractRenderer#renderWord(WordArea)
1393     */

1394    public void renderWord(WordArea word) {
1395        String JavaDoc name = getInternalFontNameForArea(word.getParentArea());
1396        int size = ((Integer JavaDoc) word.getParentArea().getTrait(Trait.FONT_SIZE)).intValue();
1397        AFPFont tf = (AFPFont) fontInfo.getFonts().get(name);
1398
1399        String JavaDoc s = word.getWord();
1400
1401        FontMetrics metrics = fontInfo.getMetricsFor(name);
1402
1403        super.renderWord(word);
1404    }
1405
1406    /**
1407     * @see org.apache.fop.render.AbstractRenderer#renderSpace(SpaceArea)
1408     */

1409    public void renderSpace(SpaceArea space) {
1410        String JavaDoc name = getInternalFontNameForArea(space.getParentArea());
1411        int size = ((Integer JavaDoc) space.getParentArea().getTrait(Trait.FONT_SIZE)).intValue();
1412        AFPFont tf = (AFPFont) fontInfo.getFonts().get(name);
1413
1414        String JavaDoc s = space.getSpace();
1415
1416        FontMetrics metrics = fontInfo.getMetricsFor(name);
1417
1418        super.renderSpace(space);
1419    }
1420
1421    /**
1422     * Render leader area.
1423     * This renders a leader area which is an area with a rule.
1424     * @param area the leader area to render
1425     */

1426    public void renderLeader(Leader area) {
1427        renderInlineAreaBackAndBorders(area);
1428
1429        int style = area.getRuleStyle();
1430        float startx = (currentIPPosition + area.getBorderAndPaddingWidthStart()) / 1000f;
1431        float starty = (currentBPPosition + area.getOffset()) / 1000f;
1432        float endx = (currentIPPosition + area.getBorderAndPaddingWidthStart()
1433                        + area.getIPD()) / 1000f;
1434        float ruleThickness = area.getRuleThickness() / 1000f;
1435        Color JavaDoc col = (Color JavaDoc)area.getTrait(Trait.COLOR);
1436
1437        switch (style) {
1438            case EN_SOLID:
1439            case EN_DASHED:
1440            case EN_DOUBLE:
1441            case EN_DOTTED:
1442            case EN_GROOVE:
1443            case EN_RIDGE:
1444                drawBorderLine(startx, starty, endx, starty + ruleThickness,
1445                        true, true, style, col);
1446                break;
1447            default:
1448                throw new UnsupportedOperationException JavaDoc("rule style not supported");
1449        }
1450        super.renderLeader(area);
1451    }
1452
1453    /**
1454     * @see org.apache.fop.render.Renderer#setOptions(Map)
1455     */

1456    public void setOptions(Map JavaDoc options) {
1457
1458        _afpOptions = options;
1459
1460    }
1461
1462    /**
1463     * Determines the orientation from the string representation, this method
1464     * guarantees to return a value of either 0, 90, 180 or 270.
1465     *
1466     * @return the orientation
1467     */

1468    private int getOrientation(String JavaDoc orientationString) {
1469
1470        int orientation = 0;
1471        if (orientationString != null && orientationString.length() > 0) {
1472            try {
1473                orientation = Integer.parseInt(orientationString);
1474            } catch (NumberFormatException JavaDoc nfe) {
1475                log.error(
1476                    "Cannot use orientation of "
1477                    + orientation
1478                    + " defaulting to zero.");
1479                orientation = 0;
1480            }
1481        } else {
1482            orientation = 0;
1483        }
1484        switch (orientation) {
1485            case 0 :
1486                break;
1487            case 90 :
1488                break;
1489            case 180 :
1490                break;
1491            case 270 :
1492                break;
1493            default :
1494                log.error(
1495                    "Cannot use orientation of "
1496                    + orientation
1497                    + " defaulting to zero.");
1498                orientation = 0;
1499                break;
1500        }
1501
1502        return orientation;
1503
1504    }
1505
1506    /**
1507     * Sets the rotation to be used for portrait pages, valid values are 0
1508     * (default), 90, 180, 270.
1509     *
1510     * @param rotation
1511     * The rotation in degrees.
1512     */

1513    public void setPortraitRotation(int rotation) {
1514
1515        if (rotation == 0
1516            || rotation == 90
1517            || rotation == 180
1518            || rotation == 270) {
1519            _portraitRotation = rotation;
1520        } else {
1521            throw new IllegalArgumentException JavaDoc("The portrait rotation must be one"
1522                + " of the values 0, 90, 180, 270");
1523
1524        }
1525
1526    }
1527
1528    /**
1529     * Sets the rotation to be used for landsacpe pages, valid values are 0, 90,
1530     * 180, 270 (default).
1531     *
1532     * @param rotation
1533     * The rotation in degrees.
1534     */

1535    public void setLandscapeRotation(int rotation) {
1536
1537        if (rotation == 0
1538            || rotation == 90
1539            || rotation == 180
1540            || rotation == 270) {
1541            _landscapeRotation = rotation;
1542        } else {
1543            throw new IllegalArgumentException JavaDoc("The landscape rotation must be one"
1544                + " of the values 0, 90, 180, 270");
1545        }
1546
1547    }
1548
1549    /**
1550     * Get the MIME type of the renderer.
1551     *
1552     * @return The MIME type of the renderer
1553     */

1554    public String JavaDoc getMimeType() {
1555        return MimeConstants.MIME_AFP;
1556    }
1557
1558    /**
1559     * Method to render the page extension.
1560     * <p>
1561     *
1562     * @param page
1563     * the page object
1564     */

1565    private void renderPageObjectExtensions(PageViewport page) {
1566
1567        _pageSegmentsMap = null;
1568        if (page.getExtensionAttachments() != null
1569            && page.getExtensionAttachments().size() > 0) {
1570            //Extract all AFPPageSetup instances from the attachment list on the s-p-m
1571
Iterator JavaDoc i = page.getExtensionAttachments().iterator();
1572            while (i.hasNext()) {
1573                ExtensionAttachment attachment = (ExtensionAttachment)i.next();
1574                if (AFPPageSetup.CATEGORY.equals(attachment.getCategory())) {
1575                    AFPPageSetup aps = (AFPPageSetup)attachment;
1576                    String JavaDoc element = aps.getElementName();
1577                    if (AFPElementMapping.INCLUDE_PAGE_OVERLAY.equals(element)) {
1578                        String JavaDoc overlay = aps.getName();
1579                        if (overlay != null) {
1580                            _afpDataStream.createIncludePageOverlay(overlay);
1581                        }
1582                    } else if (AFPElementMapping.INCLUDE_PAGE_SEGMENT.equals(element)) {
1583                        String JavaDoc name = aps.getName();
1584                        String JavaDoc source = aps.getValue();
1585                        if (_pageSegmentsMap == null) {
1586                            _pageSegmentsMap = new HashMap JavaDoc();
1587                        }
1588                        _pageSegmentsMap.put(source, name);
1589                    } else if (AFPElementMapping.TAG_LOGICAL_ELEMENT.equals(element)) {
1590                        String JavaDoc name = aps.getName();
1591                        String JavaDoc value = aps.getValue();
1592                        if (_pageSegmentsMap == null) {
1593                            _pageSegmentsMap = new HashMap JavaDoc();
1594                        }
1595                        _afpDataStream.createTagLogicalElement(name, value);
1596                    }
1597                }
1598            }
1599        }
1600
1601    }
1602
1603    /**
1604     * Converts FOP mpt measurement to afp measurement units
1605     * @param mpt the millipoints value
1606     */

1607    private int mpts2units(int mpt) {
1608        return mpts2units((double) mpt);
1609    }
1610
1611    /**
1612     * Converts FOP pt measurement to afp measurement units
1613     * @param mpt the millipoints value
1614     */

1615    private int pts2units(float mpt) {
1616        return mpts2units(mpt * 1000d);
1617    }
1618
1619    /**
1620     * Converts FOP mpt measurement to afp measurement units
1621     * @param mpt the millipoints value
1622     */

1623    private int mpts2units(double mpt) {
1624        return (int)Math.round(mpt / DPI_CONVERSION_FACTOR_240);
1625    }
1626
1627    private void convertToGrayScaleImage(ImageObject io, byte raw[]) {
1628        int pixelsPerByte = 8 / bitsPerPixel;
1629        byte bw[] = new byte[raw.length / (3 * pixelsPerByte)];
1630        int k = 0;
1631        for (int i = 0, j = 0; i < raw.length; i += 3, j++) {
1632            if (j == pixelsPerByte) {
1633                j = 0;
1634                k++;
1635            }
1636            // see http://www.jguru.com/faq/view.jsp?EID=221919
1637
double greyVal = 0.212671d * ((int) raw[i] & 0xff)
1638                + 0.715160d * ((int) raw[i + 1] & 0xff)
1639                + 0.072169d * ((int) raw[i + 2] & 0xff);
1640            switch (bitsPerPixel) {
1641                case 1:
1642                    if (greyVal > 128) {
1643                        bw[k] |= (byte)(1 << j);
1644                    }
1645                    break;
1646                case 4:
1647                    greyVal /= 16;
1648                    bw[k] |= (byte)((byte)greyVal << (j * 4));
1649                    break;
1650                case 8:
1651                    bw[k] = (byte)greyVal;
1652                    break;
1653            }
1654        }
1655        io.setImageIDESize((byte)bitsPerPixel);
1656        io.setImageData(bw);
1657    }
1658
1659    private class ViewPortPos {
1660        int x = 0;
1661        int y = 0;
1662        int rot = 0;
1663
1664        ViewPortPos() {
1665        }
1666
1667        ViewPortPos(Rectangle2D JavaDoc view, CTM ctm) {
1668            ViewPortPos currentVP = (ViewPortPos)viewPortPositions.get(viewPortPositions.size() - 1);
1669            int xOrigin;
1670            int yOrigin;
1671            int width;
1672            int height;
1673            switch (currentVP.rot) {
1674                case 90:
1675                    width = mpts2units(view.getHeight());
1676                    height = mpts2units(view.getWidth());
1677                    xOrigin = _pageWidth - width - mpts2units(view.getY()) - currentVP.y;
1678                    yOrigin = mpts2units(view.getX()) + currentVP.x;
1679                    break;
1680                case 180:
1681                    width = mpts2units(view.getWidth());
1682                    height = mpts2units(view.getHeight());
1683                    xOrigin = _pageWidth - width - mpts2units(view.getX()) - currentVP.x;
1684                    yOrigin = _pageHeight - height - mpts2units(view.getY()) - currentVP.y;
1685                    break;
1686                case 270:
1687                    width = mpts2units(view.getHeight());
1688                    height = mpts2units(view.getWidth());
1689                    xOrigin = mpts2units(view.getY()) + currentVP.y;
1690                    yOrigin = _pageHeight - height - mpts2units(view.getX()) - currentVP.x;
1691                    break;
1692                default:
1693                    xOrigin = mpts2units(view.getX()) + currentVP.x;
1694                    yOrigin = mpts2units(view.getY()) + currentVP.y;
1695                    width = mpts2units(view.getWidth());
1696                    height = mpts2units(view.getHeight());
1697                    break;
1698            }
1699            this.rot = currentVP.rot;
1700            double ctmf[] = ctm.toArray();
1701            if (ctmf[0] == 0.0d && ctmf[1] == -1.0d && ctmf[2] == 1.0d && ctmf[3] == 0.d) {
1702                this.rot += 270;
1703            } else if (ctmf[0] == -1.0d && ctmf[1] == 0.0d && ctmf[2] == 0.0d && ctmf[3] == -1.0d) {
1704                this.rot += 180;
1705            } else if (ctmf[0] == 0.0d && ctmf[1] == 1.0d && ctmf[2] == -1.0d && ctmf[3] == 0.0d) {
1706                this.rot += 90;
1707            }
1708            this.rot %= 360;
1709            switch (this.rot) {
1710                /*
1711                case 0:
1712                    this.x = mpts2units(view.getX()) + x;
1713                    this.y = mpts2units(view.getY()) + y;
1714                    break;
1715                case 90:
1716                    this.x = mpts2units(view.getY()) + y;
1717                    this.y = _pageWidth - mpts2units(view.getX() + view.getWidth()) - x;
1718                    break;
1719                case 180:
1720                    this.x = _pageWidth - mpts2units(view.getX() + view.getWidth()) - x;
1721                    this.y = _pageHeight - mpts2units(view.getY() + view.getHeight()) - y;
1722                    break;
1723                case 270:
1724                    this.x = _pageHeight - mpts2units(view.getY() + view.getHeight()) - y;
1725                    this.y = mpts2units(view.getX()) + x;
1726                    break;
1727                 */

1728                case 0:
1729                    this.x = xOrigin;
1730                    this.y = yOrigin;
1731                    break;
1732                case 90:
1733                    this.x = yOrigin;
1734                    this.y = _pageWidth - width - xOrigin;
1735                    break;
1736                case 180:
1737                    this.x = _pageWidth - width - xOrigin;
1738                    this.y = _pageHeight - height - yOrigin;
1739                    break;
1740                case 270:
1741                    this.x = _pageHeight - height - yOrigin;
1742                    this.y = xOrigin;
1743                    break;
1744            }
1745        }
1746
1747        public String JavaDoc toString() {
1748            return "x:" + x + " y:" + y + " rot:" + rot;
1749        }
1750
1751    }
1752
1753    private List JavaDoc viewPortPositions = new ArrayList JavaDoc();
1754
1755    private void pushViewPortPos(ViewPortPos vpp) {
1756        viewPortPositions.add(vpp);
1757        _afpDataStream.setOffsets(vpp.x, vpp.y, vpp.rot);
1758    }
1759
1760    private void popViewPortPos() {
1761        viewPortPositions.remove(viewPortPositions.size() - 1);
1762        if (viewPortPositions.size() > 0) {
1763            ViewPortPos vpp = (ViewPortPos)viewPortPositions.get(viewPortPositions.size() - 1);
1764            _afpDataStream.setOffsets(vpp.x, vpp.y, vpp.rot);
1765        }
1766    }
1767
1768}
1769
1770
Popular Tags