org.apache.fop.render.ps.PSImageHandlerRawJPEG\r
org.apache.fop.render.ps.PSImageHandlerGraphics2D\r
org.apache.fop.render.ps.PSImageHandlerSVG\r
+org.apache.fop.render.afp.AFPImageHandlerRenderedImage\r
+org.apache.fop.render.afp.AFPImageHandlerGraphics2D\r
+org.apache.fop.render.afp.AFPImageHandlerRawStream\r
+org.apache.fop.render.afp.AFPImageHandlerRawCCITTFax\r
+org.apache.fop.render.afp.AFPImageHandlerSVG
\ No newline at end of file
org.apache.fop.render.pdf.PDFDocumentHandlerMaker\r
org.apache.fop.render.pcl.PCLDocumentHandlerMaker\r
org.apache.fop.render.bitmap.TIFFDocumentHandlerMaker\r
-org.apache.fop.render.ps.PSDocumentHandlerMaker
\ No newline at end of file
+org.apache.fop.render.ps.PSDocumentHandlerMaker\r
+org.apache.fop.render.afp.AFPDocumentHandlerMaker
\ No newline at end of file
* page has a set of data objects associated with it. Each page within a
* document is independent from any other page, and each must establish its own
* environment parameters.
- *
+ * <p>
* The page is the level in the document component hierarchy that is used for
* printing or displaying a document's content. The data objects contained in
* the page envelope in the data stream are presented when the page is
* directs the placement and orientation of the data on the page. In addition,
* each page contains layout information that specifies the measurement units,
* page width, and page depth.
- *
+ * <p>
* A page is initiated by a begin page structured field and terminated by an end
* page structured field. Structured fields that define objects and active
* environment groups or that specify attributes of the page may be encountered
* in page state.
- *
*/
public abstract class AbstractPageObject extends AbstractNamedAFPObject implements Completable {
*
* @return the presentation text object
*/
- private PresentationTextObject getPresentationTextObject() {
+ public PresentationTextObject getPresentationTextObject() {
if (currentPresentationTextObject == null) {
PresentationTextObject presentationTextObject
= factory.createPresentationTextObject();
package org.apache.fop.afp.modca;
-import java.awt.Color;
import java.io.IOException;
import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
import org.apache.commons.io.output.ByteArrayOutputStream;
-import org.apache.fop.afp.AFPLineDataInfo;
-import org.apache.fop.afp.AFPTextDataInfo;
+import org.apache.fop.afp.ptoca.PtocaBuilder;
+import org.apache.fop.afp.ptoca.PtocaConstants;
import org.apache.fop.afp.util.BinaryUtils;
/**
* that position them - modal control sequences that adjust the positions by
* small amounts - other functions causing text to be presented with differences
* in appearance.
- *
+ * <p>
* The graphic characters are expected to conform to a coded font representation
* so that they can be translated from the code point in the object data to the
* character in the coded font. The units of measure for linear displacements
* are derived from the PresentationTextDescriptor or from the hierarchical
* defaults.
- *
+ * <p>
* In addition to graphic character code points, Presentation Text data can
* contain embedded control sequences. These are strings of two or more bytes
* which signal an alternate mode of processing for the content of the current
* Presentation Text data.
- *
+ * <p>
+ * The content for this object can be created using {@link PtocaBuilder}.
*/
-public class PresentationTextData extends AbstractAFPObject {
+public class PresentationTextData extends AbstractAFPObject implements PtocaConstants {
/** the maximum size of the presentation text data.*/
private static final int MAX_SIZE = 8192;
/** the AFP data relating to this presentation text data. */
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
- /** the current x coordinate. */
- private int currentX = -1;
-
- /** the current y cooridnate */
- private int currentY = -1;
-
- /** the current font */
- private String currentFont = "";
-
- /** the current orientation */
- private int currentOrientation = 0;
-
- /** the current color */
- private Color currentColor = new Color(0, 0, 0);
-
- /** the current variable space increment */
- private int currentVariableSpaceCharacterIncrement = 0;
-
- /** the current inter character adjustment */
- private int currentInterCharacterAdjustment = 0;
-
/**
* Default constructor for the PresentationTextData.
*/
this(false);
}
+ private static final int HEADER_LENGTH = 9;
+
/**
* Constructor for the PresentationTextData, the boolean flag indicate
* whether the control sequence prefix should be set to indicate the start
0x00, // Reserved
0x00, // Reserved
};
- baos.write(data, 0, 9);
+ baos.write(data, 0, HEADER_LENGTH);
if (controlInd) {
baos.write(new byte[] {0x2B, (byte) 0xD3}, 0, 2);
}
/**
- * The Set Coded Font Local control sequence activates a coded font and
- * specifies the character attributes to be used. This is a modal control
- * sequence.
- *
- * @param font
- * The font local identifier.
- * @param afpdata
- * The output stream to which data should be written.
- */
- private void setCodedFont(byte font, ByteArrayOutputStream afpdata) {
- // Avoid unnecessary specification of the font
- if (String.valueOf(font).equals(currentFont)) {
- return;
- } else {
- currentFont = String.valueOf(font);
- }
-
- afpdata.write(new byte[] {0x03, (byte) 0xF1, font}, 0, 3);
- }
-
- /**
- * Establishes the current presentation position on the baseline at a new
- * I-axis coordinate, which is a specified number of measurement units from
- * the B-axis. There is no change to the current B-axis coordinate.
- *
- * @param coordinate
- * The coordinate for the inline move.
- * @param afpdata
- * The output stream to which data should be written.
- */
- private void absoluteMoveInline(int coordinate,
- ByteArrayOutputStream afpdata) {
- byte[] b = BinaryUtils.convert(coordinate, 2);
- afpdata.write(new byte[] {0x04, (byte) 0xC7, b[0], b[1]}, 0, 4);
- currentX = coordinate;
- }
-
- /**
- * Establishes the baseline and the current presentation position at a new
- * B-axis coordinate, which is a specified number of measurement units from
- * the I-axis. There is no change to the current I-axis coordinate.
- *
- * @param coordinate
- * The coordinate for the baseline move.
- * @param afpdata
- * The output stream to which data should be written.
- */
- private void absoluteMoveBaseline(int coordinate,
- ByteArrayOutputStream afpdata) {
- byte[] b = BinaryUtils.convert(coordinate, 2);
- afpdata.write(new byte[] {0x04, (byte) 0xD3, b[0], b[1]}, 0, 4);
- currentY = coordinate;
- }
-
- private static final int TRANSPARENT_MAX_SIZE = 253;
-
- /**
- * The Transparent Data control sequence contains a sequence of code points
- * that are presented without a scan for embedded control sequences.
- *
- * @param data
- * The text data to add.
- * @param afpdata
- * The output stream to which data should be written.
- */
- private void addTransparentData(byte[] data, ByteArrayOutputStream afpdata) {
- // Calculate the length
- int l = data.length + 2;
- if (l > 255) {
- // Check that we are not exceeding the maximum length
- throw new IllegalArgumentException(
- "Transparent data is longer than " + TRANSPARENT_MAX_SIZE + " bytes: " + data);
- }
- afpdata.write(new byte[] {BinaryUtils.convert(l)[0], (byte) 0xDB},
- 0, 2);
- afpdata.write(data, 0, data.length);
- }
-
- /**
- * Draws a line of specified length and specified width in the B-direction
- * from the current presentation position. The location of the current
- * presentation position is unchanged.
- *
- * @param length
- * The length of the rule.
- * @param width
- * The width of the rule.
- * @param afpdata
- * The output stream to which data should be written.
+ * Returns the number of data bytes still available in this object until it is full and a new
+ * one has to be started.
+ * @return the number of data bytes available
*/
- private void drawBaxisRule(int length, int width,
- ByteArrayOutputStream afpdata) {
- afpdata.write(new byte[] {
- 0x07, // Length
- (byte) 0xE7, // Type
- }, 0, 2);
- // Rule length
- byte[] data1 = BinaryUtils.shortToByteArray((short) length);
- afpdata.write(data1, 0, data1.length);
- // Rule width
- byte[] data2 = BinaryUtils.shortToByteArray((short) width);
- afpdata.write(data2, 0, data2.length);
- // Rule width fraction
- afpdata.write(0x00);
+ public int getBytesAvailable() {
+ return MAX_SIZE - baos.size() + HEADER_LENGTH;
}
/**
- * Draws a line of specified length and specified width in the I-direction
- * from the current presentation position. The location of the current
- * presentation position is unchanged.
- *
- * @param length
- * The length of the rule.
- * @param width
- * The width of the rule.
- * @param afpdata
- * The output stream to which data should be written.
+ * Returns the output stream the content data is written to.
+ * @return the output stream
*/
- private void drawIaxisRule(int length, int width,
- ByteArrayOutputStream afpdata) {
- afpdata.write(new byte[] {
- 0x07, // Length
- (byte) 0xE5, // Type
- }, 0, 2);
- // Rule length
- byte[] data1 = BinaryUtils.shortToByteArray((short) length);
- afpdata.write(data1, 0, data1.length);
- // Rule width
- byte[] data2 = BinaryUtils.shortToByteArray((short) width);
- afpdata.write(data2, 0, data2.length);
- // Rule width fraction
- afpdata.write(0x00);
- }
-
- /**
- * Create the presentation text data for the byte array of data.
- *
- * @param textDataInfo
- * the afp text data
- * @throws MaximumSizeExceededException
- * thrown if the maximum number of text data is exceeded
- * @throws UnsupportedEncodingException
- * thrown if character encoding is not supported
- */
- public void createTextData(AFPTextDataInfo textDataInfo)
- throws MaximumSizeExceededException, UnsupportedEncodingException {
-
- ByteArrayOutputStream afpdata = new ByteArrayOutputStream();
-
- int rotation = textDataInfo.getRotation();
- if (currentOrientation != rotation) {
- setTextOrientation(rotation, afpdata);
- currentOrientation = rotation;
- currentX = -1;
- currentY = -1;
- }
-
- // Avoid unnecessary specification of the Y coordinate
- int y = textDataInfo.getY();
- if (currentY != y) {
- absoluteMoveBaseline(y, afpdata);
- currentX = -1;
- }
-
- // Avoid unnecessary specification of the X coordinate
- int x = textDataInfo.getX();
- if (currentX != x) {
- absoluteMoveInline(x, afpdata);
- }
-
- // Avoid unnecessary specification of the variable space increment
- if (textDataInfo.getVariableSpaceCharacterIncrement()
- != currentVariableSpaceCharacterIncrement) {
- setVariableSpaceCharacterIncrement(textDataInfo
- .getVariableSpaceCharacterIncrement(), afpdata);
- currentVariableSpaceCharacterIncrement = textDataInfo
- .getVariableSpaceCharacterIncrement();
- }
-
- // Avoid unnecessary specification of the inter character adjustment
- if (textDataInfo.getInterCharacterAdjustment() != currentInterCharacterAdjustment) {
- setInterCharacterAdjustment(textDataInfo.getInterCharacterAdjustment(),
- afpdata);
- currentInterCharacterAdjustment = textDataInfo
- .getInterCharacterAdjustment();
- }
-
- // Avoid unnecessary specification of the text color
- if (!textDataInfo.getColor().equals(currentColor)) {
- setExtendedTextColor(textDataInfo.getColor(), afpdata);
- currentColor = textDataInfo.getColor();
- }
-
- setCodedFont(BinaryUtils.convert(textDataInfo.getFontReference())[0],
- afpdata);
-
- // Add transparent data
- String textString = textDataInfo.getString();
- String encoding = textDataInfo.getEncoding();
- byte[] data = textString.getBytes(encoding);
- if (data.length <= TRANSPARENT_MAX_SIZE) {
- addTransparentData(data, afpdata);
- } else {
- // data size greater than TRANSPARENT_MAX_SIZE so slice
- int numTransData = data.length / TRANSPARENT_MAX_SIZE;
- byte[] buff = new byte[TRANSPARENT_MAX_SIZE];
- int currIndex = 0;
- for (int transDataCnt = 0; transDataCnt < numTransData; transDataCnt++) {
- System.arraycopy(data, currIndex, buff, 0, TRANSPARENT_MAX_SIZE);
- addTransparentData(buff, afpdata);
- currIndex += TRANSPARENT_MAX_SIZE;
- }
- int left = data.length - currIndex;
- buff = new byte[left];
- System.arraycopy(data, currIndex, buff, 0, left);
- addTransparentData(buff, afpdata);
- }
- currentX = -1;
-
- int dataSize = afpdata.size();
-
- if (baos.size() + dataSize > MAX_SIZE) {
- currentX = -1;
- currentY = -1;
- throw new MaximumSizeExceededException();
- }
-
- byte[] outputdata = afpdata.toByteArray();
- baos.write(outputdata, 0, outputdata.length);
- }
-
- private int ensurePositive(int value) {
- if (value < 0) {
- return 0;
- }
- return value;
- }
-
- /**
- * Drawing of lines using the starting and ending coordinates, thickness and
- * colour arguments.
- *
- * @param lineDataInfo the line data information.
- * @throws MaximumSizeExceededException
- * thrown if the maximum number of line data has been exceeded
- */
- public void createLineData(AFPLineDataInfo lineDataInfo) throws MaximumSizeExceededException {
-
- ByteArrayOutputStream afpdata = new ByteArrayOutputStream();
-
- int orientation = lineDataInfo.getRotation();
- if (currentOrientation != orientation) {
- setTextOrientation(orientation, afpdata);
- currentOrientation = orientation;
- }
-
- // Avoid unnecessary specification of the Y coordinate
- int y1 = ensurePositive(lineDataInfo.getY1());
- if (y1 != currentY) {
- absoluteMoveBaseline(y1, afpdata);
- }
-
- // Avoid unnecessary specification of the X coordinate
- int x1 = ensurePositive(lineDataInfo.getX1());
- if (x1 != currentX) {
- absoluteMoveInline(x1, afpdata);
- }
-
- Color color = lineDataInfo.getColor();
- if (!color.equals(currentColor)) {
- setExtendedTextColor(color, afpdata);
- currentColor = color;
- }
-
- int x2 = ensurePositive(lineDataInfo.getX2());
- int y2 = ensurePositive(lineDataInfo.getY2());
- int thickness = lineDataInfo.getThickness();
- if (y1 == y2) {
- drawIaxisRule(x2 - x1, thickness, afpdata);
- } else if (x1 == x2) {
- drawBaxisRule(y2 - y1, thickness, afpdata);
- } else {
- log.error("Invalid axis rule unable to draw line");
- return;
- }
-
- int dataSize = afpdata.size();
-
- if (baos.size() + dataSize > MAX_SIZE) {
- currentX = -1;
- currentY = -1;
- throw new MaximumSizeExceededException();
- }
-
- byte[] outputdata = afpdata.toByteArray();
- baos.write(outputdata, 0, outputdata.length);
- }
-
- /**
- * The Set Text Orientation control sequence establishes the I-direction and
- * B-direction for the subsequent text. This is a modal control sequence.
- *
- * Semantics: This control sequence specifies the I-axis and B-axis
- * orientations with respect to the Xp-axis for the current Presentation
- * Text object. The orientations are rotational values expressed in degrees
- * and minutes.
- *
- * @param orientation
- * The text orientation (0, 90, 180, 270).
- * @param os
- * The output stream to which data should be written.
- */
- private void setTextOrientation(int orientation,
- ByteArrayOutputStream os) {
- os.write(new byte[] {0x06, (byte) 0xF7, }, 0, 2);
- switch (orientation) {
- case 90:
- os.write(0x2D);
- os.write(0x00);
- os.write(0x5A);
- os.write(0x00);
- break;
- case 180:
- os.write(0x5A);
- os.write(0x00);
- os.write(0x87);
- os.write(0x00);
- break;
- case 270:
- os.write(0x87);
- os.write(0x00);
- os.write(0x00);
- os.write(0x00);
- break;
- default:
- os.write(0x00);
- os.write(0x00);
- os.write(0x2D);
- os.write(0x00);
- break;
- }
- }
-
- /**
- * The Set Extended Text Color control sequence specifies a color value and
- * defines the color space and encoding for that value. The specified color
- * value is applied to foreground areas of the text presentation space. This
- * is a modal control sequence.
- *
- * @param col
- * The color to be set.
- * @param os
- * The output stream to which data should be written.
- */
- private void setExtendedTextColor(Color col, ByteArrayOutputStream os) {
- byte[] colorData = new byte[] {
- 15, // Control sequence length
- (byte) 0x81, // Control sequence function type
- 0x00, // Reserved; must be zero
- 0x01, // Color space - 0x01 = RGB
- 0x00, // Reserved; must be zero
- 0x00, // Reserved; must be zero
- 0x00, // Reserved; must be zero
- 0x00, // Reserved; must be zero
- 8, // Number of bits in component 1
- 8, // Number of bits in component 2
- 8, // Number of bits in component 3
- 0, // Number of bits in component 4
- (byte) (col.getRed()), // Red intensity
- (byte) (col.getGreen()), // Green intensity
- (byte) (col.getBlue()), // Blue intensity
- };
-
- os.write(colorData, 0, colorData.length);
- }
-
- /**
- * //TODO This is a modal control sequence.
- *
- * @param incr
- * The increment to be set.
- * @param os
- * The output stream to which data should be written.
- */
- private void setVariableSpaceCharacterIncrement(int incr,
- ByteArrayOutputStream os) {
- byte[] b = BinaryUtils.convert(incr, 2);
-
- os.write(new byte[] {
- 4, // Control sequence length
- (byte) 0xC5, // Control sequence function type
- b[0], b[1] },
- 0, 4);
- }
-
- /**
- * //TODO This is a modal control sequence.
- *
- * @param incr
- * The increment to be set.
- * @param os
- * The output stream to which data should be written.
- */
- private void setInterCharacterAdjustment(int incr, ByteArrayOutputStream os) {
- byte[] b = BinaryUtils.convert(Math.abs(incr), 2);
- os.write(new byte[] {
- 5, // Control sequence length
- (byte) 0xC3, // Control sequence function type
- b[0], b[1], (byte) (incr >= 0 ? 0 : 1) // Direction
- }, 0, 5);
+ protected OutputStream getOutputStream() {
+ return this.baos;
}
/** {@inheritDoc} */
public void writeToStream(OutputStream os) throws IOException {
+ assert getBytesAvailable() >= 0;
byte[] data = baos.toByteArray();
byte[] size = BinaryUtils.convert(data.length - 1, 2);
data[1] = size[0];
os.write(data);
}
- /**
- * A control sequence is a sequence of bytes that specifies a control
- * function. A control sequence consists of a control sequence introducer
- * and zero or more parameters. The control sequence can extend multiple
- * presentation text data objects, but must eventually be terminated. This
- * method terminates the control sequence.
- *
- * @throws MaximumSizeExceededException
- * thrown in the event that maximum size has been exceeded
- */
- public void endControlSequence() throws MaximumSizeExceededException {
- byte[] data = new byte[2];
- data[0] = 0x02;
- data[1] = (byte) 0xF8;
- if (data.length + baos.size() > MAX_SIZE) {
- throw new MaximumSizeExceededException();
- }
- baos.write(data, 0, data.length);
- }
}
\ No newline at end of file
import org.apache.fop.afp.AFPLineDataInfo;
import org.apache.fop.afp.AFPTextDataInfo;
+import org.apache.fop.afp.ptoca.LineDataInfoProducer;
+import org.apache.fop.afp.ptoca.PtocaBuilder;
+import org.apache.fop.afp.ptoca.PtocaProducer;
+import org.apache.fop.afp.ptoca.TextDataInfoProducer;
/**
* The Presentation Text object is the data object used in document processing
* collection of the graphic characters and control codes is called Presentation
* Text, and the object that contains the Presentation Text is called the
* PresentationText object.
+ * <p>
+ * The content for this object can be created using {@link PtocaBuilder}.
*/
public class PresentationTextObject extends AbstractNamedAFPObject {
*/
private List/*<PresentationTextData>*/ presentationTextDataList = null;
+ private PtocaBuilder builder = new DefaultBuilder();
+
/**
* Construct a new PresentationTextObject for the specified name argument,
* the name should be an 8 character identifier.
* @throws UnsupportedEncodingException thrown if character encoding is not supported
*/
public void createTextData(AFPTextDataInfo textDataInfo) throws UnsupportedEncodingException {
+ createControlSequences(new TextDataInfoProducer(textDataInfo));
+ }
+
+ /**
+ * Creates a chain of control sequences using a producer.
+ * @param producer the producer
+ * @throws UnsupportedEncodingException thrown if character encoding is not supported
+ */
+ public void createControlSequences(PtocaProducer producer)
+ throws UnsupportedEncodingException {
if (currentPresentationTextData == null) {
startPresentationTextData();
}
try {
- currentPresentationTextData.createTextData(textDataInfo);
- } catch (MaximumSizeExceededException msee) {
- endPresentationTextData();
- createTextData(textDataInfo);
+ producer.produce(builder);
} catch (UnsupportedEncodingException e) {
endPresentationTextData();
throw e;
+ } catch (IOException ioe) {
+ endPresentationTextData();
+ handleUnexpectedIOError(ioe);
+ }
+ }
+
+ private class DefaultBuilder extends PtocaBuilder {
+ protected OutputStream getOutputStreamForControlSequence(int length) {
+ if (length > currentPresentationTextData.getBytesAvailable()) {
+ endPresentationTextData();
+ startPresentationTextData();
+ }
+ return currentPresentationTextData.getOutputStream();
}
}
* @param lineDataInfo the line data information.
*/
public void createLineData(AFPLineDataInfo lineDataInfo) {
- if (currentPresentationTextData == null) {
- startPresentationTextData();
- }
try {
- currentPresentationTextData.createLineData(lineDataInfo);
- } catch (MaximumSizeExceededException msee) {
- endPresentationTextData();
- createLineData(lineDataInfo);
+ createControlSequences(new LineDataInfoProducer(lineDataInfo));
+ } catch (UnsupportedEncodingException e) {
+ handleUnexpectedIOError(e); //Won't happen for lines
}
}
startPresentationTextData();
}
try {
- currentPresentationTextData.endControlSequence();
- } catch (MaximumSizeExceededException msee) {
+ builder.endChainedControlSequence();
+ } catch (IOException ioe) {
endPresentationTextData();
- endControlSequence();
+ handleUnexpectedIOError(ioe);
+ //Should not occur since we're writing to byte arrays
}
}
+ private void handleUnexpectedIOError(IOException ioe) {
+ //"Unexpected" since we're currently dealing with ByteArrayOutputStreams here.
+ throw new RuntimeException("Unexpected I/O error", ioe);
+ }
+
/** {@inheritDoc} */
public String toString() {
if (presentationTextDataList != null) {
return presentationTextDataList.toString();
}
- return null;
+ return super.toString();
}
}
--- /dev/null
+/*
+ * 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.ptoca;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.afp.AFPLineDataInfo;
+
+/**
+ * {@link PtocaProducer} implementation that interprets {@link AFPLineDataInfo} objects.
+ */
+public class LineDataInfoProducer implements PtocaProducer, PtocaConstants {
+
+ /** Static logging instance */
+ private static final Log log = LogFactory.getLog(LineDataInfoProducer.class);
+
+ private AFPLineDataInfo lineDataInfo;
+
+ /**
+ * Main constructor.
+ * @param lineDataInfo the info object
+ */
+ public LineDataInfoProducer(AFPLineDataInfo lineDataInfo) {
+ this.lineDataInfo = lineDataInfo;
+ }
+
+ /** {@inheritDoc} */
+ public void produce(PtocaBuilder builder) throws IOException {
+ builder.setTextOrientation(lineDataInfo.getRotation());
+ int x1 = ensurePositive(lineDataInfo.getX1());
+ int y1 = ensurePositive(lineDataInfo.getY1());
+ builder.absoluteMoveBaseline(y1);
+ builder.absoluteMoveInline(x1);
+ builder.setExtendedTextColor(lineDataInfo.getColor());
+
+ int x2 = ensurePositive(lineDataInfo.getX2());
+ int y2 = ensurePositive(lineDataInfo.getY2());
+ int thickness = lineDataInfo.getThickness();
+ if (y1 == y2) {
+ builder.drawIaxisRule(x2 - x1, thickness);
+ } else if (x1 == x2) {
+ builder.drawBaxisRule(y2 - y1, thickness);
+ } else {
+ log.error("Invalid axis rule: unable to draw line");
+ return;
+ }
+ }
+
+ private static int ensurePositive(int value) {
+ if (value < 0) {
+ return 0;
+ }
+ return value;
+ }
+
+}
--- /dev/null
+/*
+ * 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.ptoca;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.commons.io.output.ByteArrayOutputStream;
+
+/**
+ * Generator class for PTOCA data structures.
+ */
+public abstract class PtocaBuilder implements PtocaConstants {
+
+ private ByteArrayOutputStream baout = new ByteArrayOutputStream(256);
+
+ /** the current x coordinate. */
+ private int currentX = -1;
+
+ /** the current y coordinate */
+ private int currentY = -1;
+
+ /** the current font */
+ private int currentFont = Integer.MIN_VALUE;
+
+ /** the current orientation */
+ private int currentOrientation = 0;
+
+ /** the current color */
+ private Color currentColor = Color.BLACK;
+
+ /** the current variable space increment */
+ private int currentVariableSpaceCharacterIncrement = 0;
+
+ /** the current inter character adjustment */
+ private int currentInterCharacterAdjustment = 0;
+
+
+ /**
+ * Returns an {@link OutputStream} for the next control sequence. This gives a subclass a
+ * chance to do chunking of control sequences into multiple presentation text data objects.
+ * @param length the length of the following control sequence
+ * @return the output stream where the control sequence will be written to
+ */
+ protected abstract OutputStream getOutputStreamForControlSequence(int length);
+
+ private static byte chained(byte functionType) {
+ return (byte)(functionType | CHAIN_BIT);
+ }
+
+ private void newControlSequence() {
+ baout.reset();
+ }
+
+ private void commit(byte functionType) throws IOException {
+ int length = baout.size() + 2;
+ assert length < 256;
+
+ OutputStream out = getOutputStreamForControlSequence(length);
+ out.write(length);
+ out.write(functionType);
+ baout.writeTo(out);
+ }
+
+ private void write(byte[] data, int offset, int length) {
+ baout.write(data, offset, length);
+ }
+
+ private void writeByte(int data) {
+ baout.write(data);
+ }
+
+ private void writeShort(int data) {
+ baout.write((data >>> 8) & 0xFF);
+ baout.write(data & 0xFF);
+ }
+
+ /**
+ * Writes the introducer for a chained control sequence.
+ * @throws IOException if an I/O error occurs
+ */
+ public void writeIntroducer() throws IOException {
+ OutputStream out = getOutputStreamForControlSequence(ESCAPE.length);
+ out.write(ESCAPE);
+ }
+
+ /**
+ * The Set Coded Font Local control sequence activates a coded font and
+ * specifies the character attributes to be used.
+ * <p>
+ * This is a modal control sequence.
+ *
+ * @param font The font local identifier.
+ * @throws IOException if an I/O error occurs
+ */
+ public void setCodedFont(byte font) throws IOException {
+ // Avoid unnecessary specification of the font
+ if (currentFont == font) {
+ return;
+ } else {
+ currentFont = font;
+ }
+
+ newControlSequence();
+ writeByte(font);
+ commit(chained(SCFL));
+ }
+
+ /**
+ * Establishes the current presentation position on the baseline at a new
+ * I-axis coordinate, which is a specified number of measurement units from
+ * the B-axis. There is no change to the current B-axis coordinate.
+ *
+ * @param coordinate The coordinate for the inline move.
+ * @throws IOException if an I/O error occurs
+ */
+ public void absoluteMoveInline(int coordinate) throws IOException {
+ if (coordinate == this.currentX) {
+ return;
+ }
+ newControlSequence();
+ writeShort(coordinate);
+ commit(chained(AMI));
+
+ currentX = coordinate;
+ }
+
+ /**
+ * Moves the inline coordinate of the presentation position relative to the current
+ * inline position.
+ * @param increment the increment in 1/1440 inch units
+ * @throws IOException if an I/O error occurs
+ */
+ public void relativeMoveInline(int increment) throws IOException {
+ newControlSequence();
+ writeShort(increment);
+ commit(chained(RMI));
+ }
+
+ /**
+ * Establishes the baseline and the current presentation position at a new
+ * B-axis coordinate, which is a specified number of measurement units from
+ * the I-axis. There is no change to the current I-axis coordinate.
+ *
+ * @param coordinate The coordinate for the baseline move.
+ * @throws IOException if an I/O error occurs
+ */
+ public void absoluteMoveBaseline(int coordinate) throws IOException {
+ if (coordinate == this.currentY) {
+ return;
+ }
+ newControlSequence();
+ writeShort(coordinate);
+ commit(chained(AMB));
+
+ currentY = coordinate;
+ currentX = -1;
+ }
+
+ private static final int TRANSPARENT_MAX_SIZE = 253;
+
+ /**
+ * The Transparent Data control sequence contains a sequence of code points
+ * that are presented without a scan for embedded control sequences. If the data is larger
+ * than fits in one chunk, additional chunks are automatically generated.
+ *
+ * @param data The text data to add.
+ * @throws IOException if an I/O error occurs
+ */
+ public void addTransparentData(byte[] data) throws IOException {
+ if (data.length <= TRANSPARENT_DATA_MAX_SIZE) {
+ addTransparentDataChunk(data);
+ } else {
+ // data size greater than TRANSPARENT_MAX_SIZE, so slice
+ int numTransData = data.length / TRANSPARENT_DATA_MAX_SIZE;
+ int currIndex = 0;
+ for (int transDataCnt = 0; transDataCnt < numTransData; transDataCnt++) {
+ addTransparentDataChunk(data, currIndex, TRANSPARENT_DATA_MAX_SIZE);
+ currIndex += TRANSPARENT_DATA_MAX_SIZE;
+ }
+ int left = data.length - currIndex;
+ addTransparentDataChunk(data, currIndex, left);
+ }
+ }
+
+ private void addTransparentDataChunk(byte[] data) throws IOException {
+ addTransparentDataChunk(data, 0, data.length);
+ }
+
+ private void addTransparentDataChunk(byte[] data, int offset, int length) throws IOException {
+ if (length > TRANSPARENT_MAX_SIZE) {
+ // Check that we are not exceeding the maximum length
+ throw new IllegalArgumentException(
+ "Transparent data is longer than " + TRANSPARENT_MAX_SIZE + " bytes");
+ }
+ newControlSequence();
+ write(data, offset, length);
+ commit(chained(TRN));
+ }
+
+ /**
+ * Draws a line of specified length and specified width in the B-direction
+ * from the current presentation position. The location of the current
+ * presentation position is unchanged.
+ *
+ * @param length The length of the rule.
+ * @param width The width of the rule.
+ * @throws IOException if an I/O error occurs
+ */
+ public void drawBaxisRule(int length, int width) throws IOException {
+ newControlSequence();
+ writeShort(length); // Rule length
+ writeShort(width); // Rule width
+ writeByte(0); // Rule width fraction is always null. enough?
+ commit(chained(DBR));
+ }
+
+ /**
+ * Draws a line of specified length and specified width in the I-direction
+ * from the current presentation position. The location of the current
+ * presentation position is unchanged.
+ *
+ * @param length The length of the rule.
+ * @param width The width of the rule.
+ * @throws IOException if an I/O error occurs
+ */
+ public void drawIaxisRule(int length, int width) throws IOException {
+ newControlSequence();
+ writeShort(length); // Rule length
+ writeShort(width); // Rule width
+ writeByte(0); // Rule width fraction is always null. enough?
+ commit(chained(DIR));
+ }
+
+ /**
+ * The Set Text Orientation control sequence establishes the I-direction and
+ * B-direction for the subsequent text. This is a modal control sequence.
+ *
+ * Semantics: This control sequence specifies the I-axis and B-axis
+ * orientations with respect to the Xp-axis for the current Presentation
+ * Text object. The orientations are rotational values expressed in degrees
+ * and minutes.
+ *
+ * @param orientation The text orientation (0, 90, 180, 270).
+ * @throws IOException if an I/O error occurs
+ */
+ public void setTextOrientation(int orientation) throws IOException {
+ if (orientation == this.currentOrientation) {
+ return;
+ }
+ newControlSequence();
+ switch (orientation) {
+ case 90:
+ writeByte(0x2D);
+ writeByte(0x00);
+ writeByte(0x5A);
+ writeByte(0x00);
+ break;
+ case 180:
+ writeByte(0x5A);
+ writeByte(0x00);
+ writeByte(0x87);
+ writeByte(0x00);
+ break;
+ case 270:
+ writeByte(0x87);
+ writeByte(0x00);
+ writeByte(0x00);
+ writeByte(0x00);
+ break;
+ default:
+ writeByte(0x00);
+ writeByte(0x00);
+ writeByte(0x2D);
+ writeByte(0x00);
+ break;
+ }
+ commit(chained(STO));
+ this.currentOrientation = orientation;
+ currentX = -1;
+ currentY = -1;
+ }
+
+ /**
+ * The Set Extended Text Color control sequence specifies a color value and
+ * defines the color space and encoding for that value. The specified color
+ * value is applied to foreground areas of the text presentation space.
+ * <p>
+ * This is a modal control sequence.
+ *
+ * @param col The color to be set.
+ * @throws IOException if an I/O error occurs
+ */
+ public void setExtendedTextColor(Color col) throws IOException {
+ if (col.equals(currentColor)) {
+ return;
+ }
+ newControlSequence();
+ writeByte(0x00); // Reserved; must be zero
+ writeByte(0x01); // Color space - 0x01 = RGB
+ writeByte(0x00); // Reserved; must be zero
+ writeByte(0x00); // Reserved; must be zero
+ writeByte(0x00); // Reserved; must be zero
+ writeByte(0x00); // Reserved; must be zero
+ writeByte(8); // Number of bits in component 1
+ writeByte(8); // Number of bits in component 2
+ writeByte(8); // Number of bits in component 3
+ writeByte(0); // Number of bits in component 4
+ writeByte(col.getRed()); // Red intensity
+ writeByte(col.getGreen()); // Green intensity
+ writeByte(col.getBlue()); // Blue intensity
+ commit(chained(SEC));
+ this.currentColor = col;
+ }
+
+ /**
+ * Sets the variable space character increment.
+ * <p>
+ * This is a modal control sequence.
+ *
+ * @param incr The increment to be set (positive integer, 1/1440 inch)
+ * @throws IOException if an I/O error occurs
+ */
+ public void setVariableSpaceCharacterIncrement(int incr) throws IOException {
+ if (incr == this.currentVariableSpaceCharacterIncrement) {
+ return;
+ }
+ assert incr > 0 && incr < (1 << 16);
+ newControlSequence();
+ writeShort(Math.abs(incr)); //Increment
+ commit(chained(SVI));
+
+ this.currentVariableSpaceCharacterIncrement = incr;
+ }
+
+ /**
+ * Sets the intercharacter adjustment (additional increment or decrement between graphic
+ * characters).
+ * <p>
+ * This is a modal control sequence.
+ *
+ * @param incr The increment to be set (1/1440 inch)
+ * @throws IOException if an I/O error occurs
+ */
+ public void setInterCharacterAdjustment(int incr) throws IOException {
+ if (incr == this.currentInterCharacterAdjustment) {
+ return;
+ }
+ assert incr >= Short.MIN_VALUE && incr <= Short.MAX_VALUE;
+ newControlSequence();
+ writeShort(Math.abs(incr)); //Increment
+ writeByte(incr >= 0 ? 0 : 1); // Direction
+ commit(chained(SIA));
+
+ this.currentInterCharacterAdjustment = incr;
+ }
+
+ /**
+ * A control sequence is a sequence of bytes that specifies a control
+ * function. A control sequence consists of a control sequence introducer
+ * and zero or more parameters. The control sequence can extend multiple
+ * presentation text data objects, but must eventually be terminated. This
+ * method terminates the control sequence (by using a NOP command).
+ *
+ * @throws IOException if an I/O error occurs
+ */
+ public void endChainedControlSequence() throws IOException {
+ newControlSequence();
+ commit(NOP);
+ }
+}
--- /dev/null
+/*
+ * 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.ptoca;
+
+/**
+ * A collection of PTOCA constants.
+ */
+public interface PtocaConstants {
+
+ /**
+ * "Escape" sequence for normal PTOCA command sequences.
+ */
+ byte[] ESCAPE = new byte[] {0x2B, (byte)0xD3};
+
+ /** Bit to set for chained control sequences */
+ byte CHAIN_BIT = 1;
+
+ /** Set Intercharacter Adjustment */
+ byte SIA = (byte)0xC2;
+ /** Set Variable Space Character Increment */
+ byte SVI = (byte)0xC4;
+ /** Absolute Move Inline */
+ byte AMI = (byte)0xC6;
+ /** Relative Move Inline */
+ byte RMI = (byte)0xC8;
+
+ /** Absolute Move Baseline */
+ byte AMB = (byte)0xD2;
+
+ /** Transparent Data */
+ byte TRN = (byte)0xDA;
+
+ /** Draw I-axis Rule */
+ byte DIR = (byte)0xE4;
+ /** Draw B-axis Rule */
+ byte DBR = (byte)0xE6;
+
+ /** Set Extended Text Color */
+ byte SEC = (byte)0x80;
+
+ /** Set Coded Font Local */
+ byte SCFL = (byte)0xF0;
+ /** Set Text Orientation */
+ byte STO = (byte)0xF6;
+
+ /** No Operation */
+ byte NOP = (byte)0xF8;
+
+ /** Maximum size of transparent data chunks */
+ int TRANSPARENT_DATA_MAX_SIZE = 253;
+
+}
--- /dev/null
+/*
+ * 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.ptoca;
+
+import java.io.IOException;
+
+import org.apache.fop.afp.modca.PresentationTextObject;
+
+/**
+ * Producer interface that is passed to a {@link PresentationTextObject} to produce PTOCA control
+ * sequences using a {@link PtocaBuilder}.
+ */
+public interface PtocaProducer {
+
+ /**
+ * Produces the PTOCA control sequences by calling methods on {@link PtocaBuilder}.
+ * @param builder the builder object
+ * @throws IOException if an I/O error occurs
+ */
+ void produce(PtocaBuilder builder) throws IOException;
+
+}
--- /dev/null
+/*
+ * 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.ptoca;
+
+import java.io.IOException;
+
+import org.apache.fop.afp.AFPTextDataInfo;
+
+/**
+ * {@link PtocaProducer} implementation that interprets {@link AFPTextDataInfo} objects.
+ */
+public class TextDataInfoProducer implements PtocaProducer, PtocaConstants {
+
+ private AFPTextDataInfo textDataInfo;
+
+ /**
+ * Main constructor.
+ * @param textDataInfo the info object
+ */
+ public TextDataInfoProducer(AFPTextDataInfo textDataInfo) {
+ this.textDataInfo = textDataInfo;
+ }
+
+ /** {@inheritDoc} */
+ public void produce(PtocaBuilder builder) throws IOException {
+ builder.setTextOrientation(textDataInfo.getRotation());
+ builder.absoluteMoveBaseline(textDataInfo.getY());
+ builder.absoluteMoveInline(textDataInfo.getX());
+
+ builder.setVariableSpaceCharacterIncrement(
+ textDataInfo.getVariableSpaceCharacterIncrement());
+ builder.setInterCharacterAdjustment(
+ textDataInfo.getInterCharacterAdjustment());
+ builder.setExtendedTextColor(textDataInfo.getColor());
+ builder.setCodedFont((byte)textDataInfo.getFontReference());
+
+
+ // Add transparent data
+ String textString = textDataInfo.getString();
+ String encoding = textDataInfo.getEncoding();
+ byte[] data = textString.getBytes(encoding);
+ builder.addTransparentData(data);
+ }
+
+}
--- /dev/null
+<!--
+ 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$ -->
+<HTML>
+<TITLE>org.apache.fop.afp.ptoca Package</TITLE>
+<BODY>
+<P>Contains a collection of classes for working with Presentation Text Objects (PTOCA).</P>
+</BODY>
+</HTML>
\ No newline at end of file
}
/** {@inheritDoc} */
- public void putHints(Map hints) {
- if (hints == null) {
+ public void putHints(Map additionalHints) {
+ if (additionalHints == null) {
return;
}
if (this.hints == null) {
this.hints = new java.util.HashMap();
}
- this.hints.putAll(hints);
+ this.hints.putAll(additionalHints);
+ }
+
+ /** {@inheritDoc} */
+ public void putHint(Object key, Object value) {
+ this.hints.put(key, value);
}
/** {@inheritDoc} */
*/
FOUserAgent getUserAgent();
- void putHints(Map hints);
+ /**
+ * Adds additional hints to the existing hints, overriding existing hints.
+ * @param additionalHints a map of additional hints
+ */
+ void putHints(Map additionalHints);
+
+ /**
+ * Sets an additional hint, overriding an existing hint.
+ * @param key the key
+ * @param value the value
+ */
+ void putHint(Object key, Object value);
+ /**
+ * Returns an unmodifiable representation of all hints.
+ * @return the hints
+ */
Map getHints();
+ /**
+ * Returns a hint identified by a key.
+ * @param key the key
+ * @return the hint or null if no hint with the given key could be found
+ */
Object getHint(Object key);
}
--- /dev/null
+/*
+ * 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.render.afp;
+
+/**
+ * Interface used to customize the AFP renderer or document handler.
+ */
+public interface AFPCustomizable {
+
+ /**
+ * Sets the number of bits used per pixel
+ *
+ * @param bitsPerPixel
+ * number of bits per pixel
+ */
+ void setBitsPerPixel(int bitsPerPixel);
+
+ /**
+ * Sets whether images are color or not
+ *
+ * @param colorImages
+ * color image output
+ */
+ void setColorImages(boolean colorImages);
+
+ /**
+ * Sets whether images are supported natively or not
+ *
+ * @param nativeImages
+ * native image support
+ */
+ void setNativeImagesSupported(boolean nativeImages);
+
+ /**
+ * Sets the output/device resolution
+ *
+ * @param resolution
+ * the output resolution (dpi)
+ */
+ void setResolution(int resolution);
+
+ /**
+ * Returns the output/device resolution.
+ *
+ * @return the resolution in dpi
+ */
+ int getResolution();
+
+ /**
+ * Sets the default resource group file path
+ * @param filePath the default resource group file path
+ */
+ void setDefaultResourceGroupFilePath(String filePath);
+
+}
--- /dev/null
+/*
+ * 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.render.afp;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.geom.AffineTransform;
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.afp.AFPPaintingState;
+import org.apache.fop.afp.AFPResourceManager;
+import org.apache.fop.afp.AFPUnitConverter;
+import org.apache.fop.afp.DataStream;
+import org.apache.fop.afp.fonts.AFPPageFonts;
+import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
+import org.apache.fop.render.intermediate.IFContext;
+import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
+import org.apache.fop.render.intermediate.IFException;
+import org.apache.fop.render.intermediate.IFPainter;
+
+/**
+ * {@code IFDocumentHandler} implementation that produces AFP (MO:DCA).
+ */
+public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
+ implements AFPCustomizable {
+
+ /** logging instance */
+ private static Log log = LogFactory.getLog(AFPDocumentHandler.class);
+
+ /** the resource manager */
+ private AFPResourceManager resourceManager;
+
+ /** the painting state */
+ private final AFPPaintingState paintingState;
+
+ /** unit converter */
+ private final AFPUnitConverter unitConv;
+
+ /** the AFP datastream */
+ private DataStream dataStream;
+
+ /**
+ * Default constructor.
+ */
+ public AFPDocumentHandler() {
+ this.resourceManager = new AFPResourceManager();
+ this.paintingState = new AFPPaintingState();
+ this.unitConv = paintingState.getUnitConverter();
+ }
+
+ /** {@inheritDoc} */
+ public boolean supportsPagesOutOfOrder() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public String getMimeType() {
+ return MimeConstants.MIME_AFP;
+ }
+
+ /** {@inheritDoc} */
+ public void setContext(IFContext context) {
+ super.setContext(context);
+ }
+
+ /** {@inheritDoc} */
+ public IFDocumentHandlerConfigurator getConfigurator() {
+ return new AFPRendererConfigurator(getUserAgent());
+ }
+
+ AFPPaintingState getPaintingState() {
+ return this.paintingState;
+ }
+
+ DataStream getDataStream() {
+ return this.dataStream;
+ }
+
+ AFPResourceManager getResourceManager() {
+ return this.resourceManager;
+ }
+
+ /** {@inheritDoc} */
+ public void startDocument() throws IFException {
+ try {
+ if (getUserAgent() == null) {
+ throw new IllegalStateException(
+ "User agent must be set before starting PostScript generation");
+ }
+ if (this.outputStream == null) {
+ throw new IllegalStateException("OutputStream hasn't been set through setResult()");
+ }
+ paintingState.setColor(Color.WHITE);
+
+ this.dataStream = resourceManager.createDataStream(paintingState, outputStream);
+
+ this.dataStream.startDocument();
+ } catch (IOException e) {
+ throw new IFException("I/O error in startDocument()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endDocumentHeader() throws IFException {
+ }
+
+ /** {@inheritDoc} */
+ public void endDocument() throws IFException {
+ try {
+ this.dataStream.endDocument();
+ this.dataStream = null;
+ this.resourceManager.writeToStream();
+ this.resourceManager = null;
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in endDocument()", ioe);
+ }
+ super.endDocument();
+ }
+
+ /** {@inheritDoc} */
+ public void startPageSequence(String id) throws IFException {
+ try {
+ dataStream.startPageGroup();
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in startPageSequence()", ioe);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endPageSequence() throws IFException {
+ //nop
+ }
+
+ /**
+ * Returns the base AFP transform
+ *
+ * @return the base AFP transform
+ */
+ private AffineTransform getBaseTransform() {
+ AffineTransform baseTransform = new AffineTransform();
+ double scale = unitConv.mpt2units(1);
+ baseTransform.scale(scale, scale);
+ return baseTransform;
+ }
+
+ /** {@inheritDoc} */
+ public void startPage(int index, String name, String pageMasterName, Dimension size)
+ throws IFException {
+ paintingState.clear();
+
+ AffineTransform baseTransform = getBaseTransform();
+ paintingState.concatenate(baseTransform);
+
+ int pageWidth = Math.round(unitConv.mpt2units(size.width));
+ paintingState.setPageWidth(pageWidth);
+
+ int pageHeight = Math.round(unitConv.mpt2units(size.height));
+ paintingState.setPageHeight(pageHeight);
+
+ int pageRotation = paintingState.getPageRotation();
+ int resolution = paintingState.getResolution();
+
+ dataStream.startPage(pageWidth, pageHeight, pageRotation,
+ resolution, resolution);
+
+ //TODO Handle page extensions
+ //renderPageObjectExtensions(pageViewport);
+
+ }
+
+ /** {@inheritDoc} */
+ public void startPageHeader() throws IFException {
+ super.startPageHeader();
+
+ }
+
+ /** {@inheritDoc} */
+ public void endPageHeader() throws IFException {
+ super.endPageHeader();
+ }
+
+ /** {@inheritDoc} */
+ public IFPainter startPageContent() throws IFException {
+ return new AFPPainter(this);
+ }
+
+ /** {@inheritDoc} */
+ public void endPageContent() throws IFException {
+ }
+
+ /** {@inheritDoc} */
+ public void endPage() throws IFException {
+ try {
+ AFPPageFonts pageFonts = paintingState.getPageFonts();
+ if (pageFonts != null && !pageFonts.isEmpty()) {
+ dataStream.addFontsToCurrentPage(pageFonts);
+ }
+
+ dataStream.endPage();
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in endPage()", ioe);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void handleExtensionObject(Object extension) throws IFException {
+ /*
+ try {
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in handleExtensionObject()", ioe);
+ }
+ */
+ }
+
+ // ---=== AFPCustomizable ===---
+
+ /** {@inheritDoc} */
+ public void setBitsPerPixel(int bitsPerPixel) {
+ paintingState.setBitsPerPixel(bitsPerPixel);
+ }
+
+ /** {@inheritDoc} */
+ public void setColorImages(boolean colorImages) {
+ paintingState.setColorImages(colorImages);
+ }
+
+ /** {@inheritDoc} */
+ public void setNativeImagesSupported(boolean nativeImages) {
+ paintingState.setNativeImagesSupported(nativeImages);
+ }
+
+ /** {@inheritDoc} */
+ public void setResolution(int resolution) {
+ paintingState.setResolution(resolution);
+ }
+
+ /** {@inheritDoc} */
+ public int getResolution() {
+ return paintingState.getResolution();
+ }
+
+ /** {@inheritDoc} */
+ public void setDefaultResourceGroupFilePath(String filePath) {
+ resourceManager.setDefaultResourceGroupFilePath(filePath);
+ }
+
+}
--- /dev/null
+/*
+ * 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.render.afp;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.render.intermediate.AbstractIFDocumentHandlerMaker;
+import org.apache.fop.render.intermediate.IFContext;
+import org.apache.fop.render.intermediate.IFDocumentHandler;
+import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
+
+/**
+ * Intermediate format document handler factory for AFP output.
+ */
+public class AFPDocumentHandlerMaker extends AbstractIFDocumentHandlerMaker {
+
+ //TODO Revert to normal MIME after stabilization!
+ private static final String[] MIMES = new String[]
+ {MimeConstants.MIME_AFP + ";mode=painter"};
+
+ /** {@inheritDoc} */
+ public IFDocumentHandler makeIFDocumentHandler(FOUserAgent ua) {
+ AFPDocumentHandler handler = new AFPDocumentHandler();
+ handler.setContext(new IFContext(ua));
+ return handler;
+ }
+
+ /** {@inheritDoc} */
+ public boolean needsOutputStream() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public String[] getSupportedMimeTypes() {
+ return MIMES;
+ }
+
+ /** {@inheritDoc} */
+ public IFDocumentHandlerConfigurator getConfigurator(FOUserAgent userAgent) {
+ return new AFPRendererConfigurator(userAgent);
+ }
+
+}
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.util.QName;
+
import org.apache.fop.afp.AFPResourceInfo;
import org.apache.fop.afp.AFPResourceLevel;
import org.apache.fop.render.afp.extensions.AFPElementMapping;
-import org.apache.xmlgraphics.util.QName;
/**
* Parses any AFP foreign attributes
private static final Log log = LogFactory.getLog("org.apache.xmlgraphics.afp");
/** the resource-name attribute */
- public static final String RESOURCE_NAME = "afp:resource-name";
+ public static final QName RESOURCE_NAME = new QName(
+ AFPElementMapping.NAMESPACE, "afp:resource-name");
/** the resource-level attribute */
- public static final String RESOURCE_LEVEL = "afp:resource-level";
+ public static final QName RESOURCE_LEVEL = new QName(
+ AFPElementMapping.NAMESPACE, "afp:resource-level");
/** the resource-group-file attribute */
- public static final String RESOURCE_GROUP_FILE = "afp:resource-group-file";
+ public static final QName RESOURCE_GROUP_FILE = new QName(
+ AFPElementMapping.NAMESPACE, "afp:resource-group-file");
/**
* Main constructor
public AFPResourceInfo getResourceInfo(Map/*<QName, String>*/ foreignAttributes) {
AFPResourceInfo resourceInfo = new AFPResourceInfo();
if (foreignAttributes != null && !foreignAttributes.isEmpty()) {
- QName resourceNameKey = new QName(AFPElementMapping.NAMESPACE, RESOURCE_NAME);
- String resourceName = (String)foreignAttributes.get(resourceNameKey);
+ String resourceName = (String)foreignAttributes.get(RESOURCE_NAME);
if (resourceName != null) {
resourceInfo.setName(resourceName);
}
public AFPResourceLevel getResourceLevel(Map/*<QName, String>*/ foreignAttributes) {
AFPResourceLevel resourceLevel = null;
if (foreignAttributes != null && !foreignAttributes.isEmpty()) {
- QName resourceLevelKey = new QName(AFPElementMapping.NAMESPACE, RESOURCE_LEVEL);
- if (foreignAttributes.containsKey(resourceLevelKey)) {
- String levelString = (String)foreignAttributes.get(resourceLevelKey);
+ if (foreignAttributes.containsKey(RESOURCE_LEVEL)) {
+ String levelString = (String)foreignAttributes.get(RESOURCE_LEVEL);
resourceLevel = AFPResourceLevel.valueOf(levelString);
// if external get resource group file attributes
if (resourceLevel != null && resourceLevel.isExternal()) {
- QName resourceGroupFileKey = new QName(AFPElementMapping.NAMESPACE,
- RESOURCE_GROUP_FILE);
String resourceGroupFile
- = (String)foreignAttributes.get(resourceGroupFileKey);
+ = (String)foreignAttributes.get(RESOURCE_GROUP_FILE);
if (resourceGroupFile == null) {
String msg = RESOURCE_GROUP_FILE + " not specified";
log.error(msg);
package org.apache.fop.render.afp;
import java.awt.Point;
+import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.Map;
AFPDataObjectInfo dataObjectInfo = createDataObjectInfo();
// set resource information
- Map foreignAttributes = rendererImageInfo.getForeignAttributes();
- AFPResourceInfo resourceInfo
- = foreignAttributeReader.getResourceInfo(foreignAttributes);
- resourceInfo.setUri(rendererImageInfo.getURI());
- dataObjectInfo.setResourceInfo(resourceInfo);
+ setResourceInformation(dataObjectInfo,
+ rendererImageInfo.getURI(),
+ rendererImageInfo.getForeignAttributes());
- // set object area
- AFPObjectAreaInfo objectAreaInfo = new AFPObjectAreaInfo();
Point origin = rendererImageInfo.getOrigin();
Rectangle2D position = rendererImageInfo.getPosition();
- float srcX = origin.x + (float)position.getX();
- float srcY = origin.y + (float)position.getY();
+ int srcX = Math.round(origin.x + (float)position.getX());
+ int srcY = Math.round(origin.y + (float)position.getY());
+ Rectangle targetRect = new Rectangle(
+ srcX,
+ srcY,
+ (int)Math.round(position.getWidth()),
+ (int)Math.round(position.getHeight()));
AFPRendererContext rendererContext
= (AFPRendererContext)rendererImageInfo.getRendererContext();
AFPInfo afpInfo = rendererContext.getInfo();
AFPPaintingState paintingState = afpInfo.getPaintingState();
+
+ dataObjectInfo.setObjectAreaInfo(createObjectAreaInfo(paintingState, targetRect));
+
+ return dataObjectInfo;
+ }
+
+ /**
+ * Sets resource information on the data object info.
+ * @param dataObjectInfo the data object info instance
+ * @param uri the image's URI (or null if no URI is available)
+ * @param foreignAttributes a Map of foreign attributes (or null)
+ */
+ protected void setResourceInformation(AFPDataObjectInfo dataObjectInfo,
+ String uri, Map foreignAttributes) {
+ AFPResourceInfo resourceInfo
+ = foreignAttributeReader.getResourceInfo(foreignAttributes);
+ resourceInfo.setUri(uri);
+ dataObjectInfo.setResourceInfo(resourceInfo);
+ }
+
+ /**
+ * Creates and returns an {@link AFPObjectAreaInfo} instance for the placement of the image.
+ * @param paintingState the painting state
+ * @param targetRect the target rectangle in which to place the image (coordinates in mpt)
+ * @return the newly created object area info instance
+ */
+ public static AFPObjectAreaInfo createObjectAreaInfo(AFPPaintingState paintingState,
+ Rectangle targetRect) {
+ AFPObjectAreaInfo objectAreaInfo = new AFPObjectAreaInfo();
AFPUnitConverter unitConv = paintingState.getUnitConverter();
- int[] coords = unitConv.mpts2units(new float[] {srcX, srcY});
+
+ int[] coords = unitConv.mpts2units(new float[] {targetRect.x, targetRect.y});
objectAreaInfo.setX(coords[X]);
objectAreaInfo.setY(coords[Y]);
- int width = Math.round(unitConv.mpt2units((float)position.getWidth()));
+ int width = Math.round(unitConv.mpt2units(targetRect.width));
objectAreaInfo.setWidth(width);
- int height = Math.round(unitConv.mpt2units((float)position.getHeight()));
+ int height = Math.round(unitConv.mpt2units(targetRect.height));
objectAreaInfo.setHeight(height);
int resolution = paintingState.getResolution();
objectAreaInfo.setWidthRes(resolution);
objectAreaInfo.setRotation(paintingState.getRotation());
-
- dataObjectInfo.setObjectAreaInfo(objectAreaInfo);
-
- return dataObjectInfo;
+ return objectAreaInfo;
}
/**
package org.apache.fop.render.afp;
+import java.awt.Rectangle;
import java.io.IOException;
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
+import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
+import org.apache.xmlgraphics.util.MimeConstants;
+
import org.apache.fop.afp.AFPDataObjectInfo;
import org.apache.fop.afp.AFPGraphics2D;
import org.apache.fop.afp.AFPGraphicsObjectInfo;
import org.apache.fop.afp.AFPPaintingState;
import org.apache.fop.afp.AFPResourceInfo;
import org.apache.fop.afp.AFPResourceLevel;
-import org.apache.xmlgraphics.image.loader.ImageFlavor;
-import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
-import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
-import org.apache.xmlgraphics.util.MimeConstants;
+import org.apache.fop.render.ImageHandler;
+import org.apache.fop.render.ImageHandlerUtil;
+import org.apache.fop.render.RenderingContext;
/**
* PDFImageHandler implementation which handles Graphics2D images.
*/
-public class AFPImageHandlerGraphics2D extends AFPImageHandler {
+public class AFPImageHandlerGraphics2D extends AFPImageHandler implements ImageHandler {
private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {
ImageFlavor.GRAPHICS2D
AFPGraphicsObjectInfo graphicsObjectInfo
= (AFPGraphicsObjectInfo)super.generateDataObjectInfo(rendererImageInfo);
- AFPResourceInfo resourceInfo = graphicsObjectInfo.getResourceInfo();
- //level not explicitly set/changed so default to inline for GOCA graphic objects
- // (due to a bug in the IBM AFP Workbench Viewer (2.04.01.07), hard copy works just fine)
- if (!resourceInfo.levelChanged()) {
- resourceInfo.setLevel(new AFPResourceLevel(AFPResourceLevel.INLINE));
- }
+ setDefaultToInlineResourceLevel(graphicsObjectInfo);
// set mime type (unsupported by MOD:CA registry)
graphicsObjectInfo.setMimeType(MimeConstants.MIME_AFP_GOCA);
// set g2d
boolean textAsShapes = false;
-
AFPGraphics2D g2d = afpInfo.createGraphics2D(textAsShapes);
graphicsObjectInfo.setGraphics2D(g2d);
}
}
+ private void setDefaultToInlineResourceLevel(AFPGraphicsObjectInfo graphicsObjectInfo) {
+ AFPResourceInfo resourceInfo = graphicsObjectInfo.getResourceInfo();
+ //level not explicitly set/changed so default to inline for GOCA graphic objects
+ // (due to a bug in the IBM AFP Workbench Viewer (2.04.01.07), hard copy works just fine)
+ if (!resourceInfo.levelChanged()) {
+ resourceInfo.setLevel(new AFPResourceLevel(AFPResourceLevel.INLINE));
+ }
+ }
+
/** {@inheritDoc} */
public int getPriority() {
return 200;
protected AFPDataObjectInfo createDataObjectInfo() {
return new AFPGraphicsObjectInfo();
}
+
+ /** {@inheritDoc} */
+ public void handleImage(RenderingContext context, Image image, Rectangle pos)
+ throws IOException {
+ AFPRenderingContext afpContext = (AFPRenderingContext)context;
+
+ AFPGraphicsObjectInfo graphicsObjectInfo = (AFPGraphicsObjectInfo)createDataObjectInfo();
+
+ // set resource information
+ setResourceInformation(graphicsObjectInfo,
+ image.getInfo().getOriginalURI(),
+ afpContext.getForeignAttributes());
+
+ // Positioning
+ graphicsObjectInfo.setObjectAreaInfo(
+ createObjectAreaInfo(afpContext.getPaintingState(), pos));
+
+ setDefaultToInlineResourceLevel(graphicsObjectInfo);
+
+ // Image content
+ ImageGraphics2D imageG2D = (ImageGraphics2D)image;
+ boolean textAsShapes = false; //TODO Make configurable
+ AFPGraphics2D g2d = new AFPGraphics2D(
+ textAsShapes,
+ afpContext.getPaintingState(),
+ afpContext.getResourceManager(),
+ graphicsObjectInfo.getResourceInfo(),
+ afpContext.getFontInfo());
+ g2d.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
+
+ graphicsObjectInfo.setGraphics2D(g2d);
+ graphicsObjectInfo.setPainter(imageG2D.getGraphics2DImagePainter());
+
+ // Create image
+ afpContext.getResourceManager().createObject(graphicsObjectInfo);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isCompatible(RenderingContext targetContext, Image image) {
+ boolean supported = (image == null || image instanceof ImageGraphics2D)
+ && targetContext instanceof AFPRenderingContext;
+ if (supported) {
+ String mode = (String)targetContext.getHint(ImageHandlerUtil.CONVERSION_MODE);
+ if (ImageHandlerUtil.isConversionModeBitmap(mode)) {
+ //Disabling this image handler automatically causes a bitmap to be generated
+ return false;
+ }
+ }
+ return supported;
+ }
}
package org.apache.fop.render.afp;
-import java.io.IOException;
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawStream;
+import org.apache.xmlgraphics.util.MimeConstants;
import org.apache.fop.afp.AFPDataObjectInfo;
import org.apache.fop.afp.AFPImageObjectInfo;
-import org.apache.xmlgraphics.image.loader.ImageFlavor;
-import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax;
+import org.apache.fop.render.RenderingContext;
/**
* AFPImageHandler implementation which handles CCITT encoded images (CCITT fax group 3/4).
};
/** {@inheritDoc} */
- public AFPDataObjectInfo generateDataObjectInfo(
- AFPRendererImageInfo rendererImageInfo) throws IOException {
- AFPImageObjectInfo imageObjectInfo
- = (AFPImageObjectInfo)super.generateDataObjectInfo(rendererImageInfo);
-
- ImageRawCCITTFax ccitt = (ImageRawCCITTFax) rendererImageInfo.getImage();
+ protected void setAdditionalParameters(AFPDataObjectInfo dataObjectInfo,
+ ImageRawStream image) {
+ AFPImageObjectInfo imageObjectInfo = (AFPImageObjectInfo)dataObjectInfo;
+ ImageRawCCITTFax ccitt = (ImageRawCCITTFax)image;
int compression = ccitt.getCompression();
imageObjectInfo.setCompression(compression);
imageObjectInfo.setBitsPerPixel(1);
- return imageObjectInfo;
+
+ //CCITTFax flavor doesn't have TIFF associated but the AFP library listens to
+ //that to identify CCITT encoded images. CCITT is not exclusive to TIFF.
+ imageObjectInfo.setMimeType(MimeConstants.MIME_TIFF);
}
/** {@inheritDoc} */
return FLAVORS;
}
+ /** {@inheritDoc} */
+ public boolean isCompatible(RenderingContext targetContext, Image image) {
+ if (targetContext instanceof AFPRenderingContext) {
+ AFPRenderingContext afpContext = (AFPRenderingContext)targetContext;
+ return (afpContext.getPaintingState().isNativeImagesSupported())
+ && (image == null || image instanceof ImageRawCCITTFax);
+ }
+ return false;
+ }
+
}
package org.apache.fop.render.afp;
-import org.apache.fop.afp.AFPDataObjectInfo;
+import org.apache.xmlgraphics.image.loader.Image;
import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawEPS;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG;
import org.apache.xmlgraphics.image.loader.impl.ImageRawStream;
+import org.apache.fop.afp.AFPDataObjectInfo;
+import org.apache.fop.render.RenderingContext;
+
/**
* AFPImageHandler implementation which handles raw stream images.
*/
protected AFPDataObjectInfo createDataObjectInfo() {
return new AFPDataObjectInfo();
}
+
+ /** {@inheritDoc} */
+ public boolean isCompatible(RenderingContext targetContext, Image image) {
+ if (targetContext instanceof AFPRenderingContext) {
+ AFPRenderingContext afpContext = (AFPRenderingContext)targetContext;
+ return (afpContext.getPaintingState().isNativeImagesSupported())
+ && (image == null || image instanceof ImageRawJPEG || image instanceof ImageRawEPS);
+ }
+ return false;
+ }
}
package org.apache.fop.render.afp;
+import java.awt.Rectangle;
import java.awt.image.RenderedImage;
import java.io.IOException;
import org.apache.commons.io.output.ByteArrayOutputStream;
-import org.apache.fop.afp.AFPDataObjectInfo;
-import org.apache.fop.afp.AFPImageObjectInfo;
-import org.apache.fop.afp.AFPObjectAreaInfo;
-import org.apache.fop.afp.AFPPaintingState;
+
+import org.apache.xmlgraphics.image.loader.Image;
import org.apache.xmlgraphics.image.loader.ImageFlavor;
import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
import org.apache.xmlgraphics.ps.ImageEncodingHelper;
import org.apache.xmlgraphics.util.MimeConstants;
+import org.apache.fop.afp.AFPDataObjectInfo;
+import org.apache.fop.afp.AFPImageObjectInfo;
+import org.apache.fop.afp.AFPObjectAreaInfo;
+import org.apache.fop.afp.AFPPaintingState;
+import org.apache.fop.render.ImageHandler;
+import org.apache.fop.render.RenderingContext;
+
/**
* PDFImageHandler implementation which handles RenderedImage instances.
*/
-public class AFPImageHandlerRenderedImage extends AFPImageHandler {
+public class AFPImageHandlerRenderedImage extends AFPImageHandler implements ImageHandler {
private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {
ImageFlavor.BUFFERED_IMAGE,
= (AFPRendererContext)rendererImageInfo.getRendererContext();
AFPInfo afpInfo = rendererContext.getInfo();
AFPPaintingState paintingState = afpInfo.getPaintingState();
+ ImageRendered imageRendered = (ImageRendered) rendererImageInfo.img;
+
+ updateDataObjectInfo(imageObjectInfo, paintingState, imageRendered);
+ return imageObjectInfo;
+ }
+
+ private AFPDataObjectInfo updateDataObjectInfo(AFPImageObjectInfo imageObjectInfo,
+ AFPPaintingState paintingState, ImageRendered imageRendered)
+ throws IOException {
+
int resolution = paintingState.getResolution();
imageObjectInfo.setMimeType(MimeConstants.MIME_AFP_IOCA_FS45);
imageObjectInfo.setDataHeightRes(resolution);
imageObjectInfo.setDataWidthRes(resolution);
- ImageRendered imageRendered = (ImageRendered) rendererImageInfo.img;
RenderedImage renderedImage = imageRendered.getRenderedImage();
int dataHeight = renderedImage.getHeight();
int dataWidth = renderedImage.getWidth();
imageObjectInfo.setDataWidth(dataWidth);
+ //TODO This needs to be improved: Buffering whole images in memory is bad.
+ //Images with fewer bits per pixel than the required output format are unnecessarily
+ //increased in size (extreme case: 1-bit images are blown up to 24 bits if color is
+ //enabled). For grayscale output, encoding is even a two-step process which
+ //needs to be streamlined to a single step. The resulting bitmap is then still buffered
+ //in memory. To reduce AFP file size, investigate using a compression scheme.
+ //Currently, all image data is uncompressed.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageEncodingHelper.encodeRenderedImageAsRGB(renderedImage, baos);
byte[] imageData = baos.toByteArray();
return FLAVORS;
}
+ /** {@inheritDoc} */
+ public void handleImage(RenderingContext context, Image image, Rectangle pos)
+ throws IOException {
+ AFPRenderingContext afpContext = (AFPRenderingContext)context;
+
+ AFPImageObjectInfo imageObjectInfo = (AFPImageObjectInfo)createDataObjectInfo();
+
+ // set resource information
+ setResourceInformation(imageObjectInfo,
+ image.getInfo().getOriginalURI(),
+ afpContext.getForeignAttributes());
+
+ // Positioning
+ imageObjectInfo.setObjectAreaInfo(createObjectAreaInfo(afpContext.getPaintingState(), pos));
+
+ // Image content
+ ImageRendered imageRend = (ImageRendered)image;
+ updateDataObjectInfo(imageObjectInfo, afpContext.getPaintingState(), imageRend);
+
+ // Create image
+ afpContext.getResourceManager().createObject(imageObjectInfo);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isCompatible(RenderingContext targetContext, Image image) {
+ return (image == null || image instanceof ImageRendered)
+ && targetContext instanceof AFPRenderingContext;
+ }
+
}
--- /dev/null
+/*
+ * 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.render.afp;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.io.IOException;
+
+import org.w3c.dom.Document;
+
+import org.apache.batik.bridge.BridgeContext;
+import org.apache.batik.bridge.GVTBuilder;
+import org.apache.batik.gvt.GraphicsNode;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
+import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
+
+import org.apache.fop.afp.AFPDataObjectInfo;
+import org.apache.fop.afp.AFPGraphics2D;
+import org.apache.fop.afp.AFPGraphicsObjectInfo;
+import org.apache.fop.afp.AFPObjectAreaInfo;
+import org.apache.fop.afp.AFPPaintingState;
+import org.apache.fop.afp.AFPResourceInfo;
+import org.apache.fop.afp.AFPResourceLevel;
+import org.apache.fop.afp.AFPResourceManager;
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.image.loader.batik.BatikImageFlavors;
+import org.apache.fop.image.loader.batik.BatikUtil;
+import org.apache.fop.image.loader.batik.Graphics2DImagePainterImpl;
+import org.apache.fop.render.ImageHandler;
+import org.apache.fop.render.ImageHandlerUtil;
+import org.apache.fop.render.RenderingContext;
+import org.apache.fop.svg.SVGEventProducer;
+
+/**
+ * Image handler implementation which handles SVG images for AFP output.
+ * <p>
+ * Note: This class is not intended to be used as an {@link AFPImageHandler} but only as an
+ * {@link ImageHandler}. It subclasses {@link AFPImageHandler} only to get access to common
+ * methods.
+ */
+public class AFPImageHandlerSVG implements ImageHandler {
+
+ private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {
+ BatikImageFlavors.SVG_DOM
+ };
+
+ /** {@inheritDoc} */
+ protected AFPDataObjectInfo createDataObjectInfo() {
+ return new AFPGraphicsObjectInfo();
+ }
+
+ /** {@inheritDoc} */
+ public void handleImage(RenderingContext context, Image image, Rectangle pos)
+ throws IOException {
+ AFPRenderingContext afpContext = (AFPRenderingContext)context;
+ ImageXMLDOM imageSVG = (ImageXMLDOM)image;
+ FOUserAgent userAgent = afpContext.getUserAgent();
+
+ AFPGraphicsObjectInfo graphicsObjectInfo = (AFPGraphicsObjectInfo)createDataObjectInfo();
+ AFPResourceInfo resourceInfo = graphicsObjectInfo.getResourceInfo();
+ setDefaultToInlineResourceLevel(graphicsObjectInfo);
+
+ // Create a new AFPGraphics2D
+ final boolean textAsShapes = false; //afpInfo.strokeText(); //TODO make configurable
+ AFPGraphics2D g2d = new AFPGraphics2D(
+ textAsShapes,
+ afpContext.getPaintingState(),
+ afpContext.getResourceManager(),
+ resourceInfo,
+ afpContext.getFontInfo());
+ g2d.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
+
+ AFPPaintingState paintingState = g2d.getPaintingState();
+ paintingState.setImageUri(image.getInfo().getOriginalURI());
+
+ // Create an AFPBridgeContext
+ BridgeContext bridgeContext = AFPSVGHandler.createBridgeContext(userAgent, g2d);
+
+ // Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine)
+ // to it.
+ Document clonedDoc = BatikUtil.cloneSVGDocument(imageSVG.getDocument());
+
+ // Build the SVG DOM and provide the painter with it
+ GraphicsNode root;
+ try {
+ GVTBuilder builder = new GVTBuilder();
+ root = builder.build(bridgeContext, clonedDoc);
+ } catch (Exception e) {
+ SVGEventProducer eventProducer = SVGEventProducer.Provider.get(
+ context.getUserAgent().getEventBroadcaster());
+ eventProducer.svgNotBuilt(this, e, image.getInfo().getOriginalURI());
+ return;
+ }
+
+ paintingState.save(); // save
+
+ // Image positioning
+ AFPObjectAreaInfo objectAreaInfo = AFPImageHandler.createObjectAreaInfo(paintingState, pos);
+ graphicsObjectInfo.setObjectAreaInfo(objectAreaInfo);
+
+ //Set up painter and target
+ graphicsObjectInfo.setGraphics2D(g2d);
+ // Create Graphics2DImagePainter
+ Dimension imageSize = image.getSize().getDimensionMpt();
+ Graphics2DImagePainter painter = new Graphics2DImagePainterImpl(
+ root, bridgeContext, imageSize);
+ graphicsObjectInfo.setPainter(painter);
+
+ // Create the GOCA GraphicsObject in the DataStream
+ AFPResourceManager resourceManager = afpContext.getResourceManager();
+ resourceManager.createObject(graphicsObjectInfo);
+
+ paintingState.restore(); // resume
+ }
+
+ private void setDefaultToInlineResourceLevel(AFPGraphicsObjectInfo graphicsObjectInfo) {
+ AFPResourceInfo resourceInfo = graphicsObjectInfo.getResourceInfo();
+ //level not explicitly set/changed so default to inline for GOCA graphic objects
+ // (due to a bug in the IBM AFP Workbench Viewer (2.04.01.07), hard copy works just fine)
+ if (!resourceInfo.levelChanged()) {
+ resourceInfo.setLevel(new AFPResourceLevel(AFPResourceLevel.INLINE));
+ }
+ }
+
+ /** {@inheritDoc} */
+ public int getPriority() {
+ return 400;
+ }
+
+ /** {@inheritDoc} */
+ public Class getSupportedImageClass() {
+ return ImageXMLDOM.class;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor[] getSupportedImageFlavors() {
+ return FLAVORS;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isCompatible(RenderingContext targetContext, Image image) {
+ boolean supported = (image == null || (image instanceof ImageXMLDOM
+ && image.getFlavor().isCompatible(BatikImageFlavors.SVG_DOM)))
+ && targetContext instanceof AFPRenderingContext;
+ if (supported) {
+ String mode = (String)targetContext.getHint(ImageHandlerUtil.CONVERSION_MODE);
+ if (ImageHandlerUtil.isConversionModeBitmap(mode)) {
+ //Disabling this image handler automatically causes a bitmap to be generated
+ return false;
+ }
+ }
+ return supported;
+ }
+
+}
--- /dev/null
+/*
+ * 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.render.afp;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Paint;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
+import java.io.IOException;
+import java.util.Map;
+
+import org.w3c.dom.Document;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.image.loader.ImageProcessingHints;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+
+import org.apache.fop.afp.AFPBorderPainter;
+import org.apache.fop.afp.AFPPaintingState;
+import org.apache.fop.afp.AFPRectanglePainter;
+import org.apache.fop.afp.AFPUnitConverter;
+import org.apache.fop.afp.DataStream;
+import org.apache.fop.afp.RectanglePaintingInfo;
+import org.apache.fop.afp.fonts.AFPFont;
+import org.apache.fop.afp.fonts.AFPFontAttributes;
+import org.apache.fop.afp.fonts.AFPPageFonts;
+import org.apache.fop.afp.fonts.CharacterSet;
+import org.apache.fop.afp.modca.AbstractPageObject;
+import org.apache.fop.afp.modca.PresentationTextObject;
+import org.apache.fop.afp.ptoca.PtocaBuilder;
+import org.apache.fop.afp.ptoca.PtocaProducer;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontTriplet;
+import org.apache.fop.render.RenderingContext;
+import org.apache.fop.render.intermediate.AbstractIFPainter;
+import org.apache.fop.render.intermediate.IFContext;
+import org.apache.fop.render.intermediate.IFException;
+import org.apache.fop.render.intermediate.IFState;
+import org.apache.fop.traits.BorderProps;
+import org.apache.fop.traits.RuleStyle;
+
+/**
+ * IFPainter implementation that produces AFP (MO:DCA).
+ */
+public class AFPPainter extends AbstractIFPainter {
+
+ /** logging instance */
+ private static Log log = LogFactory.getLog(AFPPainter.class);
+
+ private static final int X = 0;
+ private static final int Y = 1;
+
+ private AFPDocumentHandler documentHandler;
+
+ /** the line painter */
+ private AFPBorderPainter borderPainter;
+ /** the rectangle painter */
+ private AFPRectanglePainter rectanglePainter;
+
+ /** unit converter */
+ private final AFPUnitConverter unitConv;
+
+ /**
+ * Default constructor.
+ * @param documentHandler the parent document handler
+ */
+ public AFPPainter(AFPDocumentHandler documentHandler) {
+ super();
+ this.documentHandler = documentHandler;
+ this.state = IFState.create();
+ this.borderPainter = new AFPBorderPainter(getPaintingState(), getDataStream());
+ this.rectanglePainter = new AFPRectanglePainter(getPaintingState(), getDataStream());
+ this.unitConv = getPaintingState().getUnitConverter();
+ }
+
+ /** {@inheritDoc} */
+ protected IFContext getContext() {
+ return this.documentHandler.getContext();
+ }
+
+ FontInfo getFontInfo() {
+ return this.documentHandler.getFontInfo();
+ }
+
+ AFPPaintingState getPaintingState() {
+ return this.documentHandler.getPaintingState();
+ }
+
+ DataStream getDataStream() {
+ return this.documentHandler.getDataStream();
+ }
+
+ /** {@inheritDoc} */
+ public void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect)
+ throws IFException {
+ //AFP doesn't support clipping, so we treat viewport like a group
+ //this is the same code as for startGroup()
+ try {
+ saveGraphicsState();
+ concatenateTransformationMatrix(transform);
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in startViewport()", ioe);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endViewport() throws IFException {
+ try {
+ restoreGraphicsState();
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in endViewport()", ioe);
+ }
+ }
+
+ private void concatenateTransformationMatrix(AffineTransform at) {
+ if (!at.isIdentity()) {
+ getPaintingState().concatenate(at);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startGroup(AffineTransform transform) throws IFException {
+ try {
+ saveGraphicsState();
+ concatenateTransformationMatrix(transform);
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in startGroup()", ioe);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endGroup() throws IFException {
+ try {
+ restoreGraphicsState();
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in endGroup()", ioe);
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected Map createDefaultImageProcessingHints(ImageSessionContext sessionContext) {
+ Map hints = super.createDefaultImageProcessingHints(sessionContext);
+
+ //AFP doesn't support alpha channels
+ hints.put(ImageProcessingHints.TRANSPARENCY_INTENT,
+ ImageProcessingHints.TRANSPARENCY_INTENT_IGNORE);
+ return hints;
+ }
+
+ /** {@inheritDoc} */
+ protected RenderingContext createRenderingContext() {
+ AFPRenderingContext psContext = new AFPRenderingContext(
+ getUserAgent(),
+ documentHandler.getResourceManager(),
+ getPaintingState(),
+ getFontInfo(),
+ getContext().getForeignAttributes());
+ return psContext;
+ }
+
+ /** {@inheritDoc} */
+ public void drawImage(String uri, Rectangle rect) throws IFException {
+ drawImageUsingURI(uri, rect);
+ }
+
+ /** {@inheritDoc} */
+ public void drawImage(Document doc, Rectangle rect) throws IFException {
+ drawImageUsingDocument(doc, rect);
+ }
+
+ /** {@inheritDoc} */
+ public void clipRect(Rectangle rect) throws IFException {
+ //Not supported!
+ }
+
+ private float toPoint(int mpt) {
+ return mpt / 1000f;
+ }
+
+ /** {@inheritDoc} */
+ public void fillRect(Rectangle rect, Paint fill) throws IFException {
+ if (fill == null) {
+ return;
+ }
+ if (rect.width != 0 && rect.height != 0) {
+ if (fill instanceof Color) {
+ getPaintingState().setColor((Color)fill);
+ } else {
+ throw new UnsupportedOperationException("Non-Color paints NYI");
+ }
+ RectanglePaintingInfo rectanglePaintInfo = new RectanglePaintingInfo(
+ toPoint(rect.x), toPoint(rect.y), toPoint(rect.width), toPoint(rect.height));
+ rectanglePainter.paint(rectanglePaintInfo);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after,
+ BorderProps start, BorderProps end) throws IFException {
+ if (before != null || after != null || start != null || end != null) {
+ /*
+ try {
+ endTextObject();
+ this.borderPainter.drawBorders(rect, before, after, start, end);
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in drawBorderRect()", ioe);
+ }*/
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void drawLine(Point start, Point end, int width, Color color, RuleStyle style)
+ throws IFException {
+ /*
+ try {
+ endTextObject();
+ this.borderPainter.drawLine(start, end, width, color, style);
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in drawLine()", ioe);
+ }*/
+ }
+
+ /** {@inheritDoc} */
+ public void drawText(int x, int y, final int[] dx, int[] dy, final String text)
+ throws IFException {
+ int fontSize = this.state.getFontSize();
+ getPaintingState().setFontSize(fontSize);
+
+ FontTriplet triplet = new FontTriplet(
+ state.getFontFamily(), state.getFontStyle(), state.getFontWeight());
+ //TODO Ignored: state.getFontVariant()
+ String fontKey = getFontInfo().getInternalFontKey(triplet);
+
+ // register font as necessary
+ Map/*<String,FontMetrics>*/ fontMetricMap = documentHandler.getFontInfo().getFonts();
+ final AFPFont afpFont = (AFPFont)fontMetricMap.get(fontKey);
+ AFPPageFonts pageFonts = getPaintingState().getPageFonts();
+ AFPFontAttributes fontAttributes = pageFonts.registerFont(fontKey, afpFont, fontSize);
+
+ final int fontReference = fontAttributes.getFontReference();
+
+ final int[] coords = unitConv.mpts2units(new float[] {x, y} );
+
+ final CharacterSet charSet = afpFont.getCharacterSet(fontSize);
+
+ AbstractPageObject page = getDataStream().getCurrentPage();
+ PresentationTextObject pto = page.getPresentationTextObject();
+ try {
+ pto.createControlSequences(new PtocaProducer() {
+
+ public void produce(PtocaBuilder builder) throws IOException {
+ Point p = getPaintingState().getPoint(coords[X], coords[Y]);
+ builder.setTextOrientation(getPaintingState().getRotation());
+ builder.absoluteMoveBaseline(p.y);
+ builder.absoluteMoveInline(p.x);
+
+ builder.setVariableSpaceCharacterIncrement(0);
+ builder.setInterCharacterAdjustment(0);
+ builder.setExtendedTextColor(state.getTextColor());
+ builder.setCodedFont((byte)fontReference);
+
+ if (dx == null) {
+ //No glyph-shifting necessary, so take a shortcut
+ builder.addTransparentData(text.getBytes(charSet.getEncoding()));
+ } else {
+ int l = text.length();
+ int dxl = dx.length;
+ StringBuffer sb = new StringBuffer();
+
+ if (dxl > 0 && dx[0] != 0) {
+ int dxu = Math.round(unitConv.mpt2units(dx[0]));
+ builder.relativeMoveInline(-dxu);
+ }
+ for (int i = 0; i < l; i++) {
+ sb.append(text.charAt(i));
+ float glyphAdjust = 0;
+
+ if (i < dxl - 1) {
+ glyphAdjust += dx[i + 1];
+ }
+
+ if (glyphAdjust != 0) {
+ if (sb.length() > 0) {
+ String t = sb.toString();
+ builder.addTransparentData(t.getBytes(charSet.getEncoding()));
+ sb.setLength(0);
+ }
+ int increment = Math.round(unitConv.mpt2units(glyphAdjust));
+ builder.relativeMoveInline(increment);
+ }
+ }
+ if (sb.length() > 0) {
+ String t = sb.toString();
+ builder.addTransparentData(t.getBytes(charSet.getEncoding()));
+ }
+ }
+ }
+
+ });
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in drawText()", ioe);
+ }
+ }
+
+ /**
+ * Saves the graphics state of the rendering engine.
+ * @throws IOException if an I/O error occurs
+ */
+ protected void saveGraphicsState() throws IOException {
+ getPaintingState().save();
+ }
+
+ /**
+ * Restores the last graphics state of the rendering engine.
+ * @throws IOException if an I/O error occurs
+ */
+ protected void restoreGraphicsState() throws IOException {
+ getPaintingState().restore();
+ }
+
+}
import java.util.List;
import java.util.Map;
+import org.apache.xmlgraphics.image.loader.ImageException;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.image.loader.util.ImageUtil;
+import org.apache.xmlgraphics.ps.ImageEncodingHelper;
+
import org.apache.fop.afp.AFPBorderPainter;
import org.apache.fop.afp.AFPDataObjectInfo;
import org.apache.fop.afp.AFPEventProducer;
import org.apache.fop.render.RendererContext;
import org.apache.fop.render.afp.extensions.AFPElementMapping;
import org.apache.fop.render.afp.extensions.AFPPageSetup;
-import org.apache.xmlgraphics.image.loader.ImageException;
-import org.apache.xmlgraphics.image.loader.ImageFlavor;
-import org.apache.xmlgraphics.image.loader.ImageInfo;
-import org.apache.xmlgraphics.image.loader.ImageManager;
-import org.apache.xmlgraphics.image.loader.ImageSessionContext;
-import org.apache.xmlgraphics.image.loader.util.ImageUtil;
-import org.apache.xmlgraphics.ps.ImageEncodingHelper;
/**
* This is an implementation of a FOP Renderer that renders areas to AFP.
* (ie. at the start or end of the page).
*
*/
-public class AFPRenderer extends AbstractPathOrientedRenderer {
+public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCustomizable {
private static final int X = 0;
private static final int Y = 1;
int textWordSpaceAdjust = text.getTextWordSpaceAdjust();
int textLetterSpaceAdjust = text.getTextLetterSpaceAdjust();
int textWidth = font.getWidth(' ', fontSize) / 1000;
+ textWidth = 0; //JM, the above is strange
int variableSpaceCharacterIncrement
= textWidth + textWordSpaceAdjust + textLetterSpaceAdjust;
}
/**
- * Sets the rotation to be used for landsacpe pages, valid values are 0, 90,
+ * Sets the rotation to be used for landscape pages, valid values are 0, 90,
* 180, 270 (default).
*
* @param rotation
paintingState.setLandscapeRotation(rotation);
}
- /**
- * Sets the number of bits used per pixel
- *
- * @param bitsPerPixel
- * number of bits per pixel
- */
+ // ---=== AFPCustomizable ===---
+
+ /** {@inheritDoc} */
public void setBitsPerPixel(int bitsPerPixel) {
paintingState.setBitsPerPixel(bitsPerPixel);
}
- /**
- * Sets whether images are color or not
- *
- * @param colorImages
- * color image output
- */
+ /** {@inheritDoc} */
public void setColorImages(boolean colorImages) {
paintingState.setColorImages(colorImages);
}
- /**
- * Sets whether images are supported natively or not
- *
- * @param nativeImages
- * native image support
- */
+ /** {@inheritDoc} */
public void setNativeImagesSupported(boolean nativeImages) {
paintingState.setNativeImagesSupported(nativeImages);
}
- /**
- * Sets the output/device resolution
- *
- * @param resolution
- * the output resolution (dpi)
- */
+ /** {@inheritDoc} */
public void setResolution(int resolution) {
paintingState.setResolution(resolution);
}
- /**
- * Returns the output/device resolution.
- *
- * @return the resolution in dpi
- */
+ /** {@inheritDoc} */
public int getResolution() {
return paintingState.getResolution();
}
- /**
- * Sets the default resource group file path
- * @param filePath the default resource group file path
- */
+ /** {@inheritDoc} */
public void setDefaultResourceGroupFilePath(String filePath) {
resourceManager.setDefaultResourceGroupFilePath(filePath);
}
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.fop.afp.fonts.AFPFontCollection;
import org.apache.fop.afp.fonts.AFPFontInfo;
import org.apache.fop.afp.fonts.CharacterSet;
import org.apache.fop.afp.fonts.FopCharacterSet;
import org.apache.fop.afp.fonts.RasterFont;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fonts.FontCollection;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontManager;
import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.fonts.FontUtil;
import org.apache.fop.fonts.Typeface;
import org.apache.fop.render.PrintRendererConfigurator;
import org.apache.fop.render.Renderer;
+import org.apache.fop.render.intermediate.IFDocumentHandler;
+import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
import org.apache.fop.util.LogUtil;
/**
* AFP Renderer configurator
*/
-public class AFPRendererConfigurator extends PrintRendererConfigurator {
+public class AFPRendererConfigurator extends PrintRendererConfigurator
+ implements IFDocumentHandlerConfigurator {
/**
* Default constructor
Configuration cfg = super.getRendererConfig(renderer);
if (cfg != null) {
AFPRenderer afpRenderer = (AFPRenderer)renderer;
+
try {
List/*<AFPFontInfo>*/ fontList = buildFontListFromConfiguration(cfg);
afpRenderer.setFontList(fontList);
userAgent.getFactory().validateUserConfigStrictly());
}
- // image information
- Configuration imagesCfg = cfg.getChild("images");
+ configure(afpRenderer, cfg);
+ }
+ }
+
+ private void configure(AFPCustomizable customizable, Configuration cfg) throws FOPException {
+
+ // image information
+ Configuration imagesCfg = cfg.getChild("images");
+
+ // default to grayscale images
+ String imagesMode = imagesCfg.getAttribute("mode", IMAGES_MODE_GRAYSCALE);
+ if (IMAGES_MODE_COLOR.equals(imagesMode)) {
+ customizable.setColorImages(true);
+ } else {
+ customizable.setColorImages(false);
+ // default to 8 bits per pixel
+ int bitsPerPixel = imagesCfg.getAttributeAsInteger("bits-per-pixel", 8);
+ customizable.setBitsPerPixel(bitsPerPixel);
+ }
+
+ // native image support
+ boolean nativeImageSupport = imagesCfg.getAttributeAsBoolean("native", false);
+ customizable.setNativeImagesSupported(nativeImageSupport);
- // default to grayscale images
- String imagesMode = imagesCfg.getAttribute("mode", IMAGES_MODE_GRAYSCALE);
- if (IMAGES_MODE_COLOR.equals(imagesMode)) {
- afpRenderer.setColorImages(true);
+ // renderer resolution
+ Configuration rendererResolutionCfg = cfg.getChild("renderer-resolution", false);
+ if (rendererResolutionCfg != null) {
+ customizable.setResolution(rendererResolutionCfg.getValueAsInteger(240));
+ }
+
+ // a default external resource group file setting
+ Configuration resourceGroupFileCfg
+ = cfg.getChild("resource-group-file", false);
+ if (resourceGroupFileCfg != null) {
+ String resourceGroupDest = null;
+ try {
+ resourceGroupDest = resourceGroupFileCfg.getValue();
+ } catch (ConfigurationException e) {
+ LogUtil.handleException(log, e,
+ userAgent.getFactory().validateUserConfigStrictly());
+ }
+ File resourceGroupFile = new File(resourceGroupDest);
+ if (resourceGroupFile.canWrite()) {
+ customizable.setDefaultResourceGroupFilePath(resourceGroupDest);
} else {
- afpRenderer.setColorImages(false);
- // default to 8 bits per pixel
- int bitsPerPixel = imagesCfg.getAttributeAsInteger("bits-per-pixel", 8);
- afpRenderer.setBitsPerPixel(bitsPerPixel);
+ log.warn("Unable to write to default external resource group file '"
+ + resourceGroupDest + "'");
}
+ }
+ }
- // native image support
- boolean nativeImageSupport = imagesCfg.getAttributeAsBoolean("native", false);
- afpRenderer.setNativeImagesSupported(nativeImageSupport);
+ /** {@inheritDoc} */
+ public void configure(IFDocumentHandler documentHandler) throws FOPException {
+ Configuration cfg = super.getRendererConfig(documentHandler.getMimeType());
+ if (cfg != null) {
+ AFPDocumentHandler afpDocumentHandler = (AFPDocumentHandler)documentHandler;
+ configure(afpDocumentHandler, cfg);
+ }
+ }
- // renderer resolution
- Configuration rendererResolutionCfg = cfg.getChild("renderer-resolution", false);
- if (rendererResolutionCfg != null) {
- afpRenderer.setResolution(rendererResolutionCfg.getValueAsInteger(240));
- }
+ /** {@inheritDoc} */
+ public void setupFontInfo(IFDocumentHandler documentHandler, FontInfo fontInfo)
+ throws FOPException {
+ FontManager fontManager = userAgent.getFactory().getFontManager();
+ List fontCollections = new java.util.ArrayList();
- // a default external resource group file setting
- Configuration resourceGroupFileCfg
- = cfg.getChild("resource-group-file", false);
- if (resourceGroupFileCfg != null) {
- String resourceGroupDest = null;
- try {
- resourceGroupDest = resourceGroupFileCfg.getValue();
- } catch (ConfigurationException e) {
- LogUtil.handleException(log, e,
- userAgent.getFactory().validateUserConfigStrictly());
- }
- File resourceGroupFile = new File(resourceGroupDest);
- if (resourceGroupFile.canWrite()) {
- afpRenderer.setDefaultResourceGroupFilePath(resourceGroupDest);
- } else {
- log.warn("Unable to write to default external resource group file '"
- + resourceGroupDest + "'");
- }
+ Configuration cfg = super.getRendererConfig(documentHandler.getMimeType());
+ if (cfg != null) {
+ try {
+ List fontList = buildFontListFromConfiguration(cfg);
+ fontCollections.add(new AFPFontCollection(
+ userAgent.getEventBroadcaster(), fontList));
+ } catch (ConfigurationException e) {
+ LogUtil.handleException(log, e,
+ userAgent.getFactory().validateUserConfigStrictly());
}
+
}
+
+ fontManager.setup(fontInfo,
+ (FontCollection[])fontCollections.toArray(
+ new FontCollection[fontCollections.size()]));
+ documentHandler.setFontInfo(fontInfo);
}
}
import java.util.Map;
import org.apache.avalon.framework.configuration.Configuration;
+
import org.apache.fop.afp.AFPPaintingState;
import org.apache.fop.afp.AFPResourceInfo;
import org.apache.fop.afp.AFPResourceLevel;
import org.apache.fop.afp.AFPResourceManager;
import org.apache.fop.render.AbstractRenderer;
+import org.apache.fop.render.ImageHandlerUtil;
import org.apache.fop.render.RendererContext;
import org.apache.fop.render.RendererContextConstants;
+/**
+ * AFP-specific renderer context class.
+ */
public class AFPRendererContext extends RendererContext {
/**
Map foreignAttributes = (Map)getProperty(RendererContextConstants.FOREIGN_ATTRIBUTES);
if (foreignAttributes != null) {
- String conversionMode = (String)foreignAttributes.get(CONVERSION_MODE);
- boolean paintAsBitmap = BITMAP.equalsIgnoreCase(conversionMode);
+ boolean paintAsBitmap = ImageHandlerUtil.isConversionModeBitmap(foreignAttributes);
info.setPaintAsBitmap(paintAsBitmap);
AFPForeignAttributeReader foreignAttributeReader
--- /dev/null
+/*
+ * 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.render.afp;
+
+import java.util.Map;
+
+import org.apache.xmlgraphics.util.MimeConstants;
+
+import org.apache.fop.afp.AFPPaintingState;
+import org.apache.fop.afp.AFPResourceManager;
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.render.AbstractRenderingContext;
+
+/**
+ * Rendering context for AFP (MO:DCA) production.
+ */
+public class AFPRenderingContext extends AbstractRenderingContext {
+
+ private AFPResourceManager resourceManager;
+ private AFPPaintingState paintingState;
+ private FontInfo fontInfo;
+ private Map foreignAttributes;
+
+ /**
+ * Main constructor.
+ * @param userAgent the user agent
+ * @param resourceManager the resource manager
+ * @param fontInfo the font list
+ * @param foreignAttributes a map of foreign attributes
+ */
+ public AFPRenderingContext(FOUserAgent userAgent,
+ AFPResourceManager resourceManager,
+ AFPPaintingState paintingState,
+ FontInfo fontInfo, Map foreignAttributes) {
+ super(userAgent);
+ this.resourceManager = resourceManager;
+ this.paintingState = paintingState;
+ this.fontInfo = fontInfo;
+ this.foreignAttributes = foreignAttributes;
+ }
+
+ /** {@inheritDoc} */
+ public String getMimeType() {
+ return MimeConstants.MIME_AFP;
+ }
+
+ /**
+ * Returns the resource manager.
+ * @return the resource manager
+ */
+ public AFPResourceManager getResourceManager() {
+ return this.resourceManager;
+ }
+
+ public AFPPaintingState getPaintingState() {
+ return this.paintingState;
+ }
+
+ /**
+ * Returns the font list.
+ * @return the font list
+ */
+ public FontInfo getFontInfo() {
+ return this.fontInfo;
+ }
+
+ /**
+ * Returns a Map of foreign attributes.
+ * @return the foreign attributes (Map<QName, Object>)
+ */
+ public Map getForeignAttributes() {
+ return this.foreignAttributes;
+ }
+
+}
= RendererContext.wrapRendererContext(rendererContext);
Dimension imageSize = getImageSize(wrappedContext);
Graphics2DImagePainter painter
- = createGrapics2DImagePainter(bridgeContext, root, imageSize);
+ = createGraphics2DImagePainter(bridgeContext, root, imageSize);
// Create AFPObjectAreaInfo
RendererContextWrapper rctx = RendererContext.wrapRendererContext(rendererContext);
context.setProperty(AFPRendererContextConstants.AFP_GRAYSCALE, Boolean.FALSE);
}
- /** {@inheritDoc} */
- protected Graphics2DImagePainter createGrapics2DImagePainter(BridgeContext ctx, GraphicsNode root, Dimension imageSize) {
+ private Graphics2DImagePainter createGraphics2DImagePainter(BridgeContext ctx,
+ GraphicsNode root, Dimension imageSize) {
Graphics2DImagePainter painter = null;
if (paintAsBitmap()) {
// paint as IOCA Image
package org.apache.fop.render.afp;
+import java.awt.Rectangle;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageSize;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawStream;
+
import org.apache.fop.afp.AFPDataObjectInfo;
import org.apache.fop.afp.AFPObjectAreaInfo;
import org.apache.fop.afp.AFPPaintingState;
-import org.apache.xmlgraphics.image.loader.ImageInfo;
-import org.apache.xmlgraphics.image.loader.ImageSize;
-import org.apache.xmlgraphics.image.loader.impl.ImageRawStream;
+import org.apache.fop.render.ImageHandler;
+import org.apache.fop.render.RenderingContext;
/**
* A base abstract AFP raw stream image handler
*/
-public abstract class AbstractAFPImageHandlerRawStream extends AFPImageHandler {
+public abstract class AbstractAFPImageHandlerRawStream extends AFPImageHandler
+ implements ImageHandler {
/** {@inheritDoc} */
public AFPDataObjectInfo generateDataObjectInfo(
AFPRendererImageInfo rendererImageInfo) throws IOException {
AFPDataObjectInfo dataObjectInfo = super.generateDataObjectInfo(rendererImageInfo);
-
- ImageInfo imageInfo = rendererImageInfo.getImageInfo();
- String mimeType = imageInfo.getMimeType();
- if (mimeType != null) {
- dataObjectInfo.setMimeType(mimeType);
- }
ImageRawStream rawStream = (ImageRawStream) rendererImageInfo.getImage();
+
+
+ updateDataObjectInfo(dataObjectInfo, rawStream);
+
+ setAdditionalParameters(dataObjectInfo, rawStream);
+ return dataObjectInfo;
+ }
+
+ /**
+ * Sets additional parameters on the image object info being built. By default, this
+ * method does nothing but it can be overridden to provide additional functionality.
+ * @param imageObjectInfo the image object info being built
+ * @param image the image being processed
+ */
+ protected void setAdditionalParameters(AFPDataObjectInfo imageObjectInfo,
+ ImageRawStream image) {
+ //nop
+ }
+
+ private void updateDataObjectInfo(AFPDataObjectInfo dataObjectInfo,
+ ImageRawStream rawStream) throws IOException {
+ dataObjectInfo.setMimeType(rawStream.getFlavor().getMimeType());
InputStream inputStream = rawStream.createInputStream();
try {
dataObjectInfo.setData(IOUtils.toByteArray(inputStream));
ImageSize imageSize = rawStream.getSize();
dataObjectInfo.setDataHeightRes((int) (imageSize.getDpiHorizontal() * 10));
dataObjectInfo.setDataWidthRes((int) (imageSize.getDpiVertical() * 10));
+ }
+
+ /** {@inheritDoc} */
+ public void handleImage(RenderingContext context, Image image, Rectangle pos)
+ throws IOException {
+ AFPRenderingContext afpContext = (AFPRenderingContext)context;
+
+ AFPDataObjectInfo dataObjectInfo = createDataObjectInfo();
+
+ // set resource information
+ setResourceInformation(dataObjectInfo,
+ image.getInfo().getOriginalURI(),
+ afpContext.getForeignAttributes());
+
+ // Positioning
+ dataObjectInfo.setObjectAreaInfo(createObjectAreaInfo(afpContext.getPaintingState(), pos));
// set object area info
- AFPObjectAreaInfo objectAreaInfo = dataObjectInfo.getObjectAreaInfo();
- AFPRendererContext rendererContext
- = (AFPRendererContext)rendererImageInfo.getRendererContext();
- AFPInfo afpInfo = rendererContext.getInfo();
- AFPPaintingState paintingState = afpInfo.getPaintingState();
+ //AFPObjectAreaInfo objectAreaInfo = dataObjectInfo.getObjectAreaInfo();
+ AFPPaintingState paintingState = afpContext.getPaintingState();
int resolution = paintingState.getResolution();
+ AFPObjectAreaInfo objectAreaInfo = dataObjectInfo.getObjectAreaInfo();
objectAreaInfo.setWidthRes(resolution);
objectAreaInfo.setHeightRes(resolution);
- return dataObjectInfo;
+ // Image content
+ ImageRawStream imageStream = (ImageRawStream)image;
+ updateDataObjectInfo(dataObjectInfo, imageStream);
+ setAdditionalParameters(dataObjectInfo, imageStream);
+
+ // Create image
+ afpContext.getResourceManager().createObject(dataObjectInfo);
}
}