KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > infoglue > deliver > util > graphics > AdvancedImageRenderer


1 /* ===============================================================================
2 *
3 * Part of the InfoGlue Content Management Platform (www.infoglue.org)
4 *
5 * ===============================================================================
6 *
7 * Copyright (C)
8 *
9 * This program is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License version 2, as published by the
11 * Free Software Foundation. See the file LICENSE.html for more information.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY, including the implied warranty of MERCHANTABILITY or FITNESS
15 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc. / 59 Temple
19 * Place, Suite 330 / Boston, MA 02111-1307 / USA.
20 *
21 * ===============================================================================
22 */

23
24 package org.infoglue.deliver.util.graphics;
25
26 import java.awt.Color JavaDoc;
27 import java.awt.Font JavaDoc;
28 import java.awt.Graphics2D JavaDoc;
29 import java.awt.RenderingHints JavaDoc;
30 import java.awt.font.FontRenderContext JavaDoc;
31 import java.awt.font.LineBreakMeasurer JavaDoc;
32 import java.awt.font.TextAttribute JavaDoc;
33 import java.awt.font.TextLayout JavaDoc;
34 import java.awt.geom.Point2D JavaDoc;
35 import java.awt.image.BufferedImage JavaDoc;
36 import java.io.File JavaDoc;
37 import java.io.IOException JavaDoc;
38 import java.io.InputStream JavaDoc;
39 import java.lang.reflect.Method JavaDoc;
40 import java.net.URL JavaDoc;
41 import java.net.URLConnection JavaDoc;
42 import java.text.AttributedCharacterIterator JavaDoc;
43 import java.text.AttributedString JavaDoc;
44 import java.util.HashMap JavaDoc;
45 import java.util.Iterator JavaDoc;
46 import java.util.Map JavaDoc;
47
48 import javax.imageio.ImageIO JavaDoc;
49
50 import org.apache.log4j.Logger;
51 import org.infoglue.cms.util.CmsPropertyHandler;
52
53 /**
54  * Renders images and saves them.
55  * @author Per Jonsson - per.jonsson@it-huset.se
56  *
57  * @version 1.1 fixed reading properties from file, some optimizations and added a imageFileFormat,
58  */

59 public class AdvancedImageRenderer
60 {
61     private static final long serialVersionUID = -1377395059993980530L;
62
63     private final static Logger logger = Logger.getLogger( AdvancedImageRenderer.class.getName() );
64
65     // type of image, colordepth etc.
66
private int imageType = BufferedImage.TYPE_4BYTE_ABGR;
67
68     // An template image to get the right rendering attributes for the font renderer
69
// don't change this in runtime.
70
private static BufferedImage JavaDoc templateImage = null;
71
72     // the rendered image
73
private BufferedImage JavaDoc renderedImage = null;
74
75     // Fontname
76
private String JavaDoc fontName = "Dialog";
77
78     // style of font
79
private int fontStyle = Font.PLAIN;
80
81     // size of font
82
private int fontSize = 18;
83
84     // font to render
85
private Font JavaDoc font = null;
86
87     // font color
88
private Color JavaDoc fgColor = new Color JavaDoc( 0, 0, 0, 255 ); // black
89

90     //background color
91
private Color JavaDoc bgColor = new Color JavaDoc( 255, 255, 255, 255 ); // white
92

93     // width of the rendered image, maxwidth if used with trimedges
94
private int renderWidth = 200;
95
96     // the textalign
97
private int align = 0; // 0 = left, 1 = right , 2 = center
98

99     // top padding in pixels
100
private int padTop = 4;
101
102     // bottom padding in pixels
103
private int padBottom = 4;
104
105     // left padding in pixels
106
private int padLeft = 4;
107
108     // right padding in pixels
109
private int padRight = 4;
110
111     // maximum number of textrows
112
private int maxRows = 20;
113     
114     // default imageFormatName
115
private String JavaDoc imageFormatName = "png";
116
117     // 0 = notrim, 1 = left, 2 = right, 3 = left and right
118
private int trimEdges = 0;
119
120     // an url to for the background
121
private String JavaDoc backgroundImageUrl = null;
122
123     // just for caching
124
private BufferedImage JavaDoc backgroundImage = null;
125
126     // 0 = no, 1 = horizontal, 2 = vertical, 3 = both
127
private int tileBackgroundImage = 0;
128
129     
130     private static Map JavaDoc renderHints = null;
131
132     // cached map of the methods
133
private static Map JavaDoc methodMap = null;
134     
135     // if config is read from propertyfile these are stored here.
136
private static Map JavaDoc defaultConfigMap = new HashMap JavaDoc();
137
138     /**
139      * Creates a new instance of tne NewImageRenderer and reads in properties
140      * from the property file if exists. The propertieas must have the suffix of
141      * "rendertext" ie. rendertext.fontname.
142      */

143     public AdvancedImageRenderer()
144     {
145         // precalc some setters for faster seach
146
if ( methodMap == null )
147         {
148             methodMap = new HashMap JavaDoc();
149             Method JavaDoc[] methods = this.getClass().getDeclaredMethods();
150             String JavaDoc name = null;
151             for ( int i = 0; i < methods.length; i++ )
152             {
153                 name = methods[ i ].getName().toLowerCase();
154                 if ( name.startsWith( "set" ) && methods[ i ].getParameterTypes().length == 1 )
155                 {
156                     name = name.substring( "set".length() );
157                     methodMap.put( name , methods[ i ] );
158                     // Add the default config from properties to the defaultConfig map if exists.
159
String JavaDoc propVal = CmsPropertyHandler.getProperty( "rendertext." + name );
160                     if ( propVal != null && propVal.trim().length() > 0 )
161                     {
162                         //this.setAttribute( name, propVal.toLowerCase() );
163
defaultConfigMap.put( name, propVal.toLowerCase() );
164                     }
165                 }
166             }
167             logger.debug( defaultConfigMap );
168         }
169         if ( renderHints == null )
170         {
171             renderHints = new HashMap JavaDoc();
172             renderHints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );
173             renderHints.put( RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY );
174             renderHints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
175             renderHints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
176             renderHints.put( RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY );
177             renderHints.put( RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE );
178         }
179     }
180
181     /**
182      * Write the rendered image to a file.
183      * @param file the file of the image to create.
184      * @return true if success, false if error
185      */

186     public boolean writeImage( File JavaDoc file )
187     {
188         boolean success = false;
189         try
190         {
191             success = ImageIO.write( this.renderedImage, this.imageFormatName , file );
192         }
193         catch ( Exception JavaDoc e )
194         {
195             logger.error( "Couldn't write Image file : " + file, e );
196         }
197         return success;
198     }
199
200     /**
201      * Renders a text returnes the rendered picture.
202      * @param text The text to be rendered.
203      * @return an rendered image
204      */

205     public BufferedImage JavaDoc renderImage( CharSequence JavaDoc text, Map JavaDoc renderAttributes )
206     {
207         AttributedString JavaDoc attributedString = new AttributedString JavaDoc( text.toString() );
208         return this.renderImage( attributedString, renderAttributes );
209     }
210
211     /**
212      * Renders a text returnes the rendered picture.
213      * @param attributedString an attributed string, to enable multicolored or
214      * similar texts.
215      * @return an rendered image
216      */

217     public BufferedImage JavaDoc renderImage( AttributedString JavaDoc attributedString, Map JavaDoc renderAttributes )
218     {
219         // Copy the defaultconfig and merge with the supplied render attributes.
220
Map JavaDoc tempMap = new HashMap JavaDoc( defaultConfigMap );
221         if ( renderAttributes != null )
222         {
223             tempMap.putAll( renderAttributes );
224         }
225         renderAttributes = tempMap;
226         if ( renderAttributes != null && renderAttributes.size() > 0 )
227         {
228             Iterator JavaDoc keyIter = renderAttributes.entrySet().iterator();
229             while ( keyIter.hasNext() )
230             {
231                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc)keyIter.next();
232                 String JavaDoc key = entry.getKey().toString().trim().toLowerCase();
233                 if ( hasAttribute( key ) && entry.getValue() != null )
234                 {
235                     setAttribute( key, entry.getValue().toString() );
236                 }
237             }
238         }
239         // Set TemplateImage
240
if ( templateImage == null )
241         {
242             templateImage = new BufferedImage JavaDoc( 8, 8, imageType );
243         }
244
245         font = new Font JavaDoc( fontName, fontStyle, fontSize );
246
247         // renderWidth = getSize().width; // temp when testing
248

249         float wrappingWidth = renderWidth - ( padLeft + padRight );
250         
251         if ( wrappingWidth < 0 )
252         {
253             throw new IllegalArgumentException JavaDoc( "The renderwidth (" + renderWidth + ") is lesser than the total padding ("
254                     + ( padLeft + padRight ) + "), modify your settings.");
255         }
256
257         Graphics2D JavaDoc g2d = templateImage.createGraphics();
258         g2d.setRenderingHints( renderHints );
259
260         attributedString.addAttribute( TextAttribute.FONT, font );
261         attributedString.addAttribute( TextAttribute.FOREGROUND, fgColor );
262
263         FontRenderContext JavaDoc context = g2d.getFontRenderContext();
264
265         AttributedCharacterIterator JavaDoc iterator = attributedString.getIterator();
266         LineBreakMeasurer JavaDoc measurer = new LineBreakMeasurer JavaDoc( iterator, context );
267
268         TextLayout JavaDoc layout = null;
269         // precalculating the render pictureheight
270
double renderHeight = padTop + padBottom;
271         int numRows = 0;
272         while ( measurer.getPosition() < iterator.getEndIndex() )
273         {
274             if ( ( layout = measurer.nextLayout( wrappingWidth ) ) == null || ( numRows >= maxRows ) )
275             {
276                 break;
277             }
278             numRows++;
279             renderHeight += layout.getAscent() + layout.getDescent() + layout.getLeading();
280         }
281
282         renderedImage = new BufferedImage JavaDoc( renderWidth, (int)( renderHeight + 0.5 ), templateImage.getType() );
283         Graphics2D JavaDoc img2d = renderedImage.createGraphics();
284         img2d.setRenderingHints( renderHints );
285
286         img2d.setColor( fgColor );
287
288         checkAndSetBackground();
289
290         Point2D.Float JavaDoc pen = new Point2D.Float JavaDoc( padLeft, padTop );
291
292         context = img2d.getFontRenderContext();
293         iterator = attributedString.getIterator();
294         measurer = new LineBreakMeasurer JavaDoc( iterator, context );
295         numRows = 0;
296         while ( measurer.getPosition() < iterator.getEndIndex() )
297         {
298             if ( ( layout = measurer.nextLayout( wrappingWidth ) ) == null || ( numRows >= maxRows ) )
299             {
300                 break;
301             }
302             numRows++;
303             pen.y += layout.getAscent();
304
305             float dx = 0.0f;
306             if ( align == 1 || !layout.isLeftToRight() ) // align right
307
{
308                 dx = ( wrappingWidth - layout.getVisibleAdvance() );
309             }
310             else if ( align == 2 ) // align center
311
{
312                 dx = ( wrappingWidth - layout.getVisibleAdvance() ) / 2;
313             }
314
315             layout.draw( img2d, pen.x + dx, pen.y );
316             pen.y += layout.getDescent() + layout.getLeading();
317         }
318
319         // check and trim
320
renderedImage = horizontalTrim();
321
322         return renderedImage;
323     }
324
325     /**
326      * Checks the attributes and set the correct background.
327      */

328     private void checkAndSetBackground()
329     {
330         Graphics2D JavaDoc img2d = renderedImage.createGraphics();
331         img2d.setBackground( bgColor );
332         img2d.clearRect( 0, 0, renderedImage.getWidth(), renderedImage.getHeight() );
333
334         if ( backgroundImageUrl != null )
335         {
336             try
337             {
338                 if ( backgroundImage == null )
339                 {
340                     URLConnection JavaDoc connection = new URL JavaDoc( backgroundImageUrl ).openConnection();
341                     // Hmm... Only 1.5 these below.
342
//connection.setConnectTimeout( 1000 * 5 ); // set the timeout to 5 seconds.
343
//connection.setReadTimeout( 1000 * 5 ); // set the timeout to 5 seconds.
344
InputStream JavaDoc is = connection.getInputStream();
345                     backgroundImage = ImageIO.read( is );
346                     is.close();
347                 }
348
349                 if ( tileBackgroundImage == 1 && backgroundImage.getWidth() < renderedImage.getWidth() ) // horizontal
350
{
351                     int xnum = (int)( renderedImage.getWidth() / backgroundImage.getWidth() + 0.5 ) + 1;
352                     while ( xnum-- >= 0 )
353                     {
354                         img2d.drawImage( backgroundImage, backgroundImage.getWidth() * xnum, 0, null );
355                     }
356                 }
357                 if ( tileBackgroundImage == 2 && backgroundImage.getHeight() < renderedImage.getHeight() ) // vertical
358
{
359                     int ynum = (int)( renderedImage.getHeight() / backgroundImage.getHeight() + 0.5 ) + 1;
360                     while ( ynum-- >= 0 )
361                     {
362                         img2d.drawImage( backgroundImage, 0, backgroundImage.getHeight() * ynum, null );
363                     }
364                 }
365                 if ( tileBackgroundImage == 3 && backgroundImage.getHeight() < renderedImage.getHeight() ) // vertical
366
{
367                     int ynum = (int)( renderedImage.getHeight() / backgroundImage.getHeight() + 0.5 ) + 1;
368                     while ( ynum-- >= 0 )
369                     {
370                         int xnum = (int)( renderedImage.getWidth() / backgroundImage.getWidth() + 0.5 ) + 1;
371                         while ( xnum-- >= 0 )
372                         {
373                             img2d.drawImage( backgroundImage, backgroundImage.getWidth() * xnum, backgroundImage.getHeight() * ynum, null );
374                         }
375                     }
376                 }
377
378                 if ( tileBackgroundImage == 0 )
379                 {
380                     img2d.drawImage( backgroundImage, 0, 0, null );
381                 }
382             }
383             catch ( IOException JavaDoc ioe )
384             {
385                 logger.error( "Error in reading backgoundImageUrl: " + backgroundImageUrl, ioe );
386             }
387         }
388     }
389
390     /**
391      * Trims the edges of the image.
392      * @return a new trimmed image from the original.
393      */

394     private BufferedImage JavaDoc horizontalTrim()
395     {
396         if ( trimEdges == 0 )
397         {
398             return renderedImage;
399         }
400
401         int imgHeight = renderedImage.getHeight();
402         int imgWidth = renderedImage.getWidth();
403         
404         int bgRGB = bgColor.getRGB(); // get the background color
405

406         // check and trim left side
407
int w = 0;
408         int leftPos = 0, rightPos = 0;
409
410         if ( this.trimEdges == 1 || this.trimEdges == 3 )
411         {
412             loop: for ( w = 0; w < imgWidth; w++ )
413             {
414                 int imgRGB = 0;
415                 for ( int y = 0; y < imgHeight; y++ )
416                 {
417                     imgRGB = renderedImage.getRGB( w, y );
418                     if ( imgRGB != bgRGB )
419                     {
420                         break loop;
421                     }
422                 }
423             }
424
425             leftPos = ( w > 0 ) ? w - 1 : 0;
426             leftPos -= padLeft;
427             // ensure none negative numbers
428
leftPos = ( leftPos <= 0 ) ? 0 : leftPos;
429         }
430
431         // check and trim right side
432
if ( this.trimEdges == 2 || this.trimEdges == 3 )
433         {
434             loop: for ( w = ( imgWidth - 1 ); w >= 0; w-- )
435             {
436                 int imgRGB = 0;
437                 for ( int y = 0; y < imgHeight; y++ )
438                 {
439                     imgRGB = renderedImage.getRGB( w, y );
440                     if ( imgRGB != bgRGB )
441                     {
442                         break loop;
443                     }
444                 }
445             }
446             rightPos = w + 1;
447             rightPos += padRight;
448             // ensure not outside
449
rightPos = ( rightPos > imgWidth ) ? imgWidth - 1 : rightPos;
450         }
451         else
452         {
453             rightPos = imgWidth - 1;
454         }
455         return renderedImage.getSubimage( leftPos, 0, rightPos - leftPos, imgHeight - 1 );
456     }
457
458     /**
459      * Check if this class has a specific attribute, name of attribute is case
460      * insensitive. ie. "fontname", "fontsize", "bgcolor"
461      * @param attributeName name of the attribute to check
462      * @return true if attribute exisist, false otherwise
463      */

464     public boolean hasAttribute( CharSequence JavaDoc attributeName )
465     {
466         return methodMap.containsKey( attributeName.toString().toLowerCase() );
467     }
468
469     /**
470      * Using reflection to set the fields corresponing to the attribute. tries
471      * to convert to the right object. The attribute is caseinsesitive. <br>
472      * If it's a color value it has to be a string in the format
473      * "252:123:133:255" where they are "R:G:B:A" values from 0-255.
474      * @param attribute the field/property to set
475      * @param value the value to set.
476      */

477     public void setAttribute( CharSequence JavaDoc attribute, CharSequence JavaDoc value )
478     {
479         logger.debug("set attribute: " + attribute + " = " + value );
480         Method JavaDoc method = (Method JavaDoc)methodMap.get( attribute );
481         if ( method != null )
482         {
483             try
484             {
485                 Class JavaDoc[] params = method.getParameterTypes();
486                 Class JavaDoc param = params[ 0 ];
487                 if ( param.isPrimitive() )
488                 {
489                     if ( param.getName().equals( "int" ) )
490                     {
491                         method.invoke( this, new Object JavaDoc[]
492                         { new Integer JavaDoc(Integer.parseInt( value.toString() )) } );
493                     }
494                     else if ( param.getName().equals( "float" ) )
495                     {
496                         method.invoke( this, new Object JavaDoc[]
497                         { new Float JavaDoc(Float.parseFloat( value.toString() )) } );
498                     }
499                     else if ( param.getName().equals( "double" ) )
500                     {
501                         method.invoke( this, new Object JavaDoc[]
502                         { new Double JavaDoc(Double.parseDouble( value.toString() )) } );
503                     }
504                     else if ( param.getName().equals( "boolean" ) )
505                     {
506                         method.invoke( this, new Object JavaDoc[]
507                         { new Boolean JavaDoc(Boolean.getBoolean( value.toString() )) } );
508                     }
509                 }
510                 else if ( param.equals( String JavaDoc.class ) )
511                 {
512                     method.invoke( this, new Object JavaDoc[]
513                     { value.toString() } );
514                 }
515                 else if ( param.equals( Color JavaDoc.class ) )
516                 {
517                     method.invoke( this, new Object JavaDoc[]
518                     { ColorHelper.getColor( value.toString() ) } );
519                 }
520
521             }
522             catch ( Exception JavaDoc e )
523             {
524                 logger.warn( "Error in setting properties: " + attribute + " = " + value, e );
525             }
526         }
527         else
528         {
529             logger.warn( "No attribut, named: " + attribute + " found, value =" + value );
530         }
531     }
532
533     /**
534      * @param align The align to set.
535      */

536     public void setAlign( int align )
537     {
538         this.align = align;
539     }
540
541     /**
542      * @param backgroundImage The backgroundImage to set.
543      */

544     public void setBackgroundImage( BufferedImage JavaDoc backgroundImage )
545     {
546         this.backgroundImage = backgroundImage;
547     }
548
549     /**
550      * @param backgroundImageUrl The backgroundImageUrl to set.
551      */

552     public void setBackgroundImageURL( String JavaDoc backgroundImageUrl )
553     {
554         this.backgroundImageUrl = backgroundImageUrl;
555     }
556
557     /**
558      * @param bgColor The bgColor to set.
559      */

560     public void setBgColor( Color JavaDoc bgColor )
561     {
562         this.bgColor = bgColor;
563     }
564
565     /**
566      * @param fgColor The fgColor to set.
567      */

568     public void setFgColor( Color JavaDoc fgColor )
569     {
570         this.fgColor = fgColor;
571     }
572
573     /**
574      * @param fontName The fontName to set.
575      */

576     public void setFontName( String JavaDoc fontName )
577     {
578         this.fontName = fontName;
579     }
580
581     /**
582      * @param fontSize The fontSize to set.
583      */

584     public void setFontSize( int fontSize )
585     {
586         this.fontSize = fontSize;
587     }
588
589     /**
590      * @param fontStyle The fontStyle to set.
591      */

592     public void setFontStyle( int fontStyle )
593     {
594         this.fontStyle = fontStyle;
595     }
596
597     /**
598      * @param imageType The imageType to set.
599      */

600     public void setImageType( int imageType )
601     {
602         this.imageType = imageType;
603     }
604
605     /**
606      * @param padBottom The padBottom to set.
607      */

608     public void setPadBottom( int padBottom )
609     {
610         this.padBottom = padBottom;
611     }
612
613     /**
614      * @param padLeft The padLeft to set.
615      */

616     public void setPadLeft( int padLeft )
617     {
618         this.padLeft = padLeft;
619     }
620
621     /**
622      * @param padRight The padRight to set.
623      */

624     public void setPadRight( int padRight )
625     {
626         this.padRight = padRight;
627     }
628
629     /**
630      * Sets all paddings to the same value.
631      * @param pad The padRight, padLeft, padTop andpadBottom to set.
632      */

633     public void setPad( int pad )
634     {
635         this.padRight = pad;
636         this.padLeft = pad;
637         this.padTop = pad;
638         this.padBottom = pad;
639     }
640
641     /**
642      * @param padTop The padTop to set.
643      */

644     public void setPadTop( int padTop )
645     {
646         this.padTop = padTop;
647     }
648
649     /**
650      * @param renderHints The renderHints to set.
651      */

652     public void setRenderHints( Map JavaDoc renderHints )
653     {
654         this.renderHints = renderHints;
655     }
656
657     /**
658      * @param renderWidth The renderWidth to set.
659      */

660     public void setRenderWidth( int renderWidth )
661     {
662         this.renderWidth = renderWidth;
663     }
664
665     /**
666      * @param templateImage The templateImage to set.
667      */

668     public void setTemplateImage( BufferedImage JavaDoc templateImage )
669     {
670         this.templateImage = templateImage;
671     }
672
673     /**
674      * @param tileBackgroundImage The tileBackgroundImage to set.
675      */

676     public void setTileBackgroundImage( int tileBackgroundImage )
677     {
678         this.tileBackgroundImage = tileBackgroundImage;
679     }
680
681     /**
682      * @param backgroundImageUrl The backgroundImageUrl to set.
683      */

684     public void setBackgroundImageUrl( String JavaDoc backgroundImageUrl )
685     {
686         this.backgroundImageUrl = backgroundImageUrl;
687     }
688
689     /**
690      * @param maxRows The maxRows to set.
691      */

692     public void setMaxRows( int maxRows )
693     {
694         this.maxRows = maxRows;
695     }
696
697     /**
698      * @param trimEdges The trimEdges to set.
699      */

700     public void setTrimEdges( int trimEdges )
701     {
702         this.trimEdges = trimEdges;
703     }
704
705     /**
706      * @param imageFormatName the format of the image. ie ( PNG, GIF);
707      */

708     public void setImageFormatName( String JavaDoc imageFormatName )
709     {
710         this.imageFormatName = imageFormatName;
711     }
712     
713     /**
714      * Get the image format name, default is "png" if none is set.
715      * @return a string with the image format name used by the renderer.
716      */

717     public String JavaDoc getImageFormatName()
718     {
719         return this.imageFormatName;
720     }
721 }
722
Popular Tags