/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* $Id$ */ package org.apache.fop.afp; import java.awt.Point; import java.io.IOException; import java.io.ObjectInputStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.java2d.color.ColorConverter; import org.apache.xmlgraphics.java2d.color.DefaultColorConverter; import org.apache.fop.afp.fonts.AFPPageFonts; import org.apache.fop.util.AbstractPaintingState; /** * This keeps information about the current painting state when writing to an * AFP datastream. */ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState { private static final long serialVersionUID = 8206711712452344473L; private static Log log = LogFactory.getLog("org.apache.xmlgraphics.afp"); /** the portrait rotation */ private int portraitRotation; /** the landscape rotation */ private int landscapeRotation = 270; /** color image support */ private boolean colorImages; /** dithering quality setting (0.0f..1.0f) */ private float ditheringQuality; /** image encoding quality setting (0.0f..1.0f) */ private float bitmapEncodingQuality; /** color image handler */ private transient ColorConverter colorConverter; /** * true if certain image formats may be embedded unchanged in their native * format. */ private boolean nativeImagesSupported; private boolean canEmbedJpeg; /** * true if CMYK images (requires IOCA FS45 suppport on the target platform) * may be generated */ private boolean cmykImagesSupported; /** default value for image depth */ private int bitsPerPixel = 8; /** the output resolution */ private int resolution = 240; // 240 dpi /** * A configurable value to correct the line width so that the output matches the expected. Different * devices may need different values. */ private float lineWidthCorrection = AFPConstants.LINE_WIDTH_CORRECTION; /** determines whether GOCA is enabled or disabled */ private boolean gocaEnabled = true; /** determines whether to stroke text in GOCA mode or to use text operators where possible */ private boolean strokeGocaText; /** use page segment with F11 and F45 images*/ private boolean pSeg; /** use FS45 images*/ private boolean fs45; /** the current page */ private transient AFPPagePaintingState pagePaintingState; // /** reference orientation */ // private int orientation = 0; /** a unit converter */ private final transient AFPUnitConverter unitConv; public AFPPaintingState() { colorConverter = GrayScaleColorConverter.getInstance(); pagePaintingState = new AFPPagePaintingState(); unitConv = new AFPUnitConverter(this); } private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { ois.defaultReadObject(); } /** * Sets the rotation to be used for portrait pages, valid values are 0 * (default), 90, 180, 270. * * @param rotation * The rotation in degrees. */ public void setPortraitRotation(int rotation) { if (rotation == 0 || rotation == 90 || rotation == 180 || rotation == 270) { portraitRotation = rotation; } else { throw new IllegalArgumentException("The portrait rotation must be one" + " of the values 0, 90, 180, 270"); } } /** * Returns the rotation to be used for portrait pages * * @return the rotation to be used for portrait pages */ protected int getPortraitRotation() { return this.portraitRotation; } /** * Sets the rotation to be used for landscape pages, valid values are 0, 90, * 180, 270 (default). * * @param rotation * The rotation in degrees. */ public void setLandscapeRotation(int rotation) { if (rotation == 0 || rotation == 90 || rotation == 180 || rotation == 270) { landscapeRotation = rotation; } else { throw new IllegalArgumentException("The landscape rotation must be one" + " of the values 0, 90, 180, 270"); } } /** * Returns the landscape rotation * * @return the landscape rotation */ protected int getLandscapeRotation() { return this.landscapeRotation; } /** * Sets the number of bits used per pixel * * @param bitsPerPixel * number of bits per pixel */ public void setBitsPerPixel(int bitsPerPixel) { switch (bitsPerPixel) { case 1: case 4: case 8: this.bitsPerPixel = bitsPerPixel; break; default: log.warn("Invalid bits_per_pixel value, must be 1, 4 or 8."); this.bitsPerPixel = 8; break; } } /** * Returns the number of bits per pixel * * @return the number of bits per pixel */ public int getBitsPerPixel() { return this.bitsPerPixel; } /** * Sets whether images are color or not and instantiates a ColorHandler * * @param colorImages * color image output */ public void setColorImages(boolean colorImages) { this.colorImages = colorImages; if (colorImages) { this.colorConverter = DefaultColorConverter.getInstance(); } } /** * Returns true if color images are to be used * * @return true if color images are to be used */ public boolean isColorImages() { return this.colorImages; } /** * Used to convert color in respect of the colorImages flag * * @return the color converter */ public ColorConverter getColorConverter() { return this.colorConverter; } /** * Sets whether images are natively supported or not in the AFP environment * * @param nativeImagesSupported * true if images are natively supported in this AFP environment */ public void setNativeImagesSupported(boolean nativeImagesSupported) { this.nativeImagesSupported = nativeImagesSupported; } /** * Returns true if images are supported natively in this AFP environment * * @return true if images are supported natively in this AFP environment */ public boolean isNativeImagesSupported() { return this.nativeImagesSupported; } /** * Set whether or not JPEG images can be embedded within an AFP document. * * @param canEmbed true if the JPEG image can be embedded */ public void setCanEmbedJpeg(boolean canEmbed) { canEmbedJpeg = canEmbed; } /** * Returns true if JPEGs can be embedded in an AFP document. * * @return true if JPEG embedding is allowed */ public boolean canEmbedJpeg() { return canEmbedJpeg; } /** * Controls whether CMYK images (IOCA FS45) are enabled. By default, support * is disabled for wider compatibility. When disabled, any CMYK image is * converted to the selected color format. * * @param value * true to enabled CMYK images */ public void setCMYKImagesSupported(boolean value) { this.cmykImagesSupported = value; } /** * Indicates whether CMYK images (IOCA FS45) are enabled. * * @return true if IOCA FS45 is enabled */ public boolean isCMYKImagesSupported() { return this.cmykImagesSupported; } /** * Gets the dithering quality setting to use when converting images to monochrome images. * @return the dithering quality (a value between 0.0f and 1.0f) */ public float getDitheringQuality() { return this.ditheringQuality; } /** * Sets the dithering quality setting to use when converting images to monochrome images. * @param quality Defines the desired quality level for the conversion. * Valid values: a value between 0.0f (fastest) and 1.0f (best) */ public void setDitheringQuality(float quality) { quality = Math.max(quality, 0.0f); quality = Math.min(quality, 1.0f); this.ditheringQuality = quality; } /** * Gets the image encoding quality setting to use when encoding bitmap images. * @return the encoding quality (a value between 0.0f and 1.0f, 1.0 meaning loss-less) */ public float getBitmapEncodingQuality() { return this.bitmapEncodingQuality; } /** * Sets the image encoding quality setting to use when encoding bitmap images. * @param quality Defines the desired quality level for the conversion. * Valid values: a value between 0.0f (lowest) and 1.0f (best, loss-less) */ public void setBitmapEncodingQuality(float quality) { quality = Math.max(quality, 0.0f); quality = Math.min(quality, 1.0f); this.bitmapEncodingQuality = quality; } /** * Sets the output/device resolution * * @param resolution * the output resolution (dpi) */ public void setResolution(int resolution) { if (log.isDebugEnabled()) { log.debug("renderer-resolution set to: " + resolution + "dpi"); } this.resolution = resolution; } /** * Sets the line width correction * * @param correction the line width multiplying factor correction */ public void setLineWidthCorrection(float correction) { if (log.isDebugEnabled()) { log.debug("line width correction set to: " + correction); } this.lineWidthCorrection = correction; } /** * Returns the output/device resolution. * * @return the resolution in dpi */ public int getResolution() { return this.resolution; } /** * Returns the line width correction. * @return the correction */ public float getLineWidthCorrection() { return this.lineWidthCorrection; } /** * Controls whether GOCA is enabled or disabled. * @param enabled true if GOCA is enabled, false if it is disabled */ public void setGOCAEnabled(boolean enabled) { this.gocaEnabled = enabled; } /** * Indicates whether GOCA is enabled or disabled. * @return true if GOCA is enabled, false if GOCA is disabled */ public boolean isGOCAEnabled() { return this.gocaEnabled; } /** * Controls whether to stroke text in GOCA mode or to use text operators where possible. * @param stroke true to stroke, false to paint with text operators where possible */ public void setStrokeGOCAText(boolean stroke) { this.strokeGocaText = stroke; } /** * Indicates whether to stroke text in GOCA mode or to use text operators where possible. * @return true to stroke, false to paint with text operators where possible */ public boolean isStrokeGOCAText() { return this.strokeGocaText; } /** * Whether FS11 and SF45 non-inline images should be wrapped in a page segment * @return true iff images should be wrapped */ public boolean getWrapPSeg() { return pSeg; } /** * Sets whether FS11 and FS45 non-inline images should be wrapped in a page segment * @param pSeg true iff images should be wrapped */ public void setWrapPSeg(boolean pSeg) { this.pSeg = pSeg; } /** * gets whether images should be FS45 * @return true iff images should be FS45 */ public boolean getFS45() { return fs45; } /** * sets whether images should be FS45 * @param fs45 true iff images should be FS45 */ public void setFS45(boolean fs45) { this.fs45 = fs45; } /** {@inheritDoc} */ @Override protected AbstractData instantiateData() { return new AFPData(); } /** {@inheritDoc} */ @Override protected AbstractPaintingState instantiate() { return new AFPPaintingState(); } /** * Returns the painting state of the current page * * @return the painting state of the current page */ protected AFPPagePaintingState getPagePaintingState() { return this.pagePaintingState; } /** * Gets the current page fonts * * @return the current page fonts */ public AFPPageFonts getPageFonts() { return pagePaintingState.getFonts(); } /** * Sets the page width * * @param pageWidth * the page width */ public void setPageWidth(int pageWidth) { pagePaintingState.setWidth(pageWidth); } /** * Returns the page width * * @return the page width */ public int getPageWidth() { return pagePaintingState.getWidth(); } /** * Sets the page height * * @param pageHeight * the page height */ public void setPageHeight(int pageHeight) { pagePaintingState.setHeight(pageHeight); } /** * Returns the page height * * @return the page height */ public int getPageHeight() { return pagePaintingState.getHeight(); } /** * Returns the page rotation * * @return the page rotation */ public int getPageRotation() { return pagePaintingState.getOrientation(); } /** * Sets the uri of the current image * * @param uri * the uri of the current image */ public void setImageUri(String uri) { ((AFPData) getData()).imageUri = uri; } /** * Gets the uri of the current image * * @return the uri of the current image */ public String getImageUri() { return ((AFPData) getData()).imageUri; } /** * Returns the currently derived rotation * * @return the currently derived rotation */ public int getRotation() { return getData().getDerivedRotation(); } /** * Returns the unit converter * * @return the unit converter */ public AFPUnitConverter getUnitConverter() { return this.unitConv; } /** * Returns a point on the current page, taking the current painting state * into account. * * @param x * the X-coordinate * @param y * the Y-coordinate * @return a point on the current page */ public Point getPoint(int x, int y) { Point p = new Point(); int rotation = getRotation(); switch (rotation) { case 90: p.x = y; p.y = getPageWidth() - x; break; case 180: p.x = getPageWidth() - x; p.y = getPageHeight() - y; break; case 270: p.x = getPageHeight() - y; p.y = x; break; default: p.x = x; p.y = y; break; } return p; } /** {@inheritDoc} */ @Override public Object clone() { AFPPaintingState paintingState = (AFPPaintingState) super.clone(); paintingState.pagePaintingState = (AFPPagePaintingState) this.pagePaintingState.clone(); paintingState.portraitRotation = this.portraitRotation; paintingState.landscapeRotation = this.landscapeRotation; paintingState.bitsPerPixel = this.bitsPerPixel; paintingState.colorImages = this.colorImages; paintingState.colorConverter = this.colorConverter; paintingState.resolution = this.resolution; return paintingState; } /** {@inheritDoc} */ @Override public String toString() { return "AFPPaintingState{" + "portraitRotation=" + portraitRotation + ", landscapeRotation=" + landscapeRotation + ", colorImages=" + colorImages + ", bitsPerPixel=" + bitsPerPixel + ", resolution=" + resolution + ", pageState=" + pagePaintingState + super.toString() + "}"; } /** * Page level state data */ private class AFPPagePaintingState implements Cloneable { /** page width */ private int width; /** page height */ private int height; /** page fonts */ private AFPPageFonts fonts = new AFPPageFonts(); /** page font count */ private int fontCount; /** page orientation */ private int orientation; /** * Returns the page width * * @return the page width */ protected int getWidth() { return width; } /** * Sets the page width * * @param width * the page width */ protected void setWidth(int width) { this.width = width; } /** * Returns the page height * * @return the page height */ protected int getHeight() { return height; } /** * Sets the page height * * @param height * the page height */ protected void setHeight(int height) { this.height = height; } /** * Returns the page fonts * * @return the page fonts */ protected AFPPageFonts getFonts() { return fonts; } /** * Sets the current page fonts * * @param fonts * the current page fonts */ protected void setFonts(AFPPageFonts fonts) { this.fonts = fonts; } /** * Increments and returns the current page font count * * @return increment and return the current page font count */ protected int incrementFontCount() { return ++fontCount; } /** * Returns the current page orientation * * @return the current page orientation */ protected int getOrientation() { return orientation; } /** * Sets the current page orientation * * @param orientation * the current page orientation */ protected void setOrientation(int orientation) { this.orientation = orientation; } /** {@inheritDoc} */ @Override public Object clone() { AFPPagePaintingState state = new AFPPagePaintingState(); state.width = this.width; state.height = this.height; state.orientation = this.orientation; state.fonts = new AFPPageFonts(this.fonts); state.fontCount = this.fontCount; return state; } /** {@inheritDoc} */ @Override public String toString() { return "AFPPagePaintingState{width=" + width + ", height=" + height + ", orientation=" + orientation + ", fonts=" + fonts + ", fontCount=" + fontCount + "}"; } } /** * Block level state data */ // @SuppressFBWarnings("SE_INNER_CLASS") private class AFPData extends org.apache.fop.util.AbstractPaintingState.AbstractData { private static final long serialVersionUID = -1789481244175275686L; /** The current fill status */ private boolean filled; private String imageUri; /** {@inheritDoc} */ @Override public Object clone() { AFPData obj = (AFPData) super.clone(); obj.filled = this.filled; obj.imageUri = this.imageUri; return obj; } /** {@inheritDoc} */ @Override public String toString() { return "AFPData{" + super.toString() + ", filled=" + filled + ", imageUri=" + imageUri + "}"; } /** {@inheritDoc} */ @Override protected AbstractData instantiate() { return new AFPData(); } } }