blob: 74506aa7ea5a11a06756b9e04041a66777ecf061 [file] [log] [blame]
/*
* 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 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 implements
Cloneable {
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 ColorConverter colorConverter = GrayScaleColorConverter.getInstance();
/**
* 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 = new AFPPagePaintingState();
// /** reference orientation */
// private int orientation = 0;
/** a unit converter */
private final transient AFPUnitConverter unitConv = new AFPUnitConverter(this);
/**
* 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
*/
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();
}
}
}