vhardy 01/10/31 01:54:26 Modified: sources/org/apache/batik/apps/rasterizer Main.java SVGConverter.java sources/org/apache/batik/svggen AbstractImageHandlerEncoder.java DefaultImageHandler.java ErrorConstants.java ImageHandlerBase64Encoder.java ImageHandlerJPEGEncoder.java ImageHandlerPNGEncoder.java SVGGraphics2D.java SVGSyntax.java resources/org/apache/batik/apps/rasterizer/resources Messages.properties test-sources/org/apache/batik/apps/rasterizer MainTest.java SVGConverterTest.java test-sources/org/apache/batik/test AbstractTest.java Added: sources/org/apache/batik/svggen CachedImageHandler.java CachedImageHandlerBase64Encoder.java CachedImageHandlerJPEGEncoder.java CachedImageHandlerPNGEncoder.java CachedImageSVGGraphics2D.java ImageCacher.java Log: Added options to the rasterizer and related tests: - user language - user stylesheet - resolution - quality Revision Changes Path 1.15 +96 -3 xml-batik/sources/org/apache/batik/apps/rasterizer/Main.java Index: Main.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/apps/rasterizer/Main.java,v retrieving revision 1.14 retrieving revision 1.15 diff -u -r1.14 -r1.15 --- Main.java 2001/10/29 19:13:54 1.14 +++ Main.java 2001/10/31 09:54:25 1.15 @@ -46,7 +46,7 @@ * <tt>SVGConverter</tt> which is used to perform the conversion. * * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: Main.java,v 1.14 2001/10/29 19:13:54 vhardy Exp $ + * @version $Id: Main.java,v 1.15 2001/10/31 09:54:25 vhardy Exp $ */ public class Main implements SVGConverterController { /** @@ -363,6 +363,43 @@ = Messages.get("Main.cl.option.validate.description", "No description"); /** + * Option to specify the user language with which SVG + * documents should be processed + */ + public static String CL_OPTION_LANGUAGE + = Messages.get("Main.cl.option.language", "-lang"); + + public static String CL_OPTION_LANGUAGE_DESCRIPTION + = Messages.get("Main.cl.option.language.description", "No description"); + + /** + * Option to specify an addition user stylesheet + */ + public static String CL_OPTION_USER_STYLESHEET + = Messages.get("Main.cl.option.user.stylesheet", "-cssUser"); + + public static String CL_OPTION_USER_STYLESHEET_DESCRIPTION + = Messages.get("Main.cl.option.user.stylesheet.description", "No description"); + + /** + * Option to specify the resolution for the output image + */ + public static String CL_OPTION_DPI + = Messages.get("Main.cl.option.dpi", "-dpi"); + + public static String CL_OPTION_DPI_DESCRIPTION + = Messages.get("Main.cl.option.dpi.description", "No description"); + + /** + * Option to specify the output JPEG quality + */ + public static String CL_OPTION_QUALITY + = Messages.get("Main.cl.option.quality", "-q"); + + public static String CL_OPTION_QUALITY_DESCRIPTION + = Messages.get("Main.cl.option.quality.description", "No description"); + + /** * Static map containing all the option handlers able to analyze the * various options. */ @@ -481,7 +518,7 @@ } public String getOptionDescription(){ - return CL_OPTION_MEDIA_TYPE; + return CL_OPTION_MEDIA_TYPE_DESCRIPTION; } }); @@ -492,8 +529,64 @@ c.setAlternateStylesheet(optionValue); } + public String getOptionDescription(){ + return CL_OPTION_ALTERNATE_STYLESHEET_DESCRIPTION; + } + }); + + optionMap.put(CL_OPTION_USER_STYLESHEET, + new SingleValueOptionHandler(){ + public void handleOption(String optionValue, + SVGConverter c){ + c.setUserStylesheet(optionValue); + } + + public String getOptionDescription(){ + return CL_OPTION_USER_STYLESHEET_DESCRIPTION; + } + }); + + optionMap.put(CL_OPTION_LANGUAGE, + new SingleValueOptionHandler(){ + public void handleOption(String optionValue, + SVGConverter c){ + c.setLanguage(optionValue); + } + + public String getOptionDescription(){ + return CL_OPTION_LANGUAGE_DESCRIPTION; + } + }); + + optionMap.put(CL_OPTION_DPI, + new FloatOptionHandler(){ + public void handleOption(float optionValue, + SVGConverter c){ + if (optionValue <= 0){ + throw new IllegalArgumentException(); + } + + c.setPixelToMillimeter(2.54f/optionValue); + } + + public String getOptionDescription(){ + return CL_OPTION_DPI_DESCRIPTION; + } + }); + + optionMap.put(CL_OPTION_QUALITY, + new FloatOptionHandler(){ + public void handleOption(float optionValue, + SVGConverter c){ + if (optionValue <= 0 || optionValue >= 1){ + throw new IllegalArgumentException(); + } + + c.setQuality(optionValue); + } + public String getOptionDescription(){ - return CL_OPTION_ALTERNATE_STYLESHEET; + return CL_OPTION_QUALITY_DESCRIPTION; } }); 1.5 +70 -1 xml-batik/sources/org/apache/batik/apps/rasterizer/SVGConverter.java Index: SVGConverter.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/apps/rasterizer/SVGConverter.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- SVGConverter.java 2001/10/30 13:59:34 1.4 +++ SVGConverter.java 2001/10/31 09:54:25 1.5 @@ -73,9 +73,14 @@ * <li>mediaType: controls the CSS media, or list of media, for which the * image should be rendered.</li> * <li>alternate: controls the alternate CSS stylesheet to activate, if any.</li> + * <li>language: controls the user language with which the SVG document should + * be converted.</li> + * <li>userStylesheet: defines the user stylesheet to apply to SVG documents + * in addition to other stylesheets referenced by or embedded in the SVG documents.</li> + * <li>pixelToMillimeter: defines the size of a pixel when processing the SVG documents.</li> * </ul> * - * @version $Id: SVGConverter.java,v 1.4 2001/10/30 13:59:34 tkormann Exp $ + * @version $Id: SVGConverter.java,v 1.5 2001/10/31 09:54:25 vhardy Exp $ * @author Henri Ruini * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> */ @@ -208,6 +213,15 @@ /** Output AOI area. */ protected Rectangle2D area = null; + /** Language */ + protected String language = null; + + /** User stylesheet */ + protected String userStylesheet = null; + + /** Pixel to Millimeter */ + protected float pixelToMillimeter = -1f; + /** Validation flag */ protected boolean validate = false; @@ -320,6 +334,44 @@ } /** + * Sets the user language. If the value is null, then the default + * (see {@link org.apache.batik.transcoder.image.ImageTranscoder#getLanguage}) + * is used. + */ + public void setLanguage(String language){ + this.language = language; + } + + public String getLanguage(){ + return language; + } + + /** + * Sets the user stylesheet. May be null. + */ + public void setUserStylesheet(String userStylesheet){ + this.userStylesheet = userStylesheet; + } + + public String getUserStylesheet(){ + return userStylesheet; + } + + /** + * Sets the pixel to millimeter conversion constant. A negative + * value will cause the default value + * (see {@link org.apache.batik.transcoder.image.ImageTranscoder#getPixelToMM}) + * to be used. + */ + public void setPixelToMillimeter(float pixelToMillimeter){ + this.pixelToMillimeter = pixelToMillimeter; + } + + public float getPixelToMillimeter(){ + return pixelToMillimeter; + } + + /** * Sets the <tt>area</tt> as a Rectangle. This value can * be null in which case the whole image will be rendered. If the * area is not null, then only the portion of the image it @@ -643,11 +695,28 @@ map.put(ImageTranscoder.KEY_ALTERNATE_STYLESHEET, alternateStylesheet); } + // Set user stylesheet + if (userStylesheet != null){ + map.put(ImageTranscoder.KEY_USER_STYLESHEET_URI, userStylesheet); + } + + // Set the user language + if (language != null){ + map.put(ImageTranscoder.KEY_LANGUAGE, language); + } + + // Sets the pixel to millimeter ratio + if (pixelToMillimeter > 0){ + map.put(ImageTranscoder.KEY_PIXEL_TO_MM, new Float(pixelToMillimeter)); + } + // Set validation if (validate){ map.put(ImageTranscoder.KEY_XML_PARSER_VALIDATING, new Boolean(validate)); } + + return map; } 1.11 +64 -8 xml-batik/sources/org/apache/batik/svggen/AbstractImageHandlerEncoder.java Index: AbstractImageHandlerEncoder.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/svggen/AbstractImageHandlerEncoder.java,v retrieving revision 1.10 retrieving revision 1.11 diff -u -r1.10 -r1.11 --- AbstractImageHandlerEncoder.java 2001/09/19 12:52:32 1.10 +++ AbstractImageHandlerEncoder.java 2001/10/31 09:54:25 1.11 @@ -17,6 +17,10 @@ import java.awt.geom.AffineTransform; import java.lang.reflect.Method; import java.io.File; +import java.io.OutputStream; +import java.io.FileOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.net.MalformedURLException; import org.w3c.dom.Element; @@ -29,7 +33,7 @@ * of the file created by this handler. * * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: AbstractImageHandlerEncoder.java,v 1.10 2001/09/19 12:52:32 cjolif Exp $ + * @version $Id: AbstractImageHandlerEncoder.java,v 1.11 2001/10/31 09:54:25 vhardy Exp $ * @see org.apache.batik.svggen.SVGGraphics2D * @see org.apache.batik.svggen.ImageHandlerJPEGEncoder * @see org.apache.batik.svggen.ImageHandlerPNGEncoder @@ -66,7 +70,7 @@ createGraphics = clazz.getMethod("createGraphics", paramc); paramo = new Object[1]; } catch (Throwable t) { - // happen only if Batik extensions are not their + // happen only if Batik extensions are not there } finally { initDone = true; } @@ -79,7 +83,7 @@ try { g2d = (Graphics2D)createGraphics.invoke(null, paramo); } catch (Exception e) { - // should not happened + // should not happen } return g2d; } @@ -178,10 +182,43 @@ saveBufferedImageToFile(imageElement, buf, generatorContext); } - private void saveBufferedImageToFile(Element imageElement, - BufferedImage buf, - SVGGeneratorContext generatorContext) + void cacheBufferedImage(Element imageElement, + BufferedImage buf, + SVGGeneratorContext generatorContext) throws SVGGraphics2DIOException { + + ByteArrayOutputStream os; + + if (generatorContext == null) + throw new SVGGraphics2DRuntimeException(ERR_CONTEXT_NULL); + + try { + os = new ByteArrayOutputStream(); + // encode the image in memory + encodeImage(buf, os); + os.close(); + } catch (IOException e) { + // should not happen since we do in-memory processing + throw new SVGGraphics2DIOException(ERR_UNEXPECTED, e); + } + + // ask the cacher for a reference + String imageFileName = imageCacher.lookup(os, + buf.getWidth(), + buf.getHeight(), + generatorContext); + + // set the URL + imageElement.setAttributeNS(XLINK_NAMESPACE_URI, + ATTR_XLINK_HREF, + urlRoot + "/" + imageFileName); + } + + + protected void saveBufferedImageToFile(Element imageElement, + BufferedImage buf, + SVGGeneratorContext generatorContext) + throws SVGGraphics2DIOException { if (generatorContext == null) throw new SVGGraphics2DRuntimeException(ERR_CONTEXT_NULL); @@ -221,9 +258,28 @@ /** * Derived classes should implement this method and encode the input * BufferedImage as needed + */ + public abstract void encodeImage(BufferedImage buf, OutputStream os) + throws IOException; + // NOTE: This breaks super <- sub interface! + // The (BufferedImage, File) signature used to be abstract. + // Instead, it is now the (BufferedImage, OutputStream) signature. + // How serious is this? + + /** + * This method encodes the BufferedImage to a file */ - public abstract void encodeImage(BufferedImage buf, File imageFile) - throws SVGGraphics2DIOException; + public void encodeImage(BufferedImage buf, File imageFile) + throws SVGGraphics2DIOException { + try { + OutputStream os = new FileOutputStream(imageFile); + encodeImage(buf, os); + os.flush(); + os.close(); + } catch (IOException e) { + throw new SVGGraphics2DIOException(ERR_WRITE+imageFile.getName()); + } + } /** * This method creates a BufferedImage of the right size and type 1.9 +209 -2 xml-batik/sources/org/apache/batik/svggen/DefaultImageHandler.java Index: DefaultImageHandler.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/svggen/DefaultImageHandler.java,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- DefaultImageHandler.java 2001/09/19 12:52:32 1.8 +++ DefaultImageHandler.java 2001/10/31 09:54:25 1.9 @@ -11,6 +11,7 @@ import java.awt.Image; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; +import java.awt.geom.AffineTransform; import org.w3c.dom.Element; @@ -20,10 +21,13 @@ * attribute and sets the width and height of the element. * * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: DefaultImageHandler.java,v 1.8 2001/09/19 12:52:32 cjolif Exp $ + * @author <a href="mailto:[EMAIL PROTECTED]">Paul Evenblij</a> + * @version $Id: DefaultImageHandler.java,v 1.9 2001/10/31 09:54:25 vhardy Exp $ * @see org.apache.batik.svggen.SVGGraphics2D */ -public class DefaultImageHandler implements ImageHandler, ErrorConstants { +public class DefaultImageHandler implements ImageHandler, + CachedImageHandler, + ErrorConstants { // duplicate the string here to remove dependencies on // org.apache.batik.dom.util.XLinkSupport static final String XLINK_NAMESPACE_URI = @@ -34,6 +38,10 @@ */ public DefaultImageHandler() {} + /*=================================================================* + * ImageHandler implementations + *=================================================================*/ + /** * The handler should set the xlink:href tag and the width and * height attributes. @@ -159,4 +167,203 @@ imageElement.setAttributeNS(XLINK_NAMESPACE_URI, ATTR_XLINK_HREF, image.toString()); } + + + /*=================================================================* + * CachedImageHandler implementations + *=================================================================*/ + + protected ImageCacher imageCacher; + + /** + * The image cache can be used by subclasses for efficient image storage + */ + public ImageCacher getImageCacher() { + return imageCacher; + } + + void setImageCacher(ImageCacher imageCacher) { + this.imageCacher = imageCacher; + } + + /** + * Creates an Element which can refer to an image. + * Note that no assumptions should be made by the caller about the + * corresponding SVG tag. + */ + public Element createElement(SVGGeneratorContext generatorContext) { + // Create a DOM Element in SVG namespace to refer to an image + Element imageElement = + generatorContext.getDOMFactory().createElementNS( + SVG_NAMESPACE_URI, SVG_IMAGE_TAG); + + return imageElement; + } + + /** + * The handler sets the xlink:href tag and returns a transform + */ + public AffineTransform handleImage(Image image, + Element imageElement, + int x, int y, + int width, int height, + SVGGeneratorContext generatorContext) { + + int imageWidth = image.getWidth(null); + int imageHeight = image.getHeight(null); + AffineTransform af = null; + + if(imageWidth == 0 || imageHeight == 0 || + width == 0 || height == 0) { + + // Forget about it + handleEmptyImage(imageElement); + + } else { + // First set the href + try { + handleHREF(image, imageElement, generatorContext); + } catch (SVGGraphics2DIOException e) { + try { + generatorContext.errorHandler.handleError(e); + } catch (SVGGraphics2DIOException io) { + // we need a runtime exception because + // java.awt.Graphics2D method doesn't throw exceptions.. + throw new SVGGraphics2DRuntimeException(io); + } + } + + // Then create the transformation: + // Because we cache image data, the stored image may + // need to be scaled. + af = handleTransform(imageElement, (double) x, (double) y, + (double) imageWidth, (double) imageHeight, + (double) width, (double) height); + } + return af; + } + + /** + * The handler sets the xlink:href tag and returns a transform + */ + public AffineTransform handleImage(RenderedImage image, + Element imageElement, + int x, int y, + int width, int height, + SVGGeneratorContext generatorContext) { + + int imageWidth = image.getWidth(); + int imageHeight = image.getHeight(); + AffineTransform af = null; + + if(imageWidth == 0 || imageHeight == 0 || + width == 0 || height == 0) { + + // Forget about it + handleEmptyImage(imageElement); + + } else { + // First set the href + try { + handleHREF(image, imageElement, generatorContext); + } catch (SVGGraphics2DIOException e) { + try { + generatorContext.errorHandler.handleError(e); + } catch (SVGGraphics2DIOException io) { + // we need a runtime exception because + // java.awt.Graphics2D method doesn't throw exceptions.. + throw new SVGGraphics2DRuntimeException(io); + } + } + + // Then create the transformation: + // Because we cache image data, the stored image may + // need to be scaled. + af = handleTransform(imageElement, (double) x, (double) y, + (double) imageWidth, (double) imageHeight, + (double) width, (double) height); + } + return af; + } + + /** + * The handler sets the xlink:href tag and returns a transform + */ + public AffineTransform handleImage(RenderableImage image, + Element imageElement, + double x, double y, + double width, double height, + SVGGeneratorContext generatorContext) { + + double imageWidth = image.getWidth(); + double imageHeight = image.getHeight(); + AffineTransform af = null; + + if(imageWidth == 0 || imageHeight == 0 || + width == 0 || height == 0) { + + // Forget about it + handleEmptyImage(imageElement); + + } else { + // First set the href + try { + handleHREF(image, imageElement, generatorContext); + } catch (SVGGraphics2DIOException e) { + try { + generatorContext.errorHandler.handleError(e); + } catch (SVGGraphics2DIOException io) { + // we need a runtime exception because + // java.awt.Graphics2D method doesn't throw exceptions.. + throw new SVGGraphics2DRuntimeException(io); + } + } + + // Then create the transformation: + // Because we cache image data, the stored image may + // need to be scaled. + af = handleTransform(imageElement, x,y, + imageWidth, imageHeight, + width, height); + } + return af; + } + + /** + * Determines the transformation needed to get the cached image to + * scale & position properly. Sets x and y attributes on the element + * accordingly. + */ + protected AffineTransform handleTransform(Element imageElement, + double x, double y, + double srcWidth, + double srcHeight, + double dstWidth, + double dstHeight) { + // In this the default case, <image> element, we just + // set x, y, width and height attributes. + // No additional transform is necessary. + + imageElement.setAttributeNS(null, + SVG_X_ATTRIBUTE, + AbstractSVGConverter.doubleString(x)); + imageElement.setAttributeNS(null, + SVG_Y_ATTRIBUTE, + AbstractSVGConverter.doubleString(y)); + imageElement.setAttributeNS(null, + SVG_WIDTH_ATTRIBUTE, + AbstractSVGConverter.doubleString(dstWidth)); + imageElement.setAttributeNS(null, + SVG_HEIGHT_ATTRIBUTE, + AbstractSVGConverter.doubleString(dstHeight)); + return null; + } + + protected void handleEmptyImage(Element imageElement) { + imageElement.setAttributeNS(XLINK_NAMESPACE_URI, + ATTR_XLINK_HREF, ""); + imageElement.setAttributeNS(null, SVG_WIDTH_ATTRIBUTE, "0"); + imageElement.setAttributeNS(null, SVG_HEIGHT_ATTRIBUTE, "0"); + } + } 1.3 +4 -0 xml-batik/sources/org/apache/batik/svggen/ErrorConstants.java Index: ErrorConstants.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/svggen/ErrorConstants.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- ErrorConstants.java 2001/04/26 14:17:03 1.2 +++ ErrorConstants.java 2001/10/31 09:54:25 1.3 @@ -28,6 +28,10 @@ "image should not be null"; public static final String ERR_WRITE = "could not write image File "; + public static final String ERR_READ = + "could not read image File "; + public static final String ERR_IMAGE_HANDLER_NOT_SUPPORTED = + "imageHandler does not implement CachedImageHandler: "; // SVGGraphics2D errors 1.14 +1 -4 xml-batik/sources/org/apache/batik/svggen/ImageHandlerBase64Encoder.java Index: ImageHandlerBase64Encoder.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/svggen/ImageHandlerBase64Encoder.java,v retrieving revision 1.13 retrieving revision 1.14 diff -u -r1.13 -r1.14 --- ImageHandlerBase64Encoder.java 2001/07/05 16:54:44 1.13 +++ ImageHandlerBase64Encoder.java 2001/10/31 09:54:25 1.14 @@ -28,14 +28,11 @@ * the data protocol. * * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: ImageHandlerBase64Encoder.java,v 1.13 2001/07/05 16:54:44 deweese Exp $ + * @version $Id: ImageHandlerBase64Encoder.java,v 1.14 2001/10/31 09:54:25 vhardy Exp $ * @see org.apache.batik.svggen.SVGGraphics2D * @see org.apache.batik.svggen.ImageHandler */ public class ImageHandlerBase64Encoder extends DefaultImageHandler { - private static final String DATA_PROTOCOL_PNG_PREFIX = - "data:image/png;base64,"; - /** * Build an <code>ImageHandlerBase64Encoder</code> instance. */ 1.8 +8 -15 xml-batik/sources/org/apache/batik/svggen/ImageHandlerJPEGEncoder.java Index: ImageHandlerJPEGEncoder.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/svggen/ImageHandlerJPEGEncoder.java,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- ImageHandlerJPEGEncoder.java 2001/04/02 13:36:05 1.7 +++ ImageHandlerJPEGEncoder.java 2001/10/31 09:54:25 1.8 @@ -26,7 +26,7 @@ * image elements it handles. * * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: ImageHandlerJPEGEncoder.java,v 1.7 2001/04/02 13:36:05 cjolif Exp $ + * @version $Id: ImageHandlerJPEGEncoder.java,v 1.8 2001/10/31 09:54:25 vhardy Exp $ * @see org.apache.batik.svggen.SVGGraphics2D * @see org.apache.batik.svggen.ImageHandlerJPEGEncoder * @see org.apache.batik.svggen.ImageHandlerPNGEncoder @@ -64,20 +64,13 @@ * Derived classes should implement this method and encode the input * BufferedImage as needed */ - public void encodeImage(BufferedImage buf, File imageFile) - throws SVGGraphics2DIOException { - try{ - OutputStream os = new FileOutputStream(imageFile); - JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(os); - JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(buf); - param.setQuality(1, false); - encoder.encode(buf, param); - os.flush(); - os.close(); - } catch(IOException e) { - throw new SVGGraphics2DIOException(ERR_WRITE+imageFile.getName()); - } - } + public void encodeImage(BufferedImage buf, OutputStream os) + throws IOException { + JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(os); + JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(buf); + param.setQuality(1, false); + encoder.encode(buf, param); + } /** * This method creates a BufferedImage of the right size and type 1.9 +5 -11 xml-batik/sources/org/apache/batik/svggen/ImageHandlerPNGEncoder.java Index: ImageHandlerPNGEncoder.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/svggen/ImageHandlerPNGEncoder.java,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- ImageHandlerPNGEncoder.java 2001/07/04 16:50:02 1.8 +++ ImageHandlerPNGEncoder.java 2001/10/31 09:54:25 1.9 @@ -27,7 +27,7 @@ * image elements it handles. * * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: ImageHandlerPNGEncoder.java,v 1.8 2001/07/04 16:50:02 cjolif Exp $ + * @version $Id: ImageHandlerPNGEncoder.java,v 1.9 2001/10/31 09:54:25 vhardy Exp $ * @see org.apache.batik.svggen.SVGGraphics2D * @see org.apache.batik.svggen.ImageHandlerJPEGEncoder * @see org.apache.batik.svggen.ImageHandlerPNGEncoder @@ -65,16 +65,10 @@ * Derived classes should implement this method and encode the input * BufferedImage as needed */ - public void encodeImage(BufferedImage buf, File imageFile) - throws SVGGraphics2DIOException { - try { - OutputStream os = new FileOutputStream(imageFile); - ImageEncoder encoder = new PNGImageEncoder(os, null); - encoder.encode(buf); - os.close(); - } catch (IOException e) { - throw new SVGGraphics2DIOException(ERR_WRITE+imageFile.getName()); - } + public void encodeImage(BufferedImage buf, OutputStream os) + throws IOException { + ImageEncoder encoder = new PNGImageEncoder(os, null); + encoder.encode(buf); } /** 1.27 +8 -1 xml-batik/sources/org/apache/batik/svggen/SVGGraphics2D.java Index: SVGGraphics2D.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/svggen/SVGGraphics2D.java,v retrieving revision 1.26 retrieving revision 1.27 diff -u -r1.26 -r1.27 --- SVGGraphics2D.java 2001/10/23 07:37:05 1.26 +++ SVGGraphics2D.java 2001/10/31 09:54:25 1.27 @@ -45,7 +45,7 @@ * * * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: SVGGraphics2D.java,v 1.26 2001/10/23 07:37:05 vhardy Exp $ + * @version $Id: SVGGraphics2D.java,v 1.27 2001/10/31 09:54:25 vhardy Exp $ * @see org.apache.batik.ext.awt.g2d.GraphicContext * @see org.apache.batik.svggen.DOMTreeManager * @see org.apache.batik.svggen.DOMGroupManager @@ -144,6 +144,13 @@ */ public final DOMTreeManager getDOMTreeManager(){ return domTreeManager; + } + + /** + * @return the DOMGroupManager used by this SVGGraphics2D instance + */ + final DOMGroupManager getDOMGroupManager(){ + return domGroupManager; } /** 1.6 +6 -1 xml-batik/sources/org/apache/batik/svggen/SVGSyntax.java Index: SVGSyntax.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/svggen/SVGSyntax.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- SVGSyntax.java 2001/08/03 04:21:53 1.5 +++ SVGSyntax.java 2001/10/31 09:54:25 1.6 @@ -14,7 +14,7 @@ * Contains the definition of the SVG tags and attribute names. * * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: SVGSyntax.java,v 1.5 2001/08/03 04:21:53 bella Exp $ + * @version $Id: SVGSyntax.java,v 1.6 2001/10/31 09:54:25 vhardy Exp $ */ public interface SVGSyntax extends SVGConstants{ /** @@ -49,6 +49,8 @@ public static final String ID_PREFIX_FE_SPECULAR_LIGHTING = "feSpecularLighting"; public static final String ID_PREFIX_FONT = "font"; public static final String ID_PREFIX_GENERIC_DEFS = "genericDefs"; + public static final String ID_PREFIX_IMAGE = "image"; + public static final String ID_PREFIX_IMAGE_DEFS = "imageDefs"; public static final String ID_PREFIX_LINEAR_GRADIENT = "linearGradient"; public static final String ID_PREFIX_MASK = "mask"; public static final String ID_PREFIX_PATTERN = "pattern"; @@ -69,5 +71,8 @@ public static final String SPACE = " "; public static final String URL_PREFIX = "url("; public static final String URL_SUFFIX = ")"; + + public static final String DATA_PROTOCOL_PNG_PREFIX = "data:image/png;base64,"; + } 1.1 xml-batik/sources/org/apache/batik/svggen/CachedImageHandler.java Index: CachedImageHandler.java =================================================================== /***************************************************************************** * Copyright (C) The Apache Software Foundation. All rights reserved. * * ------------------------------------------------------------------------- * * This software is published under the terms of the Apache Software License * * version 1.1, a copy of which has been included with this distribution in * * the LICENSE file. * *****************************************************************************/ package org.apache.batik.svggen; import java.awt.Image; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import java.awt.geom.AffineTransform; import org.w3c.dom.Element; /** * Extends the default ImageHandler interface with calls to * allow caching of raster images in generated SVG content. * * @author <a href="mailto:[EMAIL PROTECTED]">Paul Evenblij</a> * @version $Id: CachedImageHandler.java,v 1.1 2001/10/31 09:54:25 vhardy Exp $ */ public interface CachedImageHandler extends ImageHandler { /** * Creates an Element suitable for referring to images. * Note that no assumptions can be made about the name of this Element. */ public Element createElement(SVGGeneratorContext generatorContext); /** * The handler should set the xlink:href tag and return a transform * * @param image the image under consideration * @param imageElement the DOM Element for this image * @param x x coordinate * @param y y coordinate * @param width width for rendering * @param height height for rendering * @param generatorContext the SVGGeneratorContext * * @return transform converting the image dimension to rendered dimension */ public AffineTransform handleImage(Image image, Element imageElement, int x, int y, int width, int height, SVGGeneratorContext generatorContext); /** * The handler should set the xlink:href tag and return a transform * * @param image the image under consideration * @param imageElement the DOM Element for this image * @param x x coordinate * @param y y coordinate * @param width width for rendering * @param height height for rendering * @param generatorContext the SVGGeneratorContext * * @return transform converting the image dimension to rendered dimension */ public AffineTransform handleImage(RenderedImage image, Element imageElement, int x, int y, int width, int height, SVGGeneratorContext generatorContext); /** * The handler should set the xlink:href tag and return a transform * * @param image the image under consideration * @param imageElement the DOM Element for this image * @param x x coordinate * @param y y coordinate * @param width width for rendering * @param height height for rendering * @param generatorContext the SVGGeneratorContext * * @return transform converting the image dimension to rendered dimension */ public AffineTransform handleImage(RenderableImage image, Element imageElement, double x, double y, double width, double height, SVGGeneratorContext generatorContext); /** * Returns the image cache instance in use by this handler * * @return the image cache */ public ImageCacher getImageCacher(); } 1.1 xml-batik/sources/org/apache/batik/svggen/CachedImageHandlerBase64Encoder.java Index: CachedImageHandlerBase64Encoder.java =================================================================== /***************************************************************************** * Copyright (C) The Apache Software Foundation. All rights reserved. * * ------------------------------------------------------------------------- * * This software is published under the terms of the Apache Software License * * version 1.1, a copy of which has been included with this distribution in * * the LICENSE file. * *****************************************************************************/ package org.apache.batik.svggen; import java.awt.image.RenderedImage; import java.awt.geom.AffineTransform; import java.io.ByteArrayOutputStream; import java.io.IOException; import org.w3c.dom.*; import org.apache.batik.util.Base64EncoderStream; /** * This subclass of {@link ImageHandlerBase64Encoder} implements * functionality specific to the cached version of the image * encoder. * * @author <a href="mailto:[EMAIL PROTECTED]">Paul Evenblij</a> * @version $Id: CachedImageHandlerBase64Encoder.java,v 1.1 2001/10/31 09:54:25 vhardy Exp $ */ public class CachedImageHandlerBase64Encoder extends ImageHandlerBase64Encoder { /** * Build a <code>CachedImageHandlerBase64Encoder</code> instance. */ public CachedImageHandlerBase64Encoder() { super(); setImageCacher(new ImageCacher.Embedded()); } /** * Creates an Element which can refer to an image. * Note that no assumptions should be made by the caller about the * corresponding SVG tag. */ public Element createElement(SVGGeneratorContext generatorContext) { // Create a DOM Element in SVG namespace to refer to an image // For this cached version we return <use> Element imageElement = generatorContext.getDOMFactory().createElementNS( SVG_NAMESPACE_URI, SVG_USE_TAG); return imageElement; } /** * This version of handleHREF encodes the input image into a * PNG image whose bytes are then encoded with Base64. The * resulting encoded data is used to set the url on the * input imageElement. */ protected void handleHREF(RenderedImage image, Element imageElement, SVGGeneratorContext generatorContext) throws SVGGraphics2DIOException { // // Setup Base64Encoder stream to byte array. // ByteArrayOutputStream os = new ByteArrayOutputStream(); Base64EncoderStream b64Encoder = new Base64EncoderStream(os); try { // // Now, encode the input image to the base 64 stream. // encodeImage(image, b64Encoder); // Close the b64 encoder stream (terminates the b64 streams). b64Encoder.close(); } catch (IOException e) { // Should not happen because we are doing in-memory processing throw new SVGGraphics2DIOException(ERR_UNEXPECTED, e); } // // Ask for a href from the image cacher // String href = imageCacher.lookup(os, image.getWidth(), image.getHeight(), generatorContext); // // Finally, write out url // imageElement.setAttributeNS(XLINK_NAMESPACE_URI, ATTR_XLINK_HREF, href); } /** * Determines the transformation needed to get the cached image to * scale & position properly. Sets x and y attributes on the element * accordingly. */ protected AffineTransform handleTransform(Element imageElement, double x, double y, double srcWidth, double srcHeight, double dstWidth, double dstHeight) { // If scaling is necessary, create a transform, since "width" and "height" // have no effect on a <use> element referring to an <image> element. AffineTransform af = null; double hRatio = dstWidth / srcWidth; double vRatio = dstHeight / srcHeight; double xScaled = x / hRatio; double yScaled = y / vRatio; if(hRatio != 1 || vRatio != 1) { af = AffineTransform.getScaleInstance(hRatio, vRatio); } imageElement.setAttributeNS(null, SVG_X_ATTRIBUTE, AbstractSVGConverter.doubleString(xScaled)); imageElement.setAttributeNS(null, SVG_Y_ATTRIBUTE, AbstractSVGConverter.doubleString(yScaled)); return af; } } 1.1 xml-batik/sources/org/apache/batik/svggen/CachedImageHandlerJPEGEncoder.java Index: CachedImageHandlerJPEGEncoder.java =================================================================== /***************************************************************************** * Copyright (C) The Apache Software Foundation. All rights reserved. * * ------------------------------------------------------------------------- * * This software is published under the terms of the Apache Software License * * version 1.1, a copy of which has been included with this distribution in * * the LICENSE file. * *****************************************************************************/ package org.apache.batik.svggen; import java.awt.image.BufferedImage; import org.w3c.dom.*; /** * Caching version of the JPEG image handler. * * @author <a href="mailto:[EMAIL PROTECTED]">Paul Evenblij</a> * @version $Id: CachedImageHandlerJPEGEncoder.java,v 1.1 2001/10/31 09:54:25 vhardy Exp $ */ public class CachedImageHandlerJPEGEncoder extends ImageHandlerJPEGEncoder { /** * @param imageDir directory where this handler should generate images. * If null, an IllegalArgumentException is thrown. * @param urlRoot root for the urls that point to images created by this * image handler. If null, then the url corresponding to imageDir * is used. */ public CachedImageHandlerJPEGEncoder(String imageDir, String urlRoot) throws SVGGraphics2DIOException { super(imageDir, urlRoot); setImageCacher(new ImageCacher.External(imageDir, getPrefix(), getSuffix())); } /** * Save with caching. */ protected void saveBufferedImageToFile(Element imageElement, BufferedImage buf, SVGGeneratorContext generatorContext) throws SVGGraphics2DIOException { cacheBufferedImage(imageElement, buf, generatorContext); } } 1.1 xml-batik/sources/org/apache/batik/svggen/CachedImageHandlerPNGEncoder.java Index: CachedImageHandlerPNGEncoder.java =================================================================== /***************************************************************************** * Copyright (C) The Apache Software Foundation. All rights reserved. * * ------------------------------------------------------------------------- * * This software is published under the terms of the Apache Software License * * version 1.1, a copy of which has been included with this distribution in * * the LICENSE file. * *****************************************************************************/ package org.apache.batik.svggen; import java.awt.image.BufferedImage; import org.w3c.dom.*; /** * Caching version of the PNG image handler. * * @author <a href="mailto:[EMAIL PROTECTED]">Paul Evenblij</a> * @version $Id: CachedImageHandlerPNGEncoder.java,v 1.1 2001/10/31 09:54:25 vhardy Exp $ */ public class CachedImageHandlerPNGEncoder extends ImageHandlerPNGEncoder { /** * @param imageDir directory where this handler should generate images. * If null, an IllegalArgumentException is thrown. * @param urlRoot root for the urls that point to images created by this * image handler. If null, then the url corresponding to imageDir * is used. */ public CachedImageHandlerPNGEncoder(String imageDir, String urlRoot) throws SVGGraphics2DIOException { super(imageDir, urlRoot); setImageCacher(new ImageCacher.External(imageDir, getPrefix(), getSuffix())); } /** * Save with caching. */ protected void saveBufferedImageToFile(Element imageElement, BufferedImage buf, SVGGeneratorContext generatorContext) throws SVGGraphics2DIOException { cacheBufferedImage(imageElement, buf, generatorContext); } } 1.1 xml-batik/sources/org/apache/batik/svggen/CachedImageSVGGraphics2D.java Index: CachedImageSVGGraphics2D.java =================================================================== /***************************************************************************** * Copyright (C) The Apache Software Foundation. All rights reserved. * * ------------------------------------------------------------------------- * * This software is published under the terms of the Apache Software License * * version 1.1, a copy of which has been included with this distribution in * * the LICENSE file. * *****************************************************************************/ package org.apache.batik.svggen; import java.io.*; import java.lang.*; import java.util.*; import java.awt.*; import java.awt.image.*; import java.awt.geom.*; import java.awt.image.renderable.*; import org.w3c.dom.*; import org.apache.batik.ext.awt.g2d.AbstractGraphics2D; import org.apache.batik.ext.awt.g2d.GraphicContext; /** * This specialization of the SVGGraphics2D class * uses a caching mechanism for raster images. * * @author <a href="mailto:[EMAIL PROTECTED]">Paul Evenblij</a> * @version $Id: CachedImageSVGGraphics2D.java,v 1.1 2001/10/31 09:54:25 vhardy Exp $ */ public class CachedImageSVGGraphics2D extends SVGGraphics2D { public CachedImageSVGGraphics2D(Document domFactory) { super(domFactory); CachedImageHandler imageHandler = new CachedImageHandlerBase64Encoder(); imageHandler.getImageCacher().setDOMTreeManager(getDOMTreeManager()); getGeneratorContext().setImageHandler(imageHandler); } public CachedImageSVGGraphics2D(Document domFactory, CachedImageHandler imageHandler, ExtensionHandler extensionHandler, boolean textAsShapes) { super(domFactory, imageHandler, extensionHandler, textAsShapes); getCachedImageHandler().getImageCacher().setDOMTreeManager( getDOMTreeManager()); } public CachedImageSVGGraphics2D(SVGGeneratorContext generatorCtx, boolean textAsShapes) { super(generatorCtx, textAsShapes); getCachedImageHandler().getImageCacher().setDOMTreeManager( getDOMTreeManager()); } public CachedImageHandler getCachedImageHandler() { ImageHandler imageHandler = getImageHandler(); if( ! (imageHandler instanceof CachedImageHandler)) { throw new SVGGraphics2DRuntimeException( ERR_IMAGE_HANDLER_NOT_SUPPORTED+ imageHandler.getClass().getName()); } return (CachedImageHandler) imageHandler; } /** * Draws as much of the specified image as is currently available. * The image is drawn with its top-left corner at * (<i>x</i>, <i>y</i>) in this graphics context's coordinate * space. Transparent pixels in the image do not affect whatever * pixels are already there. * <p> * This method returns immediately in all cases, even if the * complete image has not yet been loaded, and it has not been dithered * and converted for the current output device. * <p> * If the image has not yet been completely loaded, then * <code>drawImage</code> returns <code>false</code>. As more of * the image becomes available, the process that draws the image notifies * the specified image observer. * @param img the specified image to be drawn. * @param x the <i>x</i> coordinate. * @param y the <i>y</i> coordinate. * @param observer object to be notified as more of * the image is converted. * @see java.awt.Image * @see java.awt.image.ImageObserver * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) */ public boolean drawImage(Image img, int x, int y, ImageObserver observer) { Element imageElement = getCachedImageHandler().createElement(getGeneratorContext()); AffineTransform xform = getCachedImageHandler().handleImage( img, imageElement, x, y, img.getWidth(null), img.getHeight(null), getGeneratorContext()); if (xform == null) { getDOMGroupManager().addElement(imageElement); } else { AffineTransform inverseTransform = null; try { inverseTransform = xform.createInverse(); } catch(NoninvertibleTransformException e) { // This should never happen since handleImage // always returns invertible transform throw new SVGGraphics2DRuntimeException(ERR_UNEXPECTED); } gc.transform(xform); getDOMGroupManager().addElement(imageElement); gc.transform(inverseTransform); } return true; } /** * Draws as much of the specified image as has already been scaled * to fit inside the specified rectangle. * <p> * The image is drawn inside the specified rectangle of this * graphics context's coordinate space, and is scaled if * necessary. Transparent pixels do not affect whatever pixels * are already there. * <p> * This method returns immediately in all cases, even if the * entire image has not yet been scaled, dithered, and converted * for the current output device. * If the current output representation is not yet complete, then * <code>drawImage</code> returns <code>false</code>. As more of * the image becomes available, the process that draws the image notifies * the image observer by calling its <code>imageUpdate</code> method. * <p> * A scaled version of an image will not necessarily be * available immediately just because an unscaled version of the * image has been constructed for this output device. Each size of * the image may be cached separately and generated from the original * data in a separate image production sequence. * @param img the specified image to be drawn. * @param x the <i>x</i> coordinate. * @param y the <i>y</i> coordinate. * @param width the width of the rectangle. * @param height the height of the rectangle. * @param observer object to be notified as more of * the image is converted. * @see java.awt.Image * @see java.awt.image.ImageObserver * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) */ public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer){ Element imageElement = getCachedImageHandler().createElement(getGeneratorContext()); AffineTransform xform = getCachedImageHandler().handleImage( img, imageElement, x, y, width, height, getGeneratorContext()); if (xform == null) { getDOMGroupManager().addElement(imageElement); } else { AffineTransform inverseTransform = null; try { inverseTransform = xform.createInverse(); } catch(NoninvertibleTransformException e) { // This should never happen since handleImage // always returns invertible transform throw new SVGGraphics2DRuntimeException(ERR_UNEXPECTED); } gc.transform(xform); getDOMGroupManager().addElement(imageElement); gc.transform(inverseTransform); } return true; } /** * Renders a {@link RenderedImage}, * applying a transform from image * space into user space before drawing. * The transformation from user space into device space is done with * the current <code>Transform</code> in the <code>Graphics2D</code>. * The specified transformation is applied to the image before the * transform attribute in the <code>Graphics2D</code> context is applied. * The rendering attributes applied include the <code>Clip</code>, * <code>Transform</code>, and <code>Composite</code> attributes. Note * that no rendering is done if the specified transform is * noninvertible. * @param img the image to be rendered * @param xform the transformation from image space into user space * @see #transform * @see #setTransform * @see #setComposite * @see #clip * @see #setClip */ public void drawRenderedImage(RenderedImage img, AffineTransform trans2) { Element image = getCachedImageHandler().createElement(getGeneratorContext()); AffineTransform trans1 = getCachedImageHandler().handleImage( img, image, img.getMinX(), img.getMinY(), img.getWidth(), img.getHeight(), getGeneratorContext()); AffineTransform xform; // Concatenate the transformation we receive from the imageHandler // to the user-supplied one. Be aware that both may be null. if (trans2 == null) { xform = trans1; } else { if(trans1 == null) { xform = trans2; } else { xform = new AffineTransform(trans2); xform.concatenate(trans1); } } if(xform == null) { getDOMGroupManager().addElement(image); } else if(xform.getDeterminant() != 0){ AffineTransform inverseTransform = null; try{ inverseTransform = xform.createInverse(); }catch(NoninvertibleTransformException e){ // This should never happen since we checked // the matrix determinant throw new SVGGraphics2DRuntimeException(ERR_UNEXPECTED); } gc.transform(xform); getDOMGroupManager().addElement(image); gc.transform(inverseTransform); } else { AffineTransform savTransform = new AffineTransform(gc.getTransform()); gc.transform(xform); getDOMGroupManager().addElement(image); gc.setTransform(savTransform); } } /** * Renders a * {@link RenderableImage}, * applying a transform from image space into user space before drawing. * The transformation from user space into device space is done with * the current <code>Transform</code> in the <code>Graphics2D</code>. * The specified transformation is applied to the image before the * transform attribute in the <code>Graphics2D</code> context is applied. * The rendering attributes applied include the <code>Clip</code>, * <code>Transform</code>, and <code>Composite</code> attributes. Note * that no rendering is done if the specified transform is * noninvertible. *<p> * Rendering hints set on the <code>Graphics2D</code> object might * be used in rendering the <code>RenderableImage</code>. * If explicit control is required over specific hints recognized by a * specific <code>RenderableImage</code>, or if knowledge of which hints * are used is required, then a <code>RenderedImage</code> should be * obtained directly from the <code>RenderableImage</code> * and rendered using *{@link #drawRenderedImage(RenderedImage, AffineTransform)}. * @param img the image to be rendered * @param xform the transformation from image space into user space * @see #transform * @see #setTransform * @see #setComposite * @see #clip * @see #setClip * @see #drawRenderedImage */ public void drawRenderableImage(RenderableImage img, AffineTransform trans2){ Element image = getCachedImageHandler().createElement(getGeneratorContext()); AffineTransform trans1 = getCachedImageHandler().handleImage( img, image, img.getMinX(), img.getMinY(), img.getWidth(), img.getHeight(), getGeneratorContext()); AffineTransform xform; // Concatenate the transformation we receive from the imageHandler // to the user-supplied one. Be aware that both may be null. if (trans2 == null) { xform = trans1; } else { if(trans1 == null) { xform = trans2; } else { xform = new AffineTransform(trans2); xform.concatenate(trans1); } } if (xform == null) { getDOMGroupManager().addElement(image); } else if(xform.getDeterminant() != 0){ AffineTransform inverseTransform = null; try{ inverseTransform = xform.createInverse(); }catch(NoninvertibleTransformException e){ // This should never happen because we checked the // matrix determinant throw new SVGGraphics2DRuntimeException(ERR_UNEXPECTED); } gc.transform(xform); getDOMGroupManager().addElement(image); gc.transform(inverseTransform); } else { AffineTransform savTransform = new AffineTransform(gc.getTransform()); gc.transform(xform); getDOMGroupManager().addElement(image); gc.setTransform(savTransform); } } } 1.1 xml-batik/sources/org/apache/batik/svggen/ImageCacher.java Index: ImageCacher.java =================================================================== /***************************************************************************** * Copyright (C) The Apache Software Foundation. All rights reserved. * * ------------------------------------------------------------------------- * * This software is published under the terms of the Apache Software License * * version 1.1, a copy of which has been included with this distribution in * * the LICENSE file. * *****************************************************************************/ package org.apache.batik.svggen; import java.io.*; import java.util.Hashtable; import java.util.LinkedList; import java.util.ListIterator; import java.util.Arrays; import java.util.zip.Checksum; import java.util.zip.Adler32; import java.awt.image.RenderedImage; import org.w3c.dom.*; /** * This class implements caching functionality for raster images. * * @author <a href="mailto:[EMAIL PROTECTED]">Paul Evenblij</a> * @version $Id: ImageCacher.java,v 1.1 2001/10/31 09:54:25 vhardy Exp $ */ public abstract class ImageCacher implements SVGSyntax, ErrorConstants { DOMTreeManager domTreeManager = null; Hashtable imageCache; Checksum checkSum; /** * Creates an ImageCacher. */ public ImageCacher() { imageCache = new Hashtable(); checkSum = new Adler32(); } /** * Creates an ImageCacher. * * @param domTreeManager the DOMTreeManager for the tree this cacher works on */ public ImageCacher(DOMTreeManager domTreeManager) { this(); setDOMTreeManager(domTreeManager); } /** * Sets the DOMTreeManager this cacher should work on. * * @param domTreeManager the DOMTreeManager for the tree this cacher works on */ protected void setDOMTreeManager(DOMTreeManager domTreeManager) { this.domTreeManager = domTreeManager; } /** * Checks if the image is already in the cache, and * adds it if not. Returns a unique id for the entry. * * @param os the image as a byte stream * @param width the width of the image * @param height the height of the image * @param ctx the SVGGeneratorContext * * @return a URI for the image * @throws SVGGraphics2DIOException if an error occurs during image file i/o */ public String lookup(ByteArrayOutputStream os, int width, int height, SVGGeneratorContext ctx) throws SVGGraphics2DIOException { // We determine a checksum value for the byte data, and use it // as hash key for the image. This may not be unique, so we // need to check on actual byte-for-byte equality as well. // The checksum will be sufficient in most cases. int checksum = getChecksum(os.toByteArray()); Integer key = new Integer(checksum); String href = null; Object data = getCacheableData(os); LinkedList list = (LinkedList) imageCache.get(key); if(list == null) { // Key not found: make a new key/value pair list = new LinkedList(); imageCache.put(key, list); } else { // Key found: check if the image is already in the list for(ListIterator i = list.listIterator(0); i.hasNext(); ) { ImageCacheEntry entry = (ImageCacheEntry) i.next(); if(entry.checksum == checksum && imagesMatch(entry.src, data)) { href = entry.href; break; } } } if(href == null) { // Still no hit: add our own ImageCacheEntry newEntry = createEntry(checksum, data, width, height, ctx); list.add(newEntry); href = newEntry.href; } return href; } /** * Returns an object which can be cached. * Implementation must determine which information * should actually be stored. * * @param os the byte stream which is to be coerced */ abstract Object getCacheableData(ByteArrayOutputStream os); /** * Determines if two images are equal. * Interpretation of the objects referred to by * <code>o1</code> and <code>o2</code> is entirely * implementation-dependent. * * @param o1 object referring to one image * @param o2 object referring to the other image */ abstract boolean imagesMatch(Object o1, Object o2) throws SVGGraphics2DIOException; /** * Creates a new entry for keeping in the cache. * * @param checksum the checksum from which the hash key is derived * @param data the data to be cached * @param width image width * @param height image height * @param ctx the SVGGeneratorContext */ abstract ImageCacheEntry createEntry(int checksum, Object data, int width, int height, SVGGeneratorContext ctx) throws SVGGraphics2DIOException; /** * Calculates a checksum value for the given data. */ int getChecksum(byte[] data) { checkSum.reset(); checkSum.update(data, 0, data.length); return (int) checkSum.getValue(); } /** * Instances of this class are created to keep track of the * set of images processed by the ImageHandler. Each entry * corresponds to one unique member of this set. */ private class ImageCacheEntry { /** A checksum calculated for the data cached */ public int checksum; /** An implementation-dependent object referring to the data */ public Object src; /** A uri identifying the data */ public String href; /** * Creates a new entry */ public ImageCacheEntry(int checksum, Object src, String href) { this.checksum = checksum; this.src = src; this.href = href; } } /** * Cache implementation for images embedded in the SVG file. */ public static class Embedded extends ImageCacher { /** * Sets the DOMTreeManager this cacher should work on. * * @param domTreeManager the DOMTreeManager for the tree this cacher works on */ protected void setDOMTreeManager(DOMTreeManager domTreeManager) { // A new DOMTreeManager implies a new cache, because we cache // images in the SVG tree itself if(this.domTreeManager != domTreeManager) { this.domTreeManager = domTreeManager; this.imageCache = new Hashtable(); } } Object getCacheableData(ByteArrayOutputStream os) { // In order to have only one instance of the image data // in memory, we cache the entire xlink:href attribute value, // so we can just pass a reference to the tree manager. return DATA_PROTOCOL_PNG_PREFIX + os.toString(); } boolean imagesMatch(Object o1, Object o2) { return o1.equals(o2); } ImageCacheEntry createEntry(int checksum, Object data, int width, int height, SVGGeneratorContext ctx) { // Get a new unique id String id = ctx.idGenerator.generateID(ID_PREFIX_IMAGE); // Add the image data reference to the <defs> section addToTree(id, (String) data, width, height, ctx); // Create new cache entry return new ImageCacheEntry(checksum, data, SIGN_POUND + id); } /** * Adds a new image element to the defs section for cached images. */ private void addToTree(String id, String href, int width, int height, SVGGeneratorContext ctx) { Document domFactory = domTreeManager.getDOMFactory(); Element imageDefs = getImageDefs(domFactory, ctx); // Create and initialize the new image element Element imageElement = domFactory.createElementNS(SVG_NAMESPACE_URI, SVG_IMAGE_TAG); imageElement.setAttributeNS(null, SVG_ID_ATTRIBUTE, id); imageElement.setAttributeNS(null, SVG_WIDTH_ATTRIBUTE, Integer.toString(width)); imageElement.setAttributeNS(null, SVG_HEIGHT_ATTRIBUTE, Integer.toString(height)); imageElement.setAttributeNS(DefaultImageHandler.XLINK_NAMESPACE_URI, ATTR_XLINK_HREF, href); imageDefs.appendChild(imageElement); } /** * Returns the top level defs section dedicated to cached * embedded images, creating one if necessary. * This one very simply creates a new defs section for each image, * causing them to be spread throughout the entire SVG tree. * A nicer implementation would group all imageDefs sections into * one. */ private Element getImageDefs(Document domFactory, SVGGeneratorContext ctx) { Element imageDefs = domFactory.createElementNS(SVG_NAMESPACE_URI, SVG_DEFS_TAG); String id = ctx.idGenerator.generateID(ID_PREFIX_IMAGE_DEFS); imageDefs.setAttributeNS(null, SVG_ID_ATTRIBUTE, id); domTreeManager.appendGroup(imageDefs, null); return imageDefs; } } /** * Cache implementation for file-based images. */ public static class External extends ImageCacher { private String imageDir; private String prefix; private String suffix; public External(String imageDir, String prefix, String suffix) { super(); this.imageDir = imageDir; this.prefix = prefix; this.suffix = suffix; } Object getCacheableData(ByteArrayOutputStream os) { return os; } boolean imagesMatch(Object o1, Object o2) throws SVGGraphics2DIOException { boolean match = false; try { FileInputStream imageStream = new FileInputStream((File) o1); int imageLen = imageStream.available(); byte[] imageBytes = new byte[imageLen]; byte[] candidateBytes = ((ByteArrayOutputStream) o2).toByteArray(); int bytesRead = imageStream.read(imageBytes); match = Arrays.equals(imageBytes, candidateBytes); } catch(IOException e) { throw new SVGGraphics2DIOException( ERR_READ+((File) o1).getName()); } return match; } ImageCacheEntry createEntry(int checksum, Object data, int width, int height, SVGGeneratorContext ctx) throws SVGGraphics2DIOException { // Create a new file in image directory File imageFile = null; try { // While the files we are generating exist, try to create // another unique id. while (imageFile == null) { String fileId = ctx.idGenerator.generateID(prefix); imageFile = new File(imageDir, fileId + suffix); if (imageFile.exists()) imageFile = null; } // Write data to file OutputStream outputStream = new FileOutputStream(imageFile); ((ByteArrayOutputStream) data).writeTo(outputStream); ((ByteArrayOutputStream) data).close(); } catch(IOException e) { throw new SVGGraphics2DIOException(ERR_WRITE+imageFile.getName()); } // Create new cache entry return new ImageCacheEntry(checksum, imageFile, imageFile.getName()); } } } 1.3 +33 -0 xml-batik/resources/org/apache/batik/apps/rasterizer/resources/Messages.properties Index: Messages.properties =================================================================== RCS file: /home/cvs/xml-batik/resources/org/apache/batik/apps/rasterizer/resources/Messages.properties,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- Messages.properties 2001/10/29 19:13:54 1.2 +++ Messages.properties 2001/10/31 09:54:25 1.3 @@ -86,6 +86,16 @@ -cssAlternate <alternate> \n \ \tCSS alternate stylesheet to use when converting the source \n \ \tSVG files. \n \ + -cssUser <userStylesheet> \n \ +\tCSS user stylesheet URI to apply to converted SVG documents \n \ +\tin addition to any other referened or embeded stylesheets. \n \ + -lang <userLanguage> \n \ +\tUser language to use when converting SVG documents.\n \ + -q <quality> \n \ +\tQuality for the output image. This is only relevant for the \n \ +image/jpeg mime type. \n \ + -dpi <resolution> \n \ +\tResolution for the ouptut image. \n \ -validate \n \ \tontrols whether the source SVG files should be validated. \n @@ -143,6 +153,29 @@ SVG files. \n \ Example: -cssAlternate myFavoriteStylesheet \n \ Default: none + +Main.cl.option.cssUser.description = \ +-cssUser <userStylesheetURI> User CSS stylesheet to apply when converting SVG files. \n \ +Example: -cssUser myStylesheet.css +Default: none + +Main.cl.option.q.description = \ +-q <quality> Quality for the generated output image. This is only used for JPEG conversion. \n \ +The value should be in the [0,1[ range. \n \ +Example: -q 0.5 +Default: 0.99 + +Main.cl.option.dpi.description = \ +-dpi <resolution> Resolution for the output image. This is used to compute the \n \ +"pixel to millimeter" ratio used when processing SVG documents. \n \ +Example: -dpi 300 +Default: 96 + +Main.cl.option.lang.description = \ +-lang <language> Language to use when processing SVG documents. This is important for \n \ +SVG documents containing multiple languages. \n +Example: -lang fr +Default: en Main.cl.option.validate.description = \ -validate controls whether the source SVG files should be validated. 1.2 +73 -1 xml-batik/test-sources/org/apache/batik/apps/rasterizer/MainTest.java Index: MainTest.java =================================================================== RCS file: /home/cvs/xml-batik/test-sources/org/apache/batik/apps/rasterizer/MainTest.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- MainTest.java 2001/10/29 19:13:54 1.1 +++ MainTest.java 2001/10/31 09:54:26 1.2 @@ -24,7 +24,7 @@ * Validates the operation of the <tt>Main</tt> class. * * @author <a href="[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: MainTest.java,v 1.1 2001/10/29 19:13:54 vhardy Exp $ + * @version $Id: MainTest.java,v 1.2 2001/10/31 09:54:26 vhardy Exp $ */ public class MainTest extends DefaultTestSuite { @@ -275,6 +275,54 @@ addTest(t); t.setId("MainConfigTest.validate"); + t = new MainConfigTest("-lang fr"){ + public TestReport validate(SVGConverter c){ + if("fr".equals(c.getLanguage())){ + return reportSuccess(); + } else { + return reportError("-lang", "fr", c.getLanguage()); + } + } + }; + addTest(t); + t.setId("MainConfigTest.lang"); + + t = new MainConfigTest("-cssUser myStylesheet.css"){ + public TestReport validate(SVGConverter c){ + if("myStylesheet.css".equals(c.getUserStylesheet())){ + return reportSuccess(); + } else { + return reportError("-cssUser", "myStylesheet.css", c.getUserStylesheet()); + } + } + }; + addTest(t); + t.setId("MainConfigTest.cssUser"); + + t = new MainConfigTest("-dpi 5.08"){ + public TestReport validate(SVGConverter c){ + if(c.getPixelToMillimeter() == .5f){ + return reportSuccess(); + } else { + return reportError("-dpi", ".5f", "" + c.getPixelToMillimeter()); + } + } + }; + addTest(t); + t.setId("MainConfigTest.dpi"); + + t = new MainConfigTest("-q .5"){ + public TestReport validate(SVGConverter c){ + if(c.getQuality() == .5f){ + return reportSuccess(); + } else { + return reportError("-q", ".5f", "" + c.getQuality()); + } + } + }; + addTest(t); + t.setId("MainConfigTest.quality"); + t = new MainConfigErrorTest("-d", "hello.svg -d"); addTest(t); t.setId("MainConfigErrorTest.output"); @@ -307,6 +355,22 @@ addTest(t); t.setId("MainConfigErrorTest.cssAlternate"); + t = new MainConfigErrorTest("-lang", "hello.svg -lang"); + addTest(t); + t.setId("MainConfigErrorTest.lang"); + + t = new MainConfigErrorTest("-cssUser", "hello.svg -cssUser"); + addTest(t); + t.setId("MainConfigErrorTest.cssUser"); + + t = new MainConfigErrorTest("-dpi", "hello.svg -dpi"); + addTest(t); + t.setId("MainConfigErrorTest.dpi"); + + t = new MainConfigErrorTest("-q", "hello.svg -q"); + addTest(t); + t.setId("MainConfigErrorTest.quality"); + t = new MainIllegalArgTest("-m", "-m images/jpeq"); addTest(t); t.setId("MainIllegalArgTest.mediaType"); @@ -326,6 +390,14 @@ t = new MainIllegalArgTest("bg", "-bg a.b.c.d"); addTest(t); t.setId("MainIllegalArgTest.bg"); + + t = new MainIllegalArgTest("dpi", "-dpi invalidDPI"); + addTest(t); + t.setId("MainIllegalArgTest.dpi"); + + t = new MainIllegalArgTest("q", "-q illegalQuality"); + addTest(t); + t.setId("MainIllegalArgTest.q"); } 1.9 +28 -1 xml-batik/test-sources/org/apache/batik/apps/rasterizer/SVGConverterTest.java Index: SVGConverterTest.java =================================================================== RCS file: /home/cvs/xml-batik/test-sources/org/apache/batik/apps/rasterizer/SVGConverterTest.java,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- SVGConverterTest.java 2001/10/31 08:33:00 1.8 +++ SVGConverterTest.java 2001/10/31 09:54:26 1.9 @@ -24,7 +24,7 @@ * of source and destination sources. * * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: SVGConverterTest.java,v 1.8 2001/10/31 08:33:00 tkormann Exp $ + * @version $Id: SVGConverterTest.java,v 1.9 2001/10/31 09:54:26 vhardy Exp $ */ public class SVGConverterTest extends DefaultTestSuite { public SVGConverterTest(){ @@ -122,6 +122,33 @@ }; addTest(t); t.setId("HintsConfigTest.KEY_ALTERNATE_STYLESHEET"); + + t = new HintsConfigTest(new Object[][]{ + {ImageTranscoder.KEY_USER_STYLESHEET_URI, "userStylesheet.css"}}){ + protected void deltaConfigure(SVGConverter c){ + c.setUserStylesheet("userStylesheet.css"); + } + }; + addTest(t); + t.setId("HintsConfigTest.KEY_USER_STYLESHEET_URI"); + + t = new HintsConfigTest(new Object[][]{ + {ImageTranscoder.KEY_LANGUAGE, "fr"}}){ + protected void deltaConfigure(SVGConverter c){ + c.setLanguage("fr"); + } + }; + addTest(t); + t.setId("HintsConfigTest.KEY_LANGUAGE"); + + t = new HintsConfigTest(new Object[][]{ + {ImageTranscoder.KEY_PIXEL_TO_MM, new Float(.5f)}}){ + protected void deltaConfigure(SVGConverter c){ + c.setPixelToMillimeter(.5f); + } + }; + addTest(t); + t.setId("HintsConfigTest.KEY_PIXEL_TO_MM"); t = new HintsConfigTest(new Object[][]{ {ImageTranscoder.KEY_XML_PARSER_VALIDATING, new Boolean(true)}}){ 1.8 +6 -2 xml-batik/test-sources/org/apache/batik/test/AbstractTest.java Index: AbstractTest.java =================================================================== RCS file: /home/cvs/xml-batik/test-sources/org/apache/batik/test/AbstractTest.java,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- AbstractTest.java 2001/10/19 09:22:17 1.7 +++ AbstractTest.java 2001/10/31 09:54:26 1.8 @@ -70,7 +70,7 @@ * </code> * * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: AbstractTest.java,v 1.7 2001/10/19 09:22:17 vhardy Exp $ + * @version $Id: AbstractTest.java,v 1.8 2001/10/31 09:54:26 vhardy Exp $ */ public abstract class AbstractTest implements Test { /** @@ -105,7 +105,11 @@ */ public String getName(){ if(name == null){ - return getClass().getName(); + if (id != null && !"".equals(id)){ + return id; + } else { + return getClass().getName(); + } } return name;
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]