| /* |
| * 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.awt.color.ColorSpace; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| |
| import org.apache.commons.io.output.ByteArrayOutputStream; |
| |
| import org.apache.xmlgraphics.java2d.color.CIELabColorSpace; |
| import org.apache.xmlgraphics.java2d.color.ColorUtil; |
| import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives; |
| |
| import org.apache.fop.afp.fonts.CharactersetEncoder.EncodedChars; |
| import org.apache.fop.afp.modca.AxisOrientation; |
| import org.apache.fop.afp.ptoca.TransparentDataControlSequence.TransparentData; |
| import org.apache.fop.util.OCAColor; |
| import org.apache.fop.util.OCAColorSpace; |
| |
| /** |
| * 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; |
| |
| /** the current color */ |
| private Color currentColor = Color.BLACK; |
| |
| /** the current variable space increment */ |
| private int currentVariableSpaceCharacterIncrement; |
| |
| /** the current inter character adjustment */ |
| private int currentInterCharacterAdjustment; |
| |
| |
| /** |
| * 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 writeBytes(int... data) { |
| for (int d : data) { |
| baout.write(d); |
| } |
| } |
| |
| 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(); |
| writeBytes(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; |
| } |
| |
| /** |
| * 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 encodedChars The encoded text data to add. |
| * @throws IOException if an I/O error occurs |
| */ |
| public void addTransparentData(EncodedChars encodedChars) throws IOException { |
| for (TransparentData trn : new TransparentDataControlSequence(encodedChars)) { |
| newControlSequence(); |
| trn.writeTo(baout); |
| 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 |
| writeBytes(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 |
| writeBytes(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(); |
| AxisOrientation.getRightHandedAxisOrientationFor(orientation).writeTo(baout); |
| 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 (ColorUtil.isSameColor(col, currentColor)) { |
| return; |
| } |
| if (col instanceof ColorWithAlternatives) { |
| ColorWithAlternatives cwa = (ColorWithAlternatives)col; |
| Color alt = cwa.getFirstAlternativeOfType(ColorSpace.TYPE_CMYK); |
| if (alt != null) { |
| col = alt; |
| } |
| } |
| ColorSpace cs = col.getColorSpace(); |
| |
| newControlSequence(); |
| if (col.getColorSpace().getType() == ColorSpace.TYPE_CMYK) { |
| // Color space - 0x04 = CMYK, all else are reserved and must be zero |
| writeBytes(0x00, 0x04, 0x00, 0x00, 0x00, 0x00); |
| writeBytes(8, 8, 8, 8); // Number of bits in component 1, 2, 3 & 4 respectively |
| float[] comps = col.getColorComponents(null); |
| assert comps.length == 4; |
| for (int i = 0; i < 4; i++) { |
| int component = Math.round(comps[i] * 255); |
| writeBytes(component); |
| } |
| } else if (cs instanceof CIELabColorSpace) { |
| // Color space - 0x08 = CIELAB, all else are reserved and must be zero |
| writeBytes(0x00, 0x08, 0x00, 0x00, 0x00, 0x00); |
| writeBytes(8, 8, 8, 0); // Number of bits in component 1,2,3 & 4 |
| //Sadly, 16 bit components don't seem to work |
| float[] colorComponents = col.getColorComponents(null); |
| int l = Math.round(colorComponents[0] * 255f); |
| int a = Math.round(colorComponents[1] * 255f) - 128; |
| int b = Math.round(colorComponents[2] * 255f) - 128; |
| writeBytes(l, a, b); // l*, a* and b* |
| } else if (cs instanceof OCAColorSpace) { |
| // Color space - 0x40 = OCA, all else are reserved and must be zero |
| writeBytes(0x00, 0x40, 0x00, 0x00, 0x00, 0x00); |
| writeBytes(16, 0, 0, 0); // Number of bits in each component |
| int ocaColor = ((OCAColor) col).getOCA(); |
| writeBytes((ocaColor & 0xFF00) >> 8, ocaColor & 0xFF); |
| } else { |
| // Color space - 0x01 = RGB, all else are reserved and must be zero |
| writeBytes(0x00, 0x01, 0x00, 0x00, 0x00, 0x00); |
| writeBytes(8, 8, 8, 0); // Number of bits in component 1, 2, 3 & 4 respectively |
| writeBytes(col.getRed(), col.getGreen(), col.getBlue()); // RGB 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 |
| writeBytes(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); |
| } |
| } |