blob: d6bd65609d43a24b611f25c15d2a8df9aacbb1df [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id$ */
package org.apache.fop.afp.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;
/**
* 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 {
// 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);
}
}