diff options
Diffstat (limited to 'src/java/org/apache/fop/afp/ptoca/PtocaBuilder.java')
-rw-r--r-- | src/java/org/apache/fop/afp/ptoca/PtocaBuilder.java | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/src/java/org/apache/fop/afp/ptoca/PtocaBuilder.java b/src/java/org/apache/fop/afp/ptoca/PtocaBuilder.java new file mode 100644 index 000000000..b5e866380 --- /dev/null +++ b/src/java/org/apache/fop/afp/ptoca/PtocaBuilder.java @@ -0,0 +1,389 @@ +/* + * 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); + } +} |