KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > render > java2d > Java2DRenderer


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: Java2DRenderer.java 454018 2006-10-07 21:00:13Z pietsch $ */
19
20 package org.apache.fop.render.java2d;
21
22 // Java
23
import java.awt.BasicStroke JavaDoc;
24 import java.awt.Color JavaDoc;
25 import java.awt.Graphics JavaDoc;
26 import java.awt.Graphics2D JavaDoc;
27 import java.awt.RenderingHints JavaDoc;
28 import java.awt.color.ColorSpace JavaDoc;
29 import java.awt.font.GlyphVector JavaDoc;
30 import java.awt.geom.AffineTransform JavaDoc;
31 import java.awt.geom.GeneralPath JavaDoc;
32 import java.awt.geom.Line2D JavaDoc;
33 import java.awt.geom.Point2D JavaDoc;
34 import java.awt.geom.Rectangle2D JavaDoc;
35 import java.awt.image.BufferedImage JavaDoc;
36 import java.awt.image.ColorModel JavaDoc;
37 import java.awt.image.ComponentColorModel JavaDoc;
38 import java.awt.image.DataBuffer JavaDoc;
39 import java.awt.image.DataBufferByte JavaDoc;
40 import java.awt.image.PixelInterleavedSampleModel JavaDoc;
41 import java.awt.image.Raster JavaDoc;
42 import java.awt.image.SampleModel JavaDoc;
43 import java.awt.image.WritableRaster JavaDoc;
44 import java.awt.print.PageFormat JavaDoc;
45 import java.awt.print.Printable JavaDoc;
46 import java.awt.print.PrinterException JavaDoc;
47 import java.io.IOException JavaDoc;
48 import java.io.OutputStream JavaDoc;
49 import java.util.Iterator JavaDoc;
50 import java.util.List JavaDoc;
51 import java.util.Map JavaDoc;
52 import java.util.Stack JavaDoc;
53
54 import org.w3c.dom.Document JavaDoc;
55
56 import org.apache.avalon.framework.configuration.Configuration;
57 import org.apache.avalon.framework.configuration.ConfigurationException;
58 import org.apache.fop.apps.FOPException;
59 import org.apache.fop.apps.FOUserAgent;
60 import org.apache.fop.area.CTM;
61 import org.apache.fop.area.PageViewport;
62 import org.apache.fop.area.Trait;
63 import org.apache.fop.area.inline.Image;
64 import org.apache.fop.area.inline.InlineArea;
65 import org.apache.fop.area.inline.Leader;
66 import org.apache.fop.area.inline.SpaceArea;
67 import org.apache.fop.area.inline.TextArea;
68 import org.apache.fop.area.inline.WordArea;
69 import org.apache.fop.fo.Constants;
70 import org.apache.fop.fonts.Font;
71 import org.apache.fop.fonts.FontInfo;
72 import org.apache.fop.fonts.Typeface;
73 import org.apache.fop.image.FopImage;
74 import org.apache.fop.image.ImageFactory;
75 import org.apache.fop.image.XMLImage;
76 import org.apache.fop.pdf.PDFAMode;
77 import org.apache.fop.render.AbstractPathOrientedRenderer;
78 import org.apache.fop.render.Graphics2DAdapter;
79 import org.apache.fop.render.RendererContext;
80 import org.apache.fop.render.pdf.CTMHelper;
81 import org.apache.fop.util.CharUtilities;
82
83 /**
84  * The <code>Java2DRenderer</code> class provides the abstract technical
85  * foundation for all rendering with the Java2D API. Renderers like
86  * <code>AWTRenderer</code> subclass it and provide the concrete output paths.
87  * <p>
88  * A lot of the logic is performed by <code>AbstractRenderer</code>. The
89  * class-variables <code>currentIPPosition</code> and
90  * <code>currentBPPosition</code> hold the position of the currently rendered
91  * area.
92  * <p>
93  * <code>Java2DGraphicsState state</code> holds the <code>Graphics2D</code>,
94  * which is used along the whole rendering. <code>state</code> also acts as a
95  * stack (<code>state.push()</code> and <code>state.pop()</code>).
96  * <p>
97  * The rendering process is basically always the same:
98  * <p>
99  * <code>void renderXXXXX(Area area) {
100  * //calculate the currentPosition
101  * state.updateFont(name, size, null);
102  * state.updateColor(ct, false, null);
103  * state.getGraph.draw(new Shape(args));
104  * }</code>
105  *
106  */

107 public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implements Printable JavaDoc {
108
109     /** Rendering Options key for the controlling the transparent page background option. */
110     public static final String JavaDoc JAVA2D_TRANSPARENT_PAGE_BACKGROUND = "transparent-page-background";
111
112     /** The scale factor for the image size, values: ]0 ; 1] */
113     protected double scaleFactor = 1;
114
115     /** The page width in pixels */
116     protected int pageWidth = 0;
117
118     /** The page height in pixels */
119     protected int pageHeight = 0;
120
121     /** List of Viewports */
122     protected List JavaDoc pageViewportList = new java.util.ArrayList JavaDoc();
123
124     /** The 0-based current page number */
125     private int currentPageNumber = 0;
126
127     /** The 0-based total number of rendered pages */
128     private int numberOfPages;
129
130     /** true if antialiasing is set */
131     protected boolean antialiasing = true;
132
133     /** true if qualityRendering is set */
134     protected boolean qualityRendering = true;
135
136     /** false: paints a non-transparent white background, true: for a transparent background */
137     protected boolean transparentPageBackground = false;
138     
139     /** The current state, holds a Graphics2D and its context */
140     protected Java2DGraphicsState state;
141     
142     private Stack JavaDoc stateStack = new Stack JavaDoc();
143
144     /** true if the renderer has finished rendering all the pages */
145     private boolean renderingDone;
146
147     private GeneralPath JavaDoc currentPath = null;
148     
149     /** Default constructor */
150     public Java2DRenderer() {
151     }
152
153     /**
154      * @see org.apache.fop.render.AbstractRenderer#configure(
155      * org.apache.avalon.framework.configuration.Configuration)
156      */

157     public void configure(Configuration cfg) throws ConfigurationException {
158         super.configure(cfg);
159
160         String JavaDoc s = cfg.getChild(JAVA2D_TRANSPARENT_PAGE_BACKGROUND, true).getValue(null);
161         if (s != null) {
162             this.transparentPageBackground = "true".equalsIgnoreCase(s);
163         }
164     }
165
166     /**
167      * @see org.apache.fop.render.Renderer#setUserAgent(org.apache.fop.apps.FOUserAgent)
168      */

169     public void setUserAgent(FOUserAgent foUserAgent) {
170         super.setUserAgent(foUserAgent);
171         userAgent.setRendererOverride(this); // for document regeneration
172

173         String JavaDoc s = (String JavaDoc)userAgent.getRendererOptions().get(JAVA2D_TRANSPARENT_PAGE_BACKGROUND);
174         if (s != null) {
175             this.transparentPageBackground = "true".equalsIgnoreCase(s);
176         }
177     }
178
179     /** @return the FOUserAgent */
180     public FOUserAgent getUserAgent() {
181         return userAgent;
182     }
183
184     /**
185      * @see org.apache.fop.render.Renderer#setupFontInfo(org.apache.fop.fonts.FontInfo)
186      */

187     public void setupFontInfo(FontInfo inFontInfo) {
188         //Don't call super.setupFontInfo() here! Java2D needs a special font setup
189
// create a temp Image to test font metrics on
190
fontInfo = inFontInfo;
191         BufferedImage JavaDoc fontImage = new BufferedImage JavaDoc(100, 100,
192                 BufferedImage.TYPE_INT_RGB);
193         Graphics2D JavaDoc g = fontImage.createGraphics();
194         //The next line is important to get accurate font metrics!
195
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
196                 RenderingHints.VALUE_FRACTIONALMETRICS_ON);
197         FontSetup.setup(fontInfo, g);
198     }
199
200     /** @see org.apache.fop.render.Renderer#getGraphics2DAdapter() */
201     public Graphics2DAdapter getGraphics2DAdapter() {
202         return new Java2DGraphics2DAdapter(state);
203     }
204
205     /**
206      * Sets the new scale factor.
207      * @param newScaleFactor ]0 ; 1]
208      */

209     public void setScaleFactor(double newScaleFactor) {
210         scaleFactor = newScaleFactor;
211     }
212
213     /** @return the scale factor */
214     public double getScaleFactor() {
215         return scaleFactor;
216     }
217
218     /** @see org.apache.fop.render.Renderer#startRenderer(java.io.OutputStream) */
219     public void startRenderer(OutputStream JavaDoc out) throws IOException JavaDoc {
220         // do nothing by default
221
}
222
223     /** @see org.apache.fop.render.Renderer#stopRenderer() */
224     public void stopRenderer() throws IOException JavaDoc {
225         log.debug("Java2DRenderer stopped");
226         renderingDone = true;
227         numberOfPages = currentPageNumber;
228         // TODO set all vars to null for gc
229
if (numberOfPages == 0) {
230             new FOPException("No page could be rendered");
231         }
232     }
233
234     /** @return true if the renderer is not currently processing */
235     public boolean isRenderingDone() {
236         return this.renderingDone;
237     }
238     
239     /**
240      * @return The 0-based current page number
241      */

242     public int getCurrentPageNumber() {
243         return currentPageNumber;
244     }
245
246     /**
247      * @param c the 0-based current page number
248      */

249     public void setCurrentPageNumber(int c) {
250         this.currentPageNumber = c;
251     }
252
253     /**
254      * @return The 0-based total number of rendered pages
255      */

256     public int getNumberOfPages() {
257             return numberOfPages;
258     }
259
260     /**
261      * Clears the ViewportList.
262      * Used if the document is reloaded.
263      */

264     public void clearViewportList() {
265         pageViewportList.clear();
266         setCurrentPageNumber(0);
267     }
268
269     /**
270      * This method override only stores the PageViewport in a List. No actual
271      * rendering is performed here. A renderer override renderPage() to get the
272      * freshly produced PageViewport, and rendere them on the fly (producing the
273      * desired BufferedImages by calling getPageImage(), which lazily starts the
274      * rendering process).
275      *
276      * @param pageViewport the <code>PageViewport</code> object supplied by
277      * the Area Tree
278      * @throws IOException In case of an I/O error
279      * @see org.apache.fop.render.Renderer
280      */

281     public void renderPage(PageViewport pageViewport) throws IOException JavaDoc {
282         // TODO clone?
283
pageViewportList.add(pageViewport.clone());
284         currentPageNumber++;
285     }
286
287     /**
288      * Generates a desired page from the renderer's page viewport list.
289      *
290      * @param pageViewport the PageViewport to be rendered
291      * @return the <code>java.awt.image.BufferedImage</code> corresponding to
292      * the page or null if the page doesn't exist.
293      */

294     public BufferedImage JavaDoc getPageImage(PageViewport pageViewport) {
295
296         this.currentPageViewport = pageViewport;
297         try {
298             Rectangle2D JavaDoc bounds = pageViewport.getViewArea();
299             pageWidth = (int) Math.round(bounds.getWidth() / 1000f);
300             pageHeight = (int) Math.round(bounds.getHeight() / 1000f);
301
302             log.info(
303                     "Rendering Page " + pageViewport.getPageNumberString()
304                             + " (pageWidth " + pageWidth + ", pageHeight "
305                             + pageHeight + ")");
306
307             double scaleX = scaleFactor
308                 * (25.4 / FOUserAgent.DEFAULT_TARGET_RESOLUTION)
309                 / userAgent.getTargetPixelUnitToMillimeter();
310             double scaleY = scaleFactor
311                 * (25.4 / FOUserAgent.DEFAULT_TARGET_RESOLUTION)
312                 / userAgent.getTargetPixelUnitToMillimeter();
313             int bitmapWidth = (int) ((pageWidth * scaleX) + 0.5);
314             int bitmapHeight = (int) ((pageHeight * scaleY) + 0.5);
315                     
316             
317             BufferedImage JavaDoc currentPageImage = getBufferedImage(bitmapWidth, bitmapHeight);
318             
319             Graphics2D JavaDoc graphics = currentPageImage.createGraphics();
320             graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
321                     RenderingHints.VALUE_FRACTIONALMETRICS_ON);
322             if (antialiasing) {
323                 graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
324                         RenderingHints.VALUE_ANTIALIAS_ON);
325                 graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
326                         RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
327             }
328             if (qualityRendering) {
329                 graphics.setRenderingHint(RenderingHints.KEY_RENDERING,
330                         RenderingHints.VALUE_RENDER_QUALITY);
331             }
332
333             // transform page based on scale factor supplied
334
AffineTransform JavaDoc at = graphics.getTransform();
335             at.scale(scaleX, scaleY);
336             graphics.setTransform(at);
337
338             // draw page frame
339
if (!transparentPageBackground) {
340                 graphics.setColor(Color.white);
341                 graphics.fillRect(0, 0, pageWidth, pageHeight);
342             }
343             graphics.setColor(Color.black);
344             graphics.drawRect(-1, -1, pageWidth + 2, pageHeight + 2);
345             graphics.drawLine(pageWidth + 2, 0, pageWidth + 2, pageHeight + 2);
346             graphics.drawLine(pageWidth + 3, 1, pageWidth + 3, pageHeight + 3);
347             graphics.drawLine(0, pageHeight + 2, pageWidth + 2, pageHeight + 2);
348             graphics.drawLine(1, pageHeight + 3, pageWidth + 3, pageHeight + 3);
349
350             state = new Java2DGraphicsState(graphics, this.fontInfo, at);
351             try {
352                 // reset the current Positions
353
currentBPPosition = 0;
354                 currentIPPosition = 0;
355
356                 // this toggles the rendering of all areas
357
renderPageAreas(pageViewport.getPage());
358             } finally {
359                 state = null;
360             }
361
362             return currentPageImage;
363         } finally {
364             this.currentPageViewport = null;
365         }
366     }
367
368     /**
369      * Returns a specific <code>BufferedImage</code> to paint a page image on. This method can
370      * be overridden in subclasses to produce different image formats (ex. grayscale or b/w).
371      * @param bitmapWidth width of the image in pixels
372      * @param bitmapHeight heigth of the image in pixels
373      * @return the newly created BufferedImage
374      */

375     protected BufferedImage JavaDoc getBufferedImage(int bitmapWidth, int bitmapHeight) {
376        return new BufferedImage JavaDoc(
377                 bitmapWidth, bitmapHeight, BufferedImage.TYPE_INT_ARGB);
378     }
379     
380     /**
381      * Returns a page viewport.
382      * @param pageNum the page number
383      * @return the requested PageViewport instance
384      * @exception FOPException If the page is out of range.
385      */

386     public PageViewport getPageViewport(int pageNum) throws FOPException {
387         if (pageNum < 0 || pageNum >= pageViewportList.size()) {
388             throw new FOPException("Requested page number is out of range: " + pageNum
389                      + "; only " + pageViewportList.size()
390                      + " page(s) available.");
391         }
392         return (PageViewport) pageViewportList.get(pageNum);
393     }
394
395     /**
396      * Generates a desired page from the renderer's page viewport list.
397      *
398      * @param pageNum the 0-based page number to generate
399      * @return the <code>java.awt.image.BufferedImage</code> corresponding to
400      * the page or null if the page doesn't exist.
401      * @throws FOPException If there's a problem preparing the page image
402      */

403     public BufferedImage JavaDoc getPageImage(int pageNum) throws FOPException {
404         return getPageImage(getPageViewport(pageNum));
405     }
406
407     /** Saves the graphics state of the rendering engine. */
408     protected void saveGraphicsState() {
409         // push (and save) the current graphics state
410
stateStack.push(state);
411         state = new Java2DGraphicsState(state);
412     }
413
414     /** Restores the last graphics state of the rendering engine. */
415     protected void restoreGraphicsState() {
416         state.dispose();
417         state = (Java2DGraphicsState)stateStack.pop();
418     }
419     
420     /**
421      * @see org.apache.fop.render.AbstractRenderer#startVParea(CTM, Rectangle2D)
422      */

423     protected void startVParea(CTM ctm, Rectangle2D JavaDoc clippingRect) {
424
425         saveGraphicsState();
426
427         if (clippingRect != null) {
428             clipRect((float)clippingRect.getX() / 1000f,
429                     (float)clippingRect.getY() / 1000f,
430                     (float)clippingRect.getWidth() / 1000f,
431                     (float)clippingRect.getHeight() / 1000f);
432         }
433
434         // Set the given CTM in the graphics state
435
//state.setTransform(new AffineTransform(CTMHelper.toPDFArray(ctm)));
436
state.transform(new AffineTransform JavaDoc(CTMHelper.toPDFArray(ctm)));
437     }
438
439     /**
440      * @see org.apache.fop.render.AbstractRenderer#endVParea()
441      */

442     protected void endVParea() {
443         restoreGraphicsState();
444     }
445
446     /**
447      * @see org.apache.fop.render.AbstractPathOrientedRenderer#breakOutOfStateStack()
448      */

449     protected List JavaDoc breakOutOfStateStack() {
450         log.debug("Block.FIXED --> break out");
451         List JavaDoc breakOutList;
452         breakOutList = new java.util.ArrayList JavaDoc();
453         while (!stateStack.isEmpty()) {
454             breakOutList.add(0, state);
455             //We only pop, we don't dispose, because we can use the instances again later
456
state = (Java2DGraphicsState)stateStack.pop();
457         }
458         return breakOutList;
459     }
460
461     /**
462      * @see org.apache.fop.render.AbstractPathOrientedRenderer#restoreStateStackAfterBreakOut(
463      * java.util.List)
464      */

465     protected void restoreStateStackAfterBreakOut(List JavaDoc breakOutList) {
466         log.debug("Block.FIXED --> restoring context after break-out");
467         
468         Iterator JavaDoc i = breakOutList.iterator();
469         while (i.hasNext()) {
470             Java2DGraphicsState s = (Java2DGraphicsState)i.next();
471             stateStack.push(state);
472             state = s;
473         }
474     }
475
476     /**
477      * @see org.apache.fop.render.AbstractPathOrientedRenderer#updateColor(Color, boolean)
478      */

479     protected void updateColor(Color JavaDoc col, boolean fill) {
480         state.updateColor(col);
481     }
482
483     /**
484      * @see org.apache.fop.render.AbstractPathOrientedRenderer#clip()
485      */

486     protected void clip() {
487         if (currentPath == null) {
488             throw new IllegalStateException JavaDoc("No current path available!");
489         }
490         state.updateClip(currentPath);
491         currentPath = null;
492     }
493
494     /**
495      * @see org.apache.fop.render.AbstractPathOrientedRenderer#closePath()
496      */

497     protected void closePath() {
498         currentPath.closePath();
499     }
500
501     /**
502      * @see org.apache.fop.render.AbstractPathOrientedRenderer#lineTo(float, float)
503      */

504     protected void lineTo(float x, float y) {
505         if (currentPath == null) {
506             currentPath = new GeneralPath JavaDoc();
507         }
508         currentPath.lineTo(x, y);
509     }
510
511     /**
512      * @see org.apache.fop.render.AbstractPathOrientedRenderer#moveTo(float, float)
513      */

514     protected void moveTo(float x, float y) {
515         if (currentPath == null) {
516             currentPath = new GeneralPath JavaDoc();
517         }
518         currentPath.moveTo(x, y);
519     }
520
521     /**
522      * @see org.apache.fop.render.AbstractPathOrientedRenderer#clipRect(float, float, float, float)
523      */

524     protected void clipRect(float x, float y, float width, float height) {
525         state.updateClip(new Rectangle2D.Float JavaDoc(x, y, width, height));
526     }
527
528     /**
529      * @see org.apache.fop.render.AbstractPathOrientedRenderer#fillRect(float, float, float, float)
530      */

531     protected void fillRect(float x, float y, float width, float height) {
532         state.getGraph().fill(new Rectangle2D.Float JavaDoc(x, y, width, height));
533     }
534     
535     /**
536      * @see org.apache.fop.render.AbstractPathOrientedRenderer#drawBorderLine(float, float, float, float, boolean, boolean, int, Color)
537      */

538     protected void drawBorderLine(float x1, float y1, float x2, float y2,
539             boolean horz, boolean startOrBefore, int style, Color JavaDoc col) {
540         Graphics2D JavaDoc g2d = state.getGraph();
541         drawBorderLine(new Rectangle2D.Float JavaDoc(x1, y1, x2 - x1, y2 - y1),
542                 horz, startOrBefore, style, col, g2d);
543     }
544
545     /**
546      * Draw a border segment of an XSL-FO style border.
547      * @param lineRect the line defined by its bounding rectangle
548      * @param horz true for horizontal border segments, false for vertical border segments
549      * @param startOrBefore true for border segments on the start or before edge,
550      * false for end or after.
551      * @param style the border style (one of Constants.EN_DASHED etc.)
552      * @param col the color for the border segment
553      * @param g2d the Graphics2D instance to paint to
554      */

555     public static void drawBorderLine(Rectangle2D.Float JavaDoc lineRect,
556             boolean horz, boolean startOrBefore, int style, Color JavaDoc col, Graphics2D JavaDoc g2d) {
557         float x1 = lineRect.x;
558         float y1 = lineRect.y;
559         float x2 = x1 + lineRect.width;
560         float y2 = y1 + lineRect.height;
561         float w = lineRect.width;
562         float h = lineRect.height;
563         if ((w < 0) || (h < 0)) {
564             log.error("Negative extent received. Border won't be painted.");
565             return;
566         }
567         switch (style) {
568             case Constants.EN_DASHED:
569                 g2d.setColor(col);
570                 if (horz) {
571                     float unit = Math.abs(2 * h);
572                     int rep = (int)(w / unit);
573                     if (rep % 2 == 0) {
574                         rep++;
575                     }
576                     unit = w / rep;
577                     float ym = y1 + (h / 2);
578                     BasicStroke JavaDoc s = new BasicStroke JavaDoc(h, BasicStroke.CAP_BUTT,
579                             BasicStroke.JOIN_MITER, 10.0f, new float[] {unit}, 0);
580                     g2d.setStroke(s);
581                     g2d.draw(new Line2D.Float JavaDoc(x1, ym, x2, ym));
582                 } else {
583                     float unit = Math.abs(2 * w);
584                     int rep = (int)(h / unit);
585                     if (rep % 2 == 0) {
586                         rep++;
587                     }
588                     unit = h / rep;
589                     float xm = x1 + (w / 2);
590                     BasicStroke JavaDoc s = new BasicStroke JavaDoc(w, BasicStroke.CAP_BUTT,
591                             BasicStroke.JOIN_MITER, 10.0f, new float[] {unit}, 0);
592                     g2d.setStroke(s);
593                     g2d.draw(new Line2D.Float JavaDoc(xm, y1, xm, y2));
594                 }
595                 break;
596             case Constants.EN_DOTTED:
597                 g2d.setColor(col);
598                 if (horz) {
599                     float unit = Math.abs(2 * h);
600                     int rep = (int)(w / unit);
601                     if (rep % 2 == 0) {
602                         rep++;
603                     }
604                     unit = w / rep;
605                     float ym = y1 + (h / 2);
606                     BasicStroke JavaDoc s = new BasicStroke JavaDoc(h, BasicStroke.CAP_ROUND,
607                             BasicStroke.JOIN_MITER, 10.0f, new float[] {0, unit}, 0);
608                     g2d.setStroke(s);
609                     g2d.draw(new Line2D.Float JavaDoc(x1, ym, x2, ym));
610                 } else {
611                     float unit = Math.abs(2 * w);
612                     int rep = (int)(h / unit);
613                     if (rep % 2 == 0) {
614                         rep++;
615                     }
616                     unit = h / rep;
617                     float xm = x1 + (w / 2);
618                     BasicStroke JavaDoc s = new BasicStroke JavaDoc(w, BasicStroke.CAP_ROUND,
619                             BasicStroke.JOIN_MITER, 10.0f, new float[] {0, unit}, 0);
620                     g2d.setStroke(s);
621                     g2d.draw(new Line2D.Float JavaDoc(xm, y1, xm, y2));
622                 }
623                 break;
624             case Constants.EN_DOUBLE:
625                 g2d.setColor(col);
626                 if (horz) {
627                     float h3 = h / 3;
628                     float ym1 = y1 + (h3 / 2);
629                     float ym2 = ym1 + h3 + h3;
630                     BasicStroke JavaDoc s = new BasicStroke JavaDoc(h3);
631                     g2d.setStroke(s);
632                     g2d.draw(new Line2D.Float JavaDoc(x1, ym1, x2, ym1));
633                     g2d.draw(new Line2D.Float JavaDoc(x1, ym2, x2, ym2));
634                 } else {
635                     float w3 = w / 3;
636                     float xm1 = x1 + (w3 / 2);
637                     float xm2 = xm1 + w3 + w3;
638                     BasicStroke JavaDoc s = new BasicStroke JavaDoc(w3);
639                     g2d.setStroke(s);
640                     g2d.draw(new Line2D.Float JavaDoc(xm1, y1, xm1, y2));
641                     g2d.draw(new Line2D.Float JavaDoc(xm2, y1, xm2, y2));
642                 }
643                 break;
644             case Constants.EN_GROOVE:
645             case Constants.EN_RIDGE:
646                 float colFactor = (style == EN_GROOVE ? 0.4f : -0.4f);
647                 if (horz) {
648                     Color JavaDoc uppercol = lightenColor(col, -colFactor);
649                     Color JavaDoc lowercol = lightenColor(col, colFactor);
650                     float h3 = h / 3;
651                     float ym1 = y1 + (h3 / 2);
652                     g2d.setStroke(new BasicStroke JavaDoc(h3));
653                     g2d.setColor(uppercol);
654                     g2d.draw(new Line2D.Float JavaDoc(x1, ym1, x2, ym1));
655                     g2d.setColor(col);
656                     g2d.draw(new Line2D.Float JavaDoc(x1, ym1 + h3, x2, ym1 + h3));
657                     g2d.setColor(lowercol);
658                     g2d.draw(new Line2D.Float JavaDoc(x1, ym1 + h3 + h3, x2, ym1 + h3 + h3));
659                 } else {
660                     Color JavaDoc leftcol = lightenColor(col, -colFactor);
661                     Color JavaDoc rightcol = lightenColor(col, colFactor);
662                     float w3 = w / 3;
663                     float xm1 = x1 + (w3 / 2);
664                     g2d.setStroke(new BasicStroke JavaDoc(w3));
665                     g2d.setColor(leftcol);
666                     g2d.draw(new Line2D.Float JavaDoc(xm1, y1, xm1, y2));
667                     g2d.setColor(col);
668                     g2d.draw(new Line2D.Float JavaDoc(xm1 + w3, y1, xm1 + w3, y2));
669                     g2d.setColor(rightcol);
670                     g2d.draw(new Line2D.Float JavaDoc(xm1 + w3 + w3, y1, xm1 + w3 + w3, y2));
671                 }
672                 break;
673             case Constants.EN_INSET:
674             case Constants.EN_OUTSET:
675                 colFactor = (style == EN_OUTSET ? 0.4f : -0.4f);
676                 if (horz) {
677                     col = lightenColor(col, (startOrBefore ? 1 : -1) * colFactor);
678                     g2d.setStroke(new BasicStroke JavaDoc(h));
679                     float ym1 = y1 + (h / 2);
680                     g2d.setColor(col);
681                     g2d.draw(new Line2D.Float JavaDoc(x1, ym1, x2, ym1));
682                 } else {
683                     col = lightenColor(col, (startOrBefore ? 1 : -1) * colFactor);
684                     float xm1 = x1 + (w / 2);
685                     g2d.setStroke(new BasicStroke JavaDoc(w));
686                     g2d.setColor(col);
687                     g2d.draw(new Line2D.Float JavaDoc(xm1, y1, xm1, y2));
688                 }
689                 break;
690             case Constants.EN_HIDDEN:
691                 break;
692             default:
693                 g2d.setColor(col);
694                 if (horz) {
695                     float ym = y1 + (h / 2);
696                     g2d.setStroke(new BasicStroke JavaDoc(h));
697                     g2d.draw(new Line2D.Float JavaDoc(x1, ym, x2, ym));
698                 } else {
699                     float xm = x1 + (w / 2);
700                     g2d.setStroke(new BasicStroke JavaDoc(w));
701                     g2d.draw(new Line2D.Float JavaDoc(xm, y1, xm, y2));
702                 }
703         }
704     }
705
706     /**
707      * @see org.apache.fop.render.AbstractRenderer#renderText(TextArea)
708      */

709     public void renderText(TextArea text) {
710         renderInlineAreaBackAndBorders(text);
711
712         int rx = currentIPPosition + text.getBorderAndPaddingWidthStart();
713         int bl = currentBPPosition + text.getOffset() + text.getBaselineOffset();
714         int saveIP = currentIPPosition;
715
716         Font font = getFontFromArea(text);
717         state.updateFont(font.getFontName(), font.getFontSize());
718         saveGraphicsState();
719         AffineTransform JavaDoc at = new AffineTransform JavaDoc();
720         at.translate(rx / 1000f, bl / 1000f);
721         state.transform(at);
722         renderText(text, state.getGraph(), font);
723         restoreGraphicsState();
724         
725         currentIPPosition = saveIP + text.getAllocIPD();
726         //super.renderText(text);
727

728         // rendering text decorations
729
Typeface tf = (Typeface) fontInfo.getFonts().get(font.getFontName());
730         int fontsize = text.getTraitAsInteger(Trait.FONT_SIZE);
731         renderTextDecoration(tf, fontsize, text, bl, rx);
732     }
733
734     /**
735      * Renders a TextArea to a Graphics2D instance. Adjust the coordinate system so that the
736      * start of the baseline of the first character is at coordinate (0,0).
737      * @param text the TextArea
738      * @param g2d the Graphics2D to render to
739      * @param font the font to paint with
740      */

741     public static void renderText(TextArea text, Graphics2D JavaDoc g2d, Font font) {
742
743         Color JavaDoc col = (Color JavaDoc) text.getTrait(Trait.COLOR);
744         g2d.setColor(col);
745
746         float textCursor = 0;
747
748         Iterator JavaDoc iter = text.getChildAreas().iterator();
749         while (iter.hasNext()) {
750             InlineArea child = (InlineArea)iter.next();
751             if (child instanceof WordArea) {
752                 WordArea word = (WordArea)child;
753                 String JavaDoc s = word.getWord();
754                 int[] letterAdjust = word.getLetterAdjustArray();
755                 GlyphVector JavaDoc gv = g2d.getFont().createGlyphVector(g2d.getFontRenderContext(), s);
756                 double additionalWidth = 0.0;
757                 if (letterAdjust == null
758                         && text.getTextLetterSpaceAdjust() == 0
759                         && text.getTextWordSpaceAdjust() == 0) {
760                     //nop
761
} else {
762                     int[] offsets = getGlyphOffsets(s, font, text, letterAdjust);
763                     float cursor = 0.0f;
764                     for (int i = 0; i < offsets.length; i++) {
765                         Point2D JavaDoc pt = gv.getGlyphPosition(i);
766                         pt.setLocation(cursor, pt.getY());
767                         gv.setGlyphPosition(i, pt);
768                         cursor += offsets[i] / 1000f;
769                     }
770                     additionalWidth = cursor - gv.getLogicalBounds().getWidth();
771                 }
772                 g2d.drawGlyphVector(gv, textCursor, 0);
773                 textCursor += gv.getLogicalBounds().getWidth() + additionalWidth;
774             } else if (child instanceof SpaceArea) {
775                 SpaceArea space = (SpaceArea)child;
776                 String JavaDoc s = space.getSpace();
777                 char sp = s.charAt(0);
778                 int tws = (space.isAdjustable()
779                         ? text.getTextWordSpaceAdjust()
780                                 + 2 * text.getTextLetterSpaceAdjust()
781                         : 0);
782
783                 textCursor += (font.getCharWidth(sp) + tws) / 1000f;
784             } else {
785                 throw new IllegalStateException JavaDoc("Unsupported child element: " + child);
786             }
787         }
788     }
789     
790     private static int[] getGlyphOffsets(String JavaDoc s, Font font, TextArea text,
791             int[] letterAdjust) {
792         int textLen = s.length();
793         int[] offsets = new int[textLen];
794         for (int i = 0; i < textLen; i++) {
795             final char c = s.charAt(i);
796             final char mapped = font.mapChar(c);
797             int wordSpace;
798
799             if (CharUtilities.isAdjustableSpace(mapped)) {
800                 wordSpace = text.getTextWordSpaceAdjust();
801             } else {
802                 wordSpace = 0;
803             }
804             int cw = font.getWidth(mapped);
805             int ladj = (letterAdjust != null && i < textLen - 1 ? letterAdjust[i + 1] : 0);
806             int tls = (i < textLen - 1 ? text.getTextLetterSpaceAdjust() : 0);
807             offsets[i] = cw + ladj + tls + wordSpace;
808         }
809         return offsets;
810     }
811
812     /**
813      * Render leader area. This renders a leader area which is an area with a
814      * rule.
815      *
816      * @param area the leader area to render
817      */

818     public void renderLeader(Leader area) {
819         renderInlineAreaBackAndBorders(area);
820
821         // TODO leader-length: 25%, 50%, 75%, 100% not working yet
822
// TODO Colors do not work on Leaders yet
823

824         float startx = (currentIPPosition + area.getBorderAndPaddingWidthStart()) / 1000f;
825         float starty = ((currentBPPosition + area.getOffset()) / 1000f);
826         float endx = (currentIPPosition + area.getBorderAndPaddingWidthStart()
827                 + area.getIPD()) / 1000f;
828
829         Color JavaDoc col = (Color JavaDoc) area.getTrait(Trait.COLOR);
830         state.updateColor(col);
831
832         Line2D JavaDoc line = new Line2D.Float JavaDoc();
833         line.setLine(startx, starty, endx, starty);
834         float ruleThickness = area.getRuleThickness() / 1000f;
835
836         int style = area.getRuleStyle();
837         switch (style) {
838         case EN_SOLID:
839         case EN_DASHED:
840         case EN_DOUBLE:
841             drawBorderLine(startx, starty, endx, starty + ruleThickness,
842                     true, true, style, col);
843             break;
844         case EN_DOTTED:
845             //TODO Dots should be shifted to the left by ruleThickness / 2
846
state.updateStroke(ruleThickness, style);
847             float rt2 = ruleThickness / 2f;
848             line.setLine(line.getX1(), line.getY1() + rt2, line.getX2(), line.getY2() + rt2);
849             state.getGraph().draw(line);
850             break;
851         case EN_GROOVE:
852         case EN_RIDGE:
853             float half = area.getRuleThickness() / 2000f;
854
855             state.updateColor(lightenColor(col, 0.6f));
856             moveTo(startx, starty);
857             lineTo(endx, starty);
858             lineTo(endx, starty + 2 * half);
859             lineTo(startx, starty + 2 * half);
860             closePath();
861             state.getGraph().fill(currentPath);
862             currentPath = null;
863             state.updateColor(col);
864             if (style == EN_GROOVE) {
865                 moveTo(startx, starty);
866                 lineTo(endx, starty);
867                 lineTo(endx, starty + half);
868                 lineTo(startx + half, starty + half);
869                 lineTo(startx, starty + 2 * half);
870             } else {
871                 moveTo(endx, starty);
872                 lineTo(endx, starty + 2 * half);
873                 lineTo(startx, starty + 2 * half);
874                 lineTo(startx, starty + half);
875                 lineTo(endx - half, starty + half);
876             }
877             closePath();
878             state.getGraph().fill(currentPath);
879             currentPath = null;
880
881         case EN_NONE:
882             // No rule is drawn
883
break;
884         default:
885         } // end switch
886

887         super.renderLeader(area);
888     }
889
890     /**
891      * @see org.apache.fop.render.AbstractRenderer#renderImage(Image,
892      * Rectangle2D)
893      */

894     public void renderImage(Image JavaDoc image, Rectangle2D JavaDoc pos) {
895         // endTextObject();
896
String JavaDoc url = image.getURL();
897         drawImage(url, pos);
898     }
899
900     /**
901      * @see org.apache.fop.render.AbstractPathOrientedRenderer#drawImage(
902      * java.lang.String, java.awt.geom.Rectangle2D, java.util.Map)
903      */

904     protected void drawImage(String JavaDoc url, Rectangle2D JavaDoc pos, Map JavaDoc foreignAttributes) {
905
906         int x = currentIPPosition + (int)Math.round(pos.getX());
907         int y = currentBPPosition + (int)Math.round(pos.getY());
908         url = ImageFactory.getURL(url);
909
910         ImageFactory fact = userAgent.getFactory().getImageFactory();
911         FopImage fopimage = fact.getImage(url, userAgent);
912
913         if (fopimage == null) {
914             return;
915         }
916         if (!fopimage.load(FopImage.DIMENSIONS)) {
917             return;
918         }
919         int w = fopimage.getWidth();
920         int h = fopimage.getHeight();
921         String JavaDoc mime = fopimage.getMimeType();
922         if ("text/xml".equals(mime)) {
923             if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
924                 return;
925             }
926             Document JavaDoc doc = ((XMLImage) fopimage).getDocument();
927             String JavaDoc ns = ((XMLImage) fopimage).getNameSpace();
928             renderDocument(doc, ns, pos, foreignAttributes);
929
930         } else if ("image/svg+xml".equals(mime)) {
931             if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
932                 return;
933             }
934             Document JavaDoc doc = ((XMLImage) fopimage).getDocument();
935             String JavaDoc ns = ((XMLImage) fopimage).getNameSpace();
936
937             renderDocument(doc, ns, pos, foreignAttributes);
938         } else if ("image/eps".equals(mime)) {
939             log.warn("EPS images are not supported by this renderer");
940         } else {
941             if (!fopimage.load(FopImage.BITMAP)) {
942                 log.warn("Loading of bitmap failed: " + url);
943                 return;
944             }
945
946             byte[] raw = fopimage.getBitmaps();
947
948             // TODO Hardcoded color and sample models, FIX ME!
949
ColorModel JavaDoc cm = new ComponentColorModel JavaDoc(
950                     ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB),
951                     new int[] {8, 8, 8},
952                     false, false,
953                     ColorModel.OPAQUE, DataBuffer.TYPE_BYTE);
954             SampleModel JavaDoc sampleModel = new PixelInterleavedSampleModel JavaDoc(
955                     DataBuffer.TYPE_BYTE, w, h, 3, w * 3, new int[] {0, 1, 2});
956             DataBuffer JavaDoc dbuf = new DataBufferByte JavaDoc(raw, w * h * 3);
957
958             WritableRaster JavaDoc raster = Raster.createWritableRaster(sampleModel,
959                     dbuf, null);
960
961             java.awt.Image JavaDoc awtImage;
962             // Combine the color model and raster into a buffered image
963
awtImage = new BufferedImage JavaDoc(cm, raster, false, null);
964
965             state.getGraph().drawImage(awtImage,
966                     (int)(x / 1000f), (int)(y / 1000f),
967                     (int)(pos.getWidth() / 1000f), (int)(pos.getHeight() / 1000f), null);
968         }
969     }
970
971     /**
972      * @see org.apache.fop.render.PrintRenderer#createRendererContext(
973      * int, int, int, int, java.util.Map)
974      */

975     protected RendererContext createRendererContext(int x, int y, int width, int height,
976             Map JavaDoc foreignAttributes) {
977         RendererContext context = super.createRendererContext(
978                 x, y, width, height, foreignAttributes);
979         context.setProperty(Java2DRendererContextConstants.JAVA2D_STATE, state);
980         return context;
981     }
982
983     /**
984      * @see java.awt.print.Printable#print(java.awt.Graphics,
985      * java.awt.print.PageFormat, int)
986      */

987     public int print(Graphics JavaDoc g, PageFormat JavaDoc pageFormat, int pageIndex)
988             throws PrinterException JavaDoc {
989         if (pageIndex >= getNumberOfPages()) {
990             return NO_SUCH_PAGE;
991         }
992
993         if (state != null) {
994             throw new IllegalStateException JavaDoc("state must be null");
995         }
996         Graphics2D JavaDoc graphics = (Graphics2D JavaDoc) g;
997         try {
998             PageViewport viewport = getPageViewport(pageIndex);
999             AffineTransform JavaDoc at = graphics.getTransform();
1000            state = new Java2DGraphicsState(graphics, this.fontInfo, at);
1001
1002            // reset the current Positions
1003
currentBPPosition = 0;
1004            currentIPPosition = 0;
1005
1006            renderPageAreas(viewport.getPage());
1007            return PAGE_EXISTS;
1008        } catch (FOPException e) {
1009            log.error(e);
1010            return NO_SUCH_PAGE;
1011        } finally {
1012            state = null;
1013        }
1014    }
1015
1016    /** @see org.apache.fop.render.AbstractPathOrientedRenderer#beginTextObject() */
1017    protected void beginTextObject() {
1018        //not necessary in Java2D
1019
}
1020
1021    /** @see org.apache.fop.render.AbstractPathOrientedRenderer#endTextObject() */
1022    protected void endTextObject() {
1023        //not necessary in Java2D
1024
}
1025
1026}
1027
Popular Tags