All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mgz.afp.triplets.Triplet Maven / Gradle / Ivy

Go to download

Alpheus AFP Parser is a library and parser for the IBM Advanced Function Presentation (AFP) document/print stream format. Alpheus covers all AFP specifications: MO:DCA, BCOCA, CMOCA, FOCA, GOCA, IOCA, and PTOCA. It is a complete implementation. Every Structured Field, Repeating Group, and Triplet is fully implemented as Java class. Alpheus AFP Parser was written from scratch and has no external dependencies. Copyright 2015,2016 Rudolf Fiala Alpheus AFP Parser is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

The newest version!
/*
Copyright 2015 Rudolf Fiala

This file is part of Alpheus AFP Parser.

Alpheus AFP Parser is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Alpheus AFP Parser is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Alpheus AFP Parser.  If not, see 
*/
package com.mgz.afp.triplets;

import com.mgz.afp.base.StructuredField;
import com.mgz.afp.base.annotations.AFPField;
import com.mgz.afp.base.annotations.AFPType;
import com.mgz.afp.enums.*;
import com.mgz.afp.exceptions.AFPParserException;
import com.mgz.afp.exceptions.IAFPDecodeableWriteable;
import com.mgz.afp.parser.AFPParserConfiguration;
import com.mgz.afp.triplets.Triplet.ColorFidelity.ExceptionContinuationRule;
import com.mgz.afp.triplets.Triplet.ColorFidelity.ExceptionReportingRule;
import com.mgz.afp.triplets.Triplet.ResourceObjectType.ROT_ObjectType;
import com.mgz.util.Constants;
import com.mgz.util.UtilBinaryDecoding;
import com.mgz.util.UtilCharacterEncoding;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;

@AFPType
public abstract class Triplet implements IAFPDecodeableWriteable {
  public static short UNFORTUNATE_TRIPLETID = 0x21;
  @AFPField(isEditable = false, isHidden = true)
  short length;
  @AFPField(isHidden = true)
  TripletID tripletID;

  @Override
  public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException {
    this.length = UtilBinaryDecoding.parseShort(sfData, offset, 1);
    tripletID = TripletID.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + 1, 1));
  }

  public short getLength() {
    return length;
  }

  public void setLength(short length) {
    this.length = length;
  }

  public TripletID getTripletID() {
    return tripletID;
  }

  public void setTripletID(TripletID tripletID) {
    this.tripletID = tripletID;
  }

  public enum TripletID {
    Undefined(0x00),
    CodedGraphicCharacterSetGlobalID(0x01),
    FullyQualifiedName(0x02),
    MappingOption(0x04),
    ObjectClassification(0x10),
    MODCAInterchangeSet(0x18),

    TextOrientation(0x1D), // Retired.
    LineDataObjectPositionMigration(0x27), // Retired.


    FontDescriptorSpecification(0x1F),
    FontCodedGraphicCharacterSetGlobalID(0x20),


    /**
     * MODCA page 379.

The Resource Object Type triplet identifies the type of object * enveloped by the Begin Resource (BRS) and End Resource (ERS) structured fields.

* UNFORTUNATLY:
A similar triplet, the Object Function Set Specification triplet, * that unfortunately also uses triplet ID X'21', is retired but is still used on the BDT * structured field; see “Object Function Set Specification Triplet X'21'” on page 570. */ ResourceObjectType(UNFORTUNATE_TRIPLETID), ObjectFunctionSetSpecification_Retired(UNFORTUNATE_TRIPLETID), ExtendedResourceLocalIdentifier(0x22), ResourceLocalIdentifier(0x24), ResourceSectionNumber(0x25), CharacterRotation(0x26), ObjectByteOffset(0x2D), AttributeValue(0x36), DescriptorPosition(0x43), MediaEjectControl(0x45), PageOverlayConditionalProcessing(0x46), ResourceUsageAttribute(0x47), MeasurementUnits(0x4B), ObjectAreaSize(0x4C), AreaDefinition(0x4D), ColorSpecification(0x4E), EncodingSchemeID(0x50), MediumMapPageNumber(0x56), ObjectByteExtent(0x57), ObjectStructuredFieldOffset(0x58), ObjectStructuredFieldExtent(0x59), ObjectOffset(0x5A), FontHorizontalScaleFactor(0x5D), ObjectCount(0x5E), LocalObjectDateAndTimeStamp(0x62), ObjectChecksum(0x63), // Retired. ObjectOriginIdentifier(0x64), // Retired. Comment(0x65), MediumOrientation(0x68), ResourceObjectInclude(0x6C), PresentationSpaceResetMixing(0x70), PresentationSpaceMixingRule(0x71), UniversalDateAndTimeStamp(0x72), IMMInsertionTriplet(0x73), // Retired. TonerSaver(0x74), ColorFidelity(0x75), FontFidelity(0x78), AttributeQualifier(0x80), PagePositionInformation(0x81), ParameterValue(0x82), PresentationControl(0x83), FontResolutionAndMetricTechnology(0x84), FinishingOperation(0x85), TextFidelity(0x86), MediaFidelity(0x87), FinishingFidelity(0x88), DataObjectFontDescriptor(0x8B), LocaleSelector(0x8C), UP3iFinishingOperation(0x8E), ColorManagementResourceDescriptor(0x91), RenderingIntent(0x95), CMRTagFidelity(0x96), DeviceAppearance(0x97), ImageResolution(0x9A), ObjectContainerPresentationSpaceSize(0x9C); int code; TripletID(int code) { this.code = code; } public static TripletID valueOf(short codeByte) throws AFPParserException { for (TripletID id : values()) { if (id.code == codeByte) { return id; } } throw new AFPParserException(TripletID.class.getSimpleName() + ": the ID 0x" + Integer.toHexString(codeByte) + " is undefined."); } public int toByte() { return code; } } /** * Specifies how the GID will be used, eg. in Fully Qualified Name Triplet 0x02. */ public enum GlobalID_Use { ReplaceFirstGIDBame(0x01), FontFamilyName(0x07), FontTypefaceName(0x08), MODCAResourceHierarchyReference(0x09), BeginResourceGroupReference(0x0A), AttributeGID(0x0B), ProcessElementGID(0x0C), BeginPageGroupReference(0x0D), MediaTypeReference(0x11), MediaDestinationReference(0x12), ColorManagementResourceReference(0x41), DataObjectFontBaseFontIdentifier(0x6E), DataObjectFontLinkedFontIdentifier(0x7E), BeginDocumentReference(0x83), ResourceObjectReference(0x84), CodePageNameReference(0x85), FontCharacterSetNameReference(0x86), BeginPageReference(0x87), BeginMediumMapReference(0x8D), CodedFontNameReference(0x8E), BeginDocumentIndexReference(0x98), BeginOverlayReference(0xB0), DataObjectInternalResourceReference(0xBE), IndexElementGID(0xCA), OtherObjectDataReference(0xCE), DataObjectExternalResourceReference(0xDE); int code; GlobalID_Use(int code) { this.code = code; } public static GlobalID_Use valueOf(short codeByte) { for (GlobalID_Use gidu : values()) { if (gidu.code == codeByte) { return gidu; } } return null; } public int toByte() { return code; } } /** * Specifies the GID format */ public enum GlobalID_Format { CharacterString(0x00), OID(0x10), URL(0x20); int code; GlobalID_Format(int code) { this.code = code; } public static GlobalID_Format valueOf(byte codeByte) { for (GlobalID_Format f : values()) { return f; } return null; } public int toByte() { return code; } } /** * The {@link Undefined} triplet is used when problem occur with corrupted AFP Data. The {@link * #tripletData} field contains the entire of the data. The {@link TripletID} field contains * {@link TripletID#Undefined} and is not read from the data given to {@link #decodeAFP(byte[], * int, int, AFPParserConfiguration)}, nor is it written by {@link #writeAFP(OutputStream, * AFPParserConfiguration)}.

The method {@link #writeAFP(OutputStream, * AFPParserConfiguration)} simple writes the data contained in field {@link #tripletData}. In * addition it resets the value of the length field. */ public static class Undefined extends Triplet { byte[] tripletData; AFPParserException parsingException; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { length = UtilBinaryDecoding.parseShort(sfData, offset, 1); if (length > 2) { tripletData = new byte[length]; System.arraycopy(sfData, offset, tripletData, 0, tripletData.length); } else { tripletData = null; } } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { if (tripletData != null) { length = (short) tripletData.length; os.write(tripletData); } else { os.write(0); os.write(0); } } public byte[] getTripletData() { return tripletData; } public void setTripletData(byte[] tripletData) { this.tripletData = tripletData; } public AFPParserException getParsingException() { return parsingException; } public void setParsingException(AFPParserException parsingException) { this.parsingException = parsingException; } } /** * MO:DCA, page 349.

The Coded Graphic Character Set Global Identifier (CGCSGID) triplet * is used to establish the values of the code page and character set for interpretation of all * structured field parameters having a CHAR data type, such as name parameters, except where such * parameters define a fixed encoding. An example of a parameter that defines its own encoding is * the character string specified with a Fully Qualified Name (X'02') triplet using FQNFmt = X'20' * - URL, which is encoded using the US-ASCII coded character set. */ public static class CodedGraphicCharacterSetGlobalID extends Triplet { int graphicCharacterSetGlobalID; int codePageGlobalID_codedCharacterSetID; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); graphicCharacterSetGlobalID = UtilBinaryDecoding.parseInt(sfData, offset + 2, 2); codePageGlobalID_codedCharacterSetID = UtilBinaryDecoding.parseInt(sfData, offset + 4, 2); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(tripletID.toByte()); baos.write(UtilBinaryDecoding.intToByteArray(graphicCharacterSetGlobalID, 2)); baos.write(UtilBinaryDecoding.intToByteArray(codePageGlobalID_codedCharacterSetID, 2)); length = (short) (baos.size() + 1); os.write(UtilBinaryDecoding.shortToByteArray(length, 1)); os.write(baos.toByteArray()); } /** * Returns true, if this in the "CCSID Form", meaning the {@link #codePageGlobalID_codedCharacterSetID} * contains a codedCharacterSetID (CCSID) instead of a code page global ID. */ public boolean isCCSIDForm() { return graphicCharacterSetGlobalID == 0; } } /** * MO:DCA, page 353.

The Fully Qualified Name triplet enables the identification and * referencing of objects using Global Identifiers (GIDs). */ public static class FullyQualifiedName extends Triplet { GlobalID_Use type; GlobalID_Format format; byte[] nameAsBytes; String nameAsString; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); type = GlobalID_Use.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + 2, 1)); format = GlobalID_Format.valueOf(sfData[offset + 3]); int actualLength = StructuredField.getActualLength(sfData, offset, length); nameAsBytes = new byte[actualLength - 4]; System.arraycopy(sfData, offset + 4, nameAsBytes, 0, nameAsBytes.length); nameAsString = new String(nameAsBytes, config.getAfpCharSet()); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(tripletID.toByte()); baos.write(type.toByte()); baos.write(format.toByte()); if (nameAsBytes != null) { baos.write(nameAsBytes); } else { baos.write(nameAsString.getBytes(config.getAfpCharSet())); } length = (short) (baos.size() + 1); os.write(UtilBinaryDecoding.shortToByteArray(length, 1)); os.write(baos.toByteArray()); } public GlobalID_Use getType() { return type; } public void setType(GlobalID_Use type) { this.type = type; } public GlobalID_Format getFormat() { return format; } public void setFormat(GlobalID_Format format) { this.format = format; } public byte[] getNameAsBytes() { return nameAsBytes; } public void setNameAsBytes(byte[] nameAsBytes) { this.nameAsBytes = nameAsBytes; } public String getNameAsString() { return nameAsString; } public void setNameAsString(String nameAsString) { this.nameAsString = nameAsString; } } /** * MO:DCA, page 365.

The Mapping Option is used to specify the mapping of a data object * presentation space to an object area. */ public static class MappingOption extends Triplet { DataObjecMapingOption dataObjecMapingOption; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); dataObjecMapingOption = DataObjecMapingOption.valueOf(sfData[offset + 2]); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { os.write(UtilBinaryDecoding.shortToByteArray(length, 1)); os.write(tripletID.toByte()); os.write(dataObjecMapingOption.toByte()); } public enum DataObjecMapingOption { Position(0x00), PositionAndTrim(0x10), ScaleToFit(0x20), CenterAndTrim(0x30), ImagePointToPel(0x41), ImagePointToPelWithDoubleDot(0x42), ReplicateAndTrim(0x50), ScaleToFill(0x60), UP3iPrintDataMapping(0x70); int code; DataObjecMapingOption(int code) { this.code = code; } public static DataObjecMapingOption valueOf(byte codeByte) { for (DataObjecMapingOption o : values()) { if (o.code == codeByte) { return o; } } return null; } public int toByte() { return code; } } } /** * MO:DCA, page 368.

The Object Classification is used to classify and identify object * data. The object data may or may not be defined by an AFP presentation architecture. */ public static class ObjectClassification extends Triplet { byte reserved2 = 0x00; ObjectClass objectClass; byte[] reserved4_5 = new byte[2]; EnumSet structureFlags; byte[] registeredObjectID; // 16 bytes. String objectTypeName; String objectVersion; String companyName; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { StructuredField.checkDataLength(sfData, offset, length, 24); super.decodeAFP(sfData, offset, length, config); reserved2 = sfData[offset + 2]; objectClass = ObjectClass.valueOf(sfData[offset + 3]); reserved4_5 = new byte[2]; System.arraycopy(sfData, offset + 4, reserved4_5, 0, reserved4_5.length); structureFlags = StructureFlag.valueOf(UtilBinaryDecoding.parseInt(sfData, offset + 6, 2)); registeredObjectID = new byte[16]; System.arraycopy(sfData, offset + 8, registeredObjectID, 0, registeredObjectID.length); int actualLength = StructuredField.getActualLength(sfData, offset, length); if (actualLength > 24) { objectTypeName = new String(sfData, offset + 24, 32, config.getAfpCharSet()); } else { objectTypeName = null; } if (actualLength > 56) { objectVersion = new String(sfData, offset + 56, 8, config.getAfpCharSet()); } else { objectVersion = null; } if (actualLength > 64) { companyName = new String(sfData, offset + 64, 32, config.getAfpCharSet()); } else { companyName = null; } } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(tripletID.toByte()); baos.write(reserved2); baos.write(objectClass.toByte()); baos.write(reserved4_5); baos.write(StructureFlag.toBytes(structureFlags)); if (objectTypeName != null) { baos.write(UtilCharacterEncoding.stringToByteArray(objectTypeName, config.getAfpCharSet(), 32, Constants.EBCDIC_BLANK)); if (objectVersion != null) { baos.write(UtilCharacterEncoding.stringToByteArray(objectVersion, config.getAfpCharSet(), 8, Constants.EBCDIC_BLANK)); if (companyName != null) { baos.write(UtilCharacterEncoding.stringToByteArray(companyName, config.getAfpCharSet(), 32, Constants.EBCDIC_BLANK)); } } } length = (short) (baos.size() + 1); os.write(UtilBinaryDecoding.shortToByteArray(length, 1)); os.write(baos.toByteArray()); } /** * Sets the given {@link StructureFlag} and unsets mutual exclusive flags in {@link * #structureFlags}. */ public void setStructureFlag(StructureFlag flag) { StructureFlag.setFlag(structureFlags, flag); } /** * Specifies the object class based on differentiators such as temporal characteristics and * presentation form. */ public enum ObjectClass { TimeInvariantPaginatedPresentationObject(0x01), TimeVariantPresentationObject(0x10), ExecutableProgram(0x20), SetupFile(0x30), SecondaryResource(0x40), DataObjectFont(0x41); int code; ObjectClass(int code) { this.code = code; } public static ObjectClass valueOf(byte codeByte) throws AFPParserException { for (ObjectClass oc : values()) { if (oc.code == codeByte) { return oc; } } throw new AFPParserException(ObjectClass.class.getSimpleName() + ": object class 0x" + Integer.toHexString(codeByte) + " is unknwon."); } public int toByte() { return code; } } public enum StructureFlag implements IMutualExclusiveGroupedFlag { // b 0-1 OC_Reserved(0), OC_DataNotCarriedInObjectContainer(0), OC_UnknownContainerStructure(0), OC_DataCarriedInObjectContainer(0), // b 2-3 OEG_Reserved(1), OEG_NotIncluded(1), OEG_Unknown(1), OEG_Included(1), // b 4-5 OCD_Reserved(2), OCD_DataNotCarriedInOCD(2), OCD_UnknownIfOCDCarriesData(2), OCD_DataCarriedInOCD(2); static MutualExclusiveGroupedFlagHandler handler = new MutualExclusiveGroupedFlagHandler(); int group; StructureFlag(int code) { this.group = code; } public static EnumSet valueOf(int codeByte) { EnumSet result = EnumSet.noneOf(StructureFlag.class); if ((codeByte & 0xC000) == 0) { result.add(OC_Reserved); } else if ((codeByte & 0x8000) != 0 && (codeByte & 0x4000) != 0) { result.add(OC_DataCarriedInObjectContainer); } else if ((codeByte & 0x8000) != 0) { result.add(OC_UnknownContainerStructure); } else if ((codeByte & 0x4000) != 0) { result.add(OC_DataNotCarriedInObjectContainer); } if ((codeByte & 0x3000) == 0) { result.add(OEG_Reserved); } else if ((codeByte & 0x2000) != 0 && (codeByte & 0x1000) != 0) { result.add(OEG_Included); } else if ((codeByte & 0x2000) != 0) { result.add(OEG_Unknown); } else if ((codeByte & 0x1000) != 0) { result.add(OEG_NotIncluded); } if ((codeByte & 0x0C00) == 0) { result.add(OCD_Reserved); } else if ((codeByte & 0x0800) != 0 && (codeByte & 0x0400) != 0) { result.add(OCD_DataCarriedInOCD); } else if ((codeByte & 0x0800) != 0) { result.add(OCD_UnknownIfOCDCarriesData); } else if ((codeByte & 0x0400) != 0) { result.add(OCD_DataNotCarriedInOCD); } return result; } public static byte[] toBytes(EnumSet flags) { int result = 0; if (flags.contains(OC_DataCarriedInObjectContainer)) { result |= 0xC000; } else if (flags.contains(OC_UnknownContainerStructure)) { result |= 0x8000; } else if (flags.contains(OC_DataNotCarriedInObjectContainer)) { result |= 0x4000; } if (flags.contains(OEG_Included)) { result |= 0x3000; } else if (flags.contains(OEG_Unknown)) { result |= 0x2000; } else if (flags.contains(OEG_NotIncluded)) { result |= 0x1000; } if (flags.contains(OCD_DataCarriedInOCD)) { result |= 0x0C00; } else if (flags.contains(OCD_UnknownIfOCDCarriesData)) { result |= 0x0800; } else if (flags.contains(OCD_DataNotCarriedInOCD)) { result |= 0x0400; } return UtilBinaryDecoding.intToByteArray(result, 2); } /** * Sets the given flag and unsets mutual exclusive flags. */ public static void setFlag(EnumSet flags, StructureFlag flag) { handler.setFlag(flags, flag); } @Override public int getGroup() { return group; } } } /** * MO:DCA, page 372.

The MO:DCA Interchange Set triplet identifies the interchange set and * the data stream type. */ public static class MODCAInterchangeSet extends Triplet { MODCAInterchangeSet_Type type; MODCAInterchangeSet_Identifier identifier; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { StructuredField.checkDataLength(sfData, offset, length, 5); super.decodeAFP(sfData, offset, length, config); type = MODCAInterchangeSet_Type.valueOf(sfData[offset + 2]); identifier = MODCAInterchangeSet_Identifier.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + 3, 2)); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(tripletID.toByte()); baos.write(type.toByte()); baos.write(identifier.toBytes()); length = (short) (baos.size() + 1); os.write(UtilBinaryDecoding.shortToByteArray(length, 1)); os.write(baos.toByteArray()); } public enum MODCAInterchangeSet_Type { Presentation; public static MODCAInterchangeSet_Type valueOf(byte typeCode) throws AFPParserException { if (typeCode == 0x01) { return Presentation; } throw new AFPParserException(MODCAInterchangeSet_Type.class.getSimpleName() + ": type code 0x" + Integer.toHexString(typeCode) + " is unknown."); } public int toByte() { return 0x01; } } public enum MODCAInterchangeSet_Identifier { MODCA_IS1(0x0900), MODCA_IS2_Retired(0x0C00), MODCA_IS3(0x0D00); int code; MODCAInterchangeSet_Identifier(int code) { this.code = code; } public static MODCAInterchangeSet_Identifier valueOf(short code) throws AFPParserException { for (MODCAInterchangeSet_Identifier id : values()) { if (id.code == code) { return id; } } throw new AFPParserException(MODCAInterchangeSet_Identifier.class.getSimpleName() + ": type code 0x" + Integer.toHexString(code) + " is unknown."); } public byte[] toBytes() { return UtilBinaryDecoding.intToByteArray(code, 2); } } } /** * MO:DCA, page 374.

The Font Descriptor Specification triplet specifies the attributes of * the desired font in a coded font reference. */ public static class FontDescriptorSpecification extends Triplet { FDS_FontWeigthClass fontWeigthClass; FDS_FontWidthClass fontWidthClass; short fontHeight; short fontWidth; EnumSet fontDsFlags; byte[] reserved9_18; EnumSet fontUsFlags; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { StructuredField.checkDataLength(sfData, offset, length, 9); super.decodeAFP(sfData, offset, length, config); fontWeigthClass = FDS_FontWeigthClass.valueOf(sfData[offset + 2]); fontWidthClass = FDS_FontWidthClass.valueOf(sfData[offset + 3]); fontHeight = UtilBinaryDecoding.parseShort(sfData, offset + 4, 2); fontWidth = UtilBinaryDecoding.parseShort(sfData, offset + 6, 2); fontDsFlags = FDS_FontDsFlag.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + 8, 1)); int actualLength = StructuredField.getActualLength(sfData, offset, length); if (actualLength > 9) { reserved9_18 = new byte[10]; System.arraycopy(sfData, offset + 9, reserved9_18, 0, reserved9_18.length); } else { reserved9_18 = null; } if (actualLength > 19) { fontUsFlags = FDS_FontUsFlag.valueOf(sfData[offset + 19]); } else { fontUsFlags = null; } } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(tripletID.toByte()); baos.write(fontWeigthClass.toByte()); baos.write(fontWidthClass.toByte()); baos.write(UtilBinaryDecoding.shortToByteArray(fontHeight, 2)); baos.write(UtilBinaryDecoding.shortToByteArray(fontWidth, 2)); baos.write(FDS_FontDsFlag.toByte(fontDsFlags)); if (reserved9_18 != null) { baos.write(reserved9_18); if (fontUsFlags != null) { baos.write(FDS_FontUsFlag.toByte(fontUsFlags)); } } length = (short) (baos.size() + 1); os.write(UtilBinaryDecoding.shortToByteArray(length, 1)); os.write(baos.toByteArray()); } public enum FDS_FontWeigthClass { NotSpecified(0x00), UltraLight(0x01), ExtraLight(0x02), Light(0x03), SemiLight(0x04), Medium_Normal(0x05), SemiBold(0x06), Bold(0x07), ExtraBold(0x08), UltraBold(0x09); int code; FDS_FontWeigthClass(int code) { this.code = code; } public static FDS_FontWeigthClass valueOf(byte codeByte) throws AFPParserException { for (FDS_FontWeigthClass wc : values()) { if (wc.ordinal() == codeByte) { return wc; } } throw new AFPParserException(FDS_FontWeigthClass.class.getSimpleName() + ": class code 0x" + Integer.toHexString(codeByte) + " is unknown."); } public int toByte() { return ordinal(); } } public enum FDS_FontWidthClass { NotSpecified(0x00), UltraCondensed(0x01), ExtraCondensed(0x02), Condensed(0x03), SemiCondensed(0x04), Medium_Normal(0x05), Semi_Expanded(0x06), Expanded(0x07), Extra_Expanded(0x08), Ultra_Expanded(0x09); int code; FDS_FontWidthClass(int code) { this.code = code; } public static FDS_FontWidthClass valueOf(byte codeByte) throws AFPParserException { for (FDS_FontWidthClass wc : values()) { if (wc.ordinal() == codeByte) { return wc; } } throw new AFPParserException(FDS_FontWidthClass.class.getSimpleName() + ": class code 0x" + Integer.toHexString(codeByte) + " is unknown."); } public int toByte() { return ordinal(); } } public enum FDS_FontDsFlag implements IMutualExclusiveGroupedFlag { NoItalicCharacters(0), ItalicCharacters(0), NoUnderscoredCharacters(1), UnderscoredCharacters(1), // bit 2 is reserved. NoHollowCharacters(3), HollowCharacters(3), NoOverstruckCharacters(4), OverstruckCharacters(4), UniformlySpacedCharacters(5), ProportionallyCharacters(5), NoPairwiseKernedCharacters(6), PairwiseKernedCharacters(6), ParameterIsNotSpecified(7), ParameterIsSpecified(7); static MutualExclusiveGroupedFlagHandler handler = new MutualExclusiveGroupedFlagHandler(); int group; FDS_FontDsFlag(int group) { this.group = group; } public static EnumSet valueOf(short flagByte) { EnumSet result = EnumSet.noneOf(FDS_FontDsFlag.class); if ((flagByte & 0x80) == 0) { result.add(NoItalicCharacters); } else { result.add(ItalicCharacters); } if ((flagByte & 0x40) == 0) { result.add(NoUnderscoredCharacters); } else { result.add(UnderscoredCharacters); } if ((flagByte & 0x10) == 0) { result.add(NoHollowCharacters); } else { result.add(HollowCharacters); } if ((flagByte & 0x08) == 0) { result.add(NoOverstruckCharacters); } else { result.add(OverstruckCharacters); } if ((flagByte & 0x04) == 0) { result.add(UniformlySpacedCharacters); } else { result.add(ProportionallyCharacters); } if ((flagByte & 0x02) == 0) { result.add(NoPairwiseKernedCharacters); } else { result.add(PairwiseKernedCharacters); } if ((flagByte & 0x01) == 0) { result.add(ParameterIsNotSpecified); } else { result.add(ParameterIsSpecified); } return result; } public static int toByte(EnumSet flags) { int result = 0; if (flags.contains(ItalicCharacters)) { result |= 0x80; } if (flags.contains(UnderscoredCharacters)) { result |= 0x40; } if (flags.contains(HollowCharacters)) { result |= 0x10; } if (flags.contains(OverstruckCharacters)) { result |= 0x08; } if (flags.contains(ProportionallyCharacters)) { result |= 0x04; } if (flags.contains(PairwiseKernedCharacters)) { result |= 0x02; } if (flags.contains(ParameterIsSpecified)) { result |= 0x01; } return result; } public static void setFlag(EnumSet flags, FDS_FontDsFlag flag) { handler.setFlag(flags, flag); } @Override public int getGroup() { return group; } } public enum FDS_FontUsFlag implements IMutualExclusiveGroupedFlag { // bit 0 is reserved. FontType_BitmapFont(1), FontType_OutlineOrVector(1), TransformFont_WillNotBeTransformed(2), TransformFont_MayBeTransformed(2) // bits 3-7 are reserved. ; static MutualExclusiveGroupedFlagHandler handler = new MutualExclusiveGroupedFlagHandler(); int group; FDS_FontUsFlag(int group) { this.group = group; } public static EnumSet valueOf(byte flagByte) { EnumSet result = EnumSet.noneOf(FDS_FontUsFlag.class); if ((flagByte & 0x40) == 0) { result.add(FontType_BitmapFont); } else { result.add(FontType_OutlineOrVector); } if ((flagByte & 0x20) == 0) { result.add(TransformFont_WillNotBeTransformed); } else { result.add(TransformFont_MayBeTransformed); } return result; } public static int toByte(EnumSet flags) { int result = 0; if (flags.contains(FontType_OutlineOrVector)) { result |= 0x40; } if (flags.contains(TransformFont_MayBeTransformed)) { result |= 0x20; } return result; } public static void setFlag(EnumSet flags, FDS_FontUsFlag flag) { handler.setFlag(flags, flag); } @Override public int getGroup() { return group; } } } /** * MO:DCA, page 378.

The Font Coded Graphic Character Set Global Identifier triplet is * used to specify the code page and character set for a coded font. */ public static class FontCodedGraphicCharacterSetGlobalID extends Triplet { int codedGraphicCharacterSetGlobalID; int codePageGlobalID; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); codedGraphicCharacterSetGlobalID = UtilBinaryDecoding.parseInt(sfData, offset + 2, 2); codePageGlobalID = UtilBinaryDecoding.parseInt(sfData, offset + 4, 2); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(tripletID.toByte()); baos.write(UtilBinaryDecoding.intToByteArray(codedGraphicCharacterSetGlobalID, 2)); baos.write(UtilBinaryDecoding.intToByteArray(codePageGlobalID, 2)); length = (short) (baos.size() + 1); os.write(UtilBinaryDecoding.shortToByteArray(length, 1)); os.write(baos.toByteArray()); } } /** * MODCA page 379.

The Resource Object Type triplet identifies the type of object * enveloped by the Begin Resource (BRS) and End Resource (ERS) structured fields.

* UNFORTUNATLY:
A similar triplet, the Object Function Set Specification triplet, that * unfortunately also uses triplet ID X'21', is retired but is still used on the BDT * structured field; see “Object Function Set Specification Triplet X'21'” on page 570. */ public static class ResourceObjectType extends Triplet { ROT_ObjectType objectType; byte[] constantData; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); objectType = ROT_ObjectType.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + 2, 1)); int actualLength = StructuredField.getActualLength(sfData, offset, length); constantData = new byte[actualLength - 3]; System.arraycopy(sfData, offset + 3, constantData, 0, constantData.length); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(tripletID.toByte()); baos.write(objectType.toByte()); baos.write(constantData); length = (short) (baos.size() + 1); os.write(UtilBinaryDecoding.shortToByteArray(length, 1)); os.write(baos.toByteArray()); } /** * Unfortunately, this type is different to {@link AFPObjectType}. */ public enum ROT_ObjectType { PresentationText(0x02), GraphicsObject_GOCA(0x03), BarCodeObject_BCOCA(0x05), ImageObject_IOCA(0x06), FontCharacterSetObject(0x40), CodePageObject(0x41), CodedFontObject(0x42), ObjectContainer(0x92), DocumentObject(0xA8), PageSegmentObject(0xFB), OverlayObject(0xFC), Reserved(0xFD), FormMapObject(0xFE); int code; ROT_ObjectType(int code) { this.code = code; } public static ROT_ObjectType valueOf(short codeByte) throws AFPParserException { for (ROT_ObjectType t : values()) { if (t.code == codeByte) { return t; } } throw new AFPParserException(ROT_ObjectType.class.getSimpleName() + ": type 0x" + Integer.toHexString(codeByte) + " is unknown."); } public int toByte() { return code; } } } /** * RETIRED
The use of this triplet is restricted to the BDT structured field in the following * products: Pre-year 2012 AFP applications.

The Object Function Set Specification * triplet is used to specify the Object Content Architecture (OCA) level for objects in a MO:DCA * document. */ public static class ObjectFunctionSetSpecification_Retired extends Triplet { ROT_ObjectType objectType; byte ocaArchitectureLevel; int modcaFunctionSetIdentifier; OCAFunctionSet ocaFunctionSet; byte[] reserved; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); objectType = ROT_ObjectType.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + 2, 1)); ocaArchitectureLevel = sfData[offset + 3]; modcaFunctionSetIdentifier = UtilBinaryDecoding.parseInt(sfData, offset + 4, 2); ocaFunctionSet = OCAFunctionSet.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + 6, 2)); if (length > 8) { reserved = new byte[length - 8]; System.arraycopy(sfData, offset + 8, reserved, 0, reserved.length); } else { reserved = null; } } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(tripletID.toByte()); baos.write(objectType.toByte()); baos.write(ocaArchitectureLevel); baos.write(UtilBinaryDecoding.intToByteArray(modcaFunctionSetIdentifier, 2)); baos.write(ocaFunctionSet.toByte()); if (reserved != null) { baos.write(reserved); } length = (short) (baos.size() + 1); os.write(UtilBinaryDecoding.shortToByteArray(length, 1)); os.write(baos.toByteArray()); } public enum OCAFunctionSet { PTOCA_PT1_or_BCOCA_BCD1(0x0000), PTOCA_PT2_or_GOCA_DR2V0(0x4000), IOCA_FS10(0x8000); int code; OCAFunctionSet(int code) { this.code = code; } public static OCAFunctionSet valueOf(int code) throws AFPParserException { for (OCAFunctionSet fs : values()) { if (fs.code == code) { return fs; } } throw new AFPParserException(OCAFunctionSet.class.getSimpleName() + ": code 0x" + Integer.toHexString(code) + " is unknown."); } public int toByte() { return code; } } } /** * MODCA page 381.

The Extended Resource Local Identifier triplet specifies a resource * type and a four-byte local identifier or LID. The LID usually is associated with a specific * resource name by a map structured field, such as a Map Media Type structured field. */ public static class ExtendedResourceLocalIdentifier extends Triplet { ERLI_ResourceType resourceType; long extendedResourceLocalID; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { StructuredField.checkDataLength(sfData, offset, length, 7); super.decodeAFP(sfData, offset, length, config); resourceType = ERLI_ResourceType.valueOf(sfData[offset + 2]); extendedResourceLocalID = UtilBinaryDecoding.parseLong(sfData, offset + 3, 4); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(tripletID.toByte()); baos.write(resourceType.toByte()); baos.write(UtilBinaryDecoding.longToByteArray(extendedResourceLocalID, 4)); length = (short) (baos.size() + 1); os.write(UtilBinaryDecoding.shortToByteArray(length, 1)); os.write(baos.toByteArray()); } /** * Specifies the resource type associated with the extended local ID. */ public enum ERLI_ResourceType { IOBReference_Reserved, MediaTypeResource, MediaDestinationResource; public static ERLI_ResourceType valueOf(byte codeByte) throws AFPParserException { if (codeByte == 0x30) { return IOBReference_Reserved; } else if (codeByte == 0x40) { return MediaTypeResource; } else if (codeByte == 0x42) { return MediaDestinationResource; } else { throw new AFPParserException(ERLI_ResourceType.class.getSimpleName() + ": resource type code 0x" + Integer.toHexString(codeByte) + " is unknown."); } } public int toByte() { if (this == IOBReference_Reserved) { return 0x30; } else if (this == MediaTypeResource) { return 0x40; } else if (this == MediaDestinationResource) { return 0x42; } else { return 0; } } } } /** * MODCA page 383.

The Resource Local Identifier triplet may be used to specify a resource * type and a one-byte local identifier or LID. The LID usually is associated with a specific * resource name by a map structured field, such as a Map Coded Font structured field. */ public static class ResourceLocalIdentifier extends Triplet { RLI_ResourceType resourceType; short resourceLocalID; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { StructuredField.checkDataLength(sfData, offset, length, 4); super.decodeAFP(sfData, offset, length, config); resourceType = RLI_ResourceType.valueOf(sfData[offset + 2]); resourceLocalID = UtilBinaryDecoding.parseShort(sfData, offset + 3, 1); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 4; os.write(UtilBinaryDecoding.shortToByteArray(length, 1)); os.write(tripletID.toByte()); os.write(resourceType.toByte()); os.write(UtilBinaryDecoding.shortToByteArray(resourceLocalID, 1)); } public enum RLI_ResourceType { UsageDependent, PageOverlay, CodedFont, ColorAttributeTable; public static RLI_ResourceType valueOf(byte codeByte) throws AFPParserException { if (codeByte == 0x00) { return UsageDependent; } else if (codeByte == 0x02) { return PageOverlay; } else if (codeByte == 0x05) { return CodedFont; } else if (codeByte == 0x07) { return ColorAttributeTable; } else { throw new AFPParserException(RLI_ResourceType.class.getSimpleName() + ": resource type code 0x" + Integer.toHexString(codeByte) + " is unknown."); } } public int toByte() { if (this == UsageDependent) { return 0x00; } else if (this == PageOverlay) { return 0x02; } else if (this == CodedFont) { return 0x05; } else if (this == ColorAttributeTable) { return 0x07; } else { return 0x00; } } } } /** * MODCA page 385.

The Resource Section Number triplet specifies a coded font section * number. It may be used to select a single section of a double-byte coded font if less than the * entire double-byte coded font is required for processing. For a description of coded fonts see * the Font Object Content Architecture Reference. */ public static class ResourceSectionNumber extends Triplet { short resourceSectionNumber; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { StructuredField.checkDataLength(sfData, offset, length, 3); super.decodeAFP(sfData, offset, length, config); resourceSectionNumber = UtilBinaryDecoding.parseShort(sfData, offset + 2, 1); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 3; os.write(UtilBinaryDecoding.shortToByteArray(length, 1)); os.write(tripletID.toByte()); os.write(UtilBinaryDecoding.shortToByteArray(resourceSectionNumber, 1)); } } /** * MODCA page 386.

The Character Rotation triplet is used to specify character rotation * relative to the Character coordinate system. See the Font Object Content Architecture * Reference for further information. */ public static class CharacterRotation extends Triplet { AFPOrientation characterRotation; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { StructuredField.checkDataLength(sfData, offset, length, 4); super.decodeAFP(sfData, offset, length, config); characterRotation = AFPOrientation.valueOf(UtilBinaryDecoding.parseInt(sfData, offset + 2, 2)); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 4; os.write(UtilBinaryDecoding.shortToByteArray(length, 1)); os.write(tripletID.toByte()); os.write(characterRotation.toBytes()); } } /** * MODCA page 387.

The Object Byte Offset triplet is used to specify the byte offset of an * indexed object within a document. */ public static class ObjectByteOffset extends Triplet { long byteOffset; Long byteOffsetHighOrder; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); byteOffset = UtilBinaryDecoding.parseLong(sfData, offset + 2, 4); if (length > 6) { byteOffsetHighOrder = UtilBinaryDecoding.parseLong(sfData, offset + 6, 4); } else { byteOffsetHighOrder = null; } } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = (short) (byteOffsetHighOrder == null ? 6 : 10); os.write(UtilBinaryDecoding.shortToByteArray(length, 1)); os.write(tripletID.toByte()); os.write(UtilBinaryDecoding.longToByteArray(byteOffset, 4)); if (byteOffsetHighOrder != null) { os.write(UtilBinaryDecoding.longToByteArray(byteOffsetHighOrder, 4)); } } } /** * MODCA page 388.

The Attribute Value triplet is used to specify a value for a document * attribute. */ public static class AttributeValue extends Triplet { byte[] reserved2_3 = new byte[2]; String attributeValue; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); reserved2_3 = new byte[2]; System.arraycopy(sfData, offset + 2, reserved2_3, 0, 2); if (length > 4) { attributeValue = new String(sfData, offset + 4, length - 4, config.getAfpCharSet()); } else { attributeValue = null; } } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(tripletID.toByte()); baos.write(reserved2_3); if (attributeValue != null) { baos.write(attributeValue.getBytes(config.getAfpCharSet())); } length = (short) (baos.size() + 1); os.write(UtilBinaryDecoding.shortToByteArray(length, 1)); os.write(baos.toByteArray()); } public byte[] getReserved2_3() { return reserved2_3; } public void setReserved2_3(byte[] reserved2_3) { this.reserved2_3 = reserved2_3; } public String getAttributeValue() { return attributeValue; } public void setAttributeValue(String attributeValue) { this.attributeValue = attributeValue; } } /** * MODCA page 389.

The Descriptor Position triplet is used to associate an Object Area * Position structured field with an Object Area Descriptor structured field. */ public static class DescriptorPosition extends Triplet { short objectAreaDescriptorID; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { StructuredField.checkDataLength(sfData, offset, length, 3); super.decodeAFP(sfData, offset, length, config); objectAreaDescriptorID = UtilBinaryDecoding.parseShort(sfData, offset + 2, 1); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 3; os.write(length); os.write(tripletID.toByte()); os.write(objectAreaDescriptorID); } } /** * MODCA page 390.

The Media Eject Control triplet is used to specify the type of media * eject that is performed and the type of controls that are activated when a new medium map is * invoked and N-up partitioning is specified. */ public static class MediaEjectControl extends Triplet { byte reserved2 = 0x00; MediaEjectControlType mediaEjectControl; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); reserved2 = sfData[offset + 2]; mediaEjectControl = MediaEjectControlType.valueOf(sfData[offset + 3]); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 4; os.write(length); os.write(tripletID.toByte()); os.write(reserved2); os.write(mediaEjectControl.toByte()); } public enum MediaEjectControlType { EjectToNewSheet, ConditionalEjectToNextPartition, ConditionalEjectToNextFrontsidePartition, ConditionalEjectToNextBacksidePartition; public static MediaEjectControlType valueOf(byte codeByte) throws AFPParserException { if (codeByte == 0x01) { return EjectToNewSheet; } else if (codeByte == 0x02) { return ConditionalEjectToNextPartition; } else if (codeByte == 0x03) { return ConditionalEjectToNextFrontsidePartition; } else if (codeByte == 0x04) { return ConditionalEjectToNextBacksidePartition; } else { throw new AFPParserException(MediaEjectControlType.class.getSimpleName() + ": code byte 0x" + Integer.toHexString(codeByte) + " is undefined."); } } public int toByte() { if (this == EjectToNewSheet) { return 0x01; } else if (this == ConditionalEjectToNextPartition) { return 0x02; } else if (this == ConditionalEjectToNextFrontsidePartition) { return 0x03; } else if (this == ConditionalEjectToNextBacksidePartition) { return 0x04; } else { return 0; } } } } /** * MODCA page 576.

The use of this triplet is restricted to products that generate or * process the retired MO:DCA interchange set MO:DCA IS/2. The Page Overlay Conditional Processing * triplet is used to identify the intended utilization of a page overlay as produced by a * generator. This triplet can also be used to define an overlay level that determines whether the * overlay is to be processed. */ public static class PageOverlayConditionalProcessing extends Triplet { PageOverlayType pageOverlayType; Short levelOfOverlay; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); pageOverlayType = PageOverlayType.valueOf(sfData[offset + 2]); if (length > 3) { levelOfOverlay = UtilBinaryDecoding.parseShort(sfData, offset + 3, 1); } else { levelOfOverlay = null; } } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = (short) (levelOfOverlay == null ? 3 : 4); os.write(length); os.write(tripletID.toByte()); os.write(pageOverlayType.toByte()); if (levelOfOverlay != null) { os.write(levelOfOverlay); } } public enum PageOverlayType { Normal, Annotation, Redaction, Highlight; public static PageOverlayType valueOf(byte codeByte) throws AFPParserException { for (PageOverlayType loo : values()) { if (loo.ordinal() == codeByte) { return loo; } } throw new AFPParserException(PageOverlayType.class.getSimpleName() + ": page overlay type 0x" + Integer.toHexString(codeByte) + " is unknown."); } public int toByte() { return ordinal(); } } } /** * MODCA page 578.

The use of this triplet is restricted to products that generate or * process the retired MO:DCA interchange set MO:DCA IS/2. The Resource Usage Attribute triplet * can be used for resource management. It is used with the Include Page Overlay and Map Page * Overlay structured fields to identify the approximate frequency with which an associated page * overlay is processed. This is indicated by assigning either a low or high value to this * triplet. The Resource Usage Attribute triplet has no processing semantics associated with it. */ public static class ResourceUsageAttribute extends Triplet { FrequencyOfUse frequencyOfUse; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); frequencyOfUse = FrequencyOfUse.valueOf(sfData[offset + 2]); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 3; os.write(length); os.write(tripletID.toByte()); os.write(frequencyOfUse.toByte()); } /** * Specifies the processing frequency of the associated page overlay. */ public enum FrequencyOfUse { Low, High; public static FrequencyOfUse valueOf(byte codeByte) { if (codeByte == 0x00) { return Low; } else { return High; } } public int toByte() { if (this == Low) { return 0x00; } else { return 0xFF; } } } } /** * MODCA page 579

*

* The use of this triplet is restricted to the BMO and BPS structured fields in external (print * file level) AFP resource groups for the following products: v PSF/MVS v PSF/VSE v RPM 2.0 v RPM * 3.0 v PSF/2 (DPF) v RMARK The Object Checksum object specifies a qualifier that can be used to * identify or fingerprint an object. */ public static class ObjectChecksum extends Triplet { CheckSumFormat checksumFormat; int crcCheckSum; EnumSet objectCheckSumFlags; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { StructuredField.checkDataLength(sfData, offset, length, 6); super.decodeAFP(sfData, offset, length, config); checksumFormat = CheckSumFormat.valueOf(sfData[offset + 2]); crcCheckSum = UtilBinaryDecoding.parseInt(sfData, offset + 3, 2); objectCheckSumFlags = ChecksumFlag.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + 5, 1)); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { this.length = 6; os.write(length); os.write(tripletID.toByte()); os.write(checksumFormat.toByte()); os.write(UtilBinaryDecoding.intToByteArray(crcCheckSum, 2)); os.write(ChecksumFlag.toByte(objectCheckSumFlags)); } public CheckSumFormat getChecksumFormat() { return checksumFormat; } public void setChecksumFormat(CheckSumFormat checksumFormat) { this.checksumFormat = checksumFormat; } public int getCrcCheckSum() { return crcCheckSum; } public void setCrcCheckSum(int crcCheckSum) { this.crcCheckSum = crcCheckSum; } public EnumSet getObjectCheckSumFlags() { return objectCheckSumFlags; } public void setObjectCheckSumFlags(EnumSet objectCheckSumFlags) { this.objectCheckSumFlags = objectCheckSumFlags; } /** * Sets the given flag and un-sets all mutually exclusive flags. * * @param objectCheckSumFlags set of flags to change. * @param flag to set */ public void setObjectCheckSumFlag(EnumSet objectCheckSumFlags, ChecksumFlag flag) { ChecksumFlag.handler.setFlag(objectCheckSumFlags, flag); } public enum CheckSumFormat { ObjectCycleRedundancyCheck, Retired_PrivateUse; public static CheckSumFormat valueOf(byte codeByte) throws AFPParserException { if (codeByte == 0x01) { return ObjectCycleRedundancyCheck; } if (codeByte == 0x02) { return Retired_PrivateUse; } else { throw new AFPParserException(CheckSumFormat.class.getSimpleName() + ": checksum format code 0x" + Integer.toHexString(codeByte) + " is undefined."); } } public int toByte() { if (this == ObjectCycleRedundancyCheck) { return 0x01; } else { return 0x02; } } } public enum ChecksumFlag implements IMutualExclusiveGroupedFlag { UsageScope_PublicUnlimited(0), UsageScope_PrivateLimited(0), ResourceRetention_SaveResource(1), ResourceRetention_DoNotSaveResource(1); public static final MutualExclusiveGroupedFlagHandler handler = new MutualExclusiveGroupedFlagHandler(); int group; ChecksumFlag(int group) { this.group = group; } public static EnumSet valueOf(short codByte) { EnumSet result = EnumSet.noneOf(ChecksumFlag.class); if ((codByte & 0x80) == 0) { result.add(UsageScope_PublicUnlimited); } else { result.add(UsageScope_PrivateLimited); } if ((codByte & 0x40) == 0) { result.add(ResourceRetention_SaveResource); } else { result.add(ResourceRetention_DoNotSaveResource); } return result; } public static int toByte(EnumSet set) { int result = 0x00; if (set.contains(UsageScope_PrivateLimited)) { result |= 0x80; } if (set.contains(ResourceRetention_DoNotSaveResource)) { result |= 0x40; } return result; } @Override public int getGroup() { return group; } } } /** * MODCA page 581. Retired function

The use of this triplet is restricted to the BMO and * BPS structured fields in external (print file level) AFP resource groups for the following * products: v PSF/MVS v PSF/VSE v RPM 2.0 v PSF/2 v RMARK The Object Origin Identifier triplet is * used to identify the system on which an object originated. */ public static class ObjectOriginIdentifier extends Triplet { AFPSystem originationSystem; String systemIDSerialNumber; String storageMediaID; String dataSetID; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { StructuredField.checkDataLength(sfData, offset, length, 61); super.decodeAFP(sfData, offset, length, config); originationSystem = AFPSystem.valueOf(sfData[offset + 2]); systemIDSerialNumber = new String(sfData, offset + 3, 8, config.getAfpCharSet()); storageMediaID = new String(sfData, offset + 11, 6, config.getAfpCharSet()); dataSetID = new String(sfData, offset + 17, 44, config.getAfpCharSet()); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 61; os.write(length); os.write(tripletID.toByte()); os.write(originationSystem.toByte()); os.write(UtilCharacterEncoding.stringToByteArray(systemIDSerialNumber, config.getAfpCharSet(), 8, Constants.EBCDIC_BLANK)); os.write(UtilCharacterEncoding.stringToByteArray(storageMediaID, config.getAfpCharSet(), 6, Constants.EBCDIC_BLANK)); os.write(UtilCharacterEncoding.stringToByteArray(dataSetID, config.getAfpCharSet(), 44, Constants.EBCDIC_BLANK)); } public enum AFPSystem { MVS, VM, PC_DOS, VSE; public static AFPSystem valueOf(byte codeByte) throws AFPParserException { if (codeByte == 0x01) { return MVS; } else if (codeByte == 0x02) { return VM; } else if (codeByte == 0x03) { return PC_DOS; } else if (codeByte == 0x04) { return VSE; } throw new AFPParserException(AFPSystem.class.getSimpleName() + ": system code 0x" + Integer.toHexString(codeByte) + " is undefined."); } public int toByte() { return ordinal() + 1; } } } /** * MODCA page 582, retired function.

*

* The use of this triplet is restricted to the IMM structured field for the following products: v * AFP OnDemand v AFP Workbench The IMM Insertion triplet is used to indicate that the Invoke * Medium Map (IMM) structured field on which it is specified was inserted at the beginning of a * page group by a filtering application. The IMM was inserted between the BNG and the first BPG * in the group, but only if an IMM was not already specified there. The purpose of the inserted * IMM is to allow the page group to be processed in standalone fashion. This triplet is ignored * by presentation servers, and the IMM on which it is specified is processed as if the triplet * were absent. The presence of this triplet on an IMM may be used by an inverse filtering * application to remove the IMM when it is desired to present the complete document as it * appeared before the IMM was inserted. */ public static class IMMInsertionTriplet extends Triplet { byte[] reserved2_3 = new byte[2]; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { StructuredField.checkDataLength(sfData, offset, length, 4); super.decodeAFP(sfData, offset, length, config); reserved2_3 = new byte[2]; System.arraycopy(sfData, offset + 2, reserved2_3, 0, reserved2_3.length); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 4; os.write(length); os.write(tripletID.toByte()); os.write(reserved2_3); } } /** * MODCA page 569, retired function.

*

* Text Orientation Triplet X'1D' The use of this triplet is restricted to the MCF-2 structured * field for 3800 compatibility for the following products: v PSF/MVS v PSF/VM v PSF/VSE v PSF/400 * v PSF/2 v Infoprint Manager (IPM) v 3800 printer v Applications that generate MCF-2s in * documents to be printed on the 3800 printer The Text Orientation triplet is used to specify the * text orientation for a coded font. When the MCF-2 structured field is used to reference * different sections of the same double-byte font, a Text Orientation (X'1D') triplet may be * specified in any of the repeating groups associated with the font and need only be specified in * one of the repeating groups. However, if specified in more than one of the associated repeating * groups, the value of all Text Orientation (X'1D') triplets must be identical. */ public static class TextOrientation extends Triplet { AFPOrientation xOrientation; AFPOrientation yOrientation; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { StructuredField.checkDataLength(sfData, offset, length, 6); super.decodeAFP(sfData, offset, length, config); xOrientation = AFPOrientation.valueOf(UtilBinaryDecoding.parseInt(sfData, offset + 2, 2)); yOrientation = AFPOrientation.valueOf(UtilBinaryDecoding.parseInt(sfData, offset + 4, 2)); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 6; os.write(length); os.write(tripletID.toByte()); os.write(xOrientation.toBytes()); os.write(yOrientation.toBytes()); } } /** * MODCA page 572, retired function.

The use of this triplet is restricted to the BBC, * BGR, BII, BIM, IPS structured fields for the migration of line-data containing bar code * objects, graphic objects, image objects, and page segments to MO:DCA document format. This * triplet may be specified on these structured fields only for objects that occur directly in a * page. The triplet may not be specified on objects in a resource group or in a resource library; * if it is specified, it is ignored. Triplet X'27' Syntax: Use of this triplet is restricted to * the following products: v ACIF v PSF/MVS v PSF/VM v PSF/VSE v PSF/2 v Infoprint Manager (IPM) v * PSF/400 v AFP Workbench */ public static class LineDataObjectPositionMigration extends Triplet { LocationAndOrientation locationAndOrientation; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { StructuredField.checkDataLength(sfData, offset, length, 3); super.decodeAFP(sfData, offset, length, config); locationAndOrientation = LocationAndOrientation.valueOf(sfData[offset + 2]); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 3; os.write(length); os.write(tripletID.toByte()); os.write(locationAndOrientation.toByte()); } public enum LocationAndOrientation { Standard_0, LowerLeft_270, LowerRight_180, UperRight_90; public static LocationAndOrientation valueOf(byte codeByte) throws AFPParserException { for (LocationAndOrientation lao : values()) { if (lao.ordinal() == codeByte) { return lao; } } throw new AFPParserException(LocationAndOrientation.class.getSimpleName() + ": location/orientation code 0x" + Integer.toHexString(codeByte) + " is undefined."); } public int toByte() { return this.ordinal(); } } } /** * MODCA, page 395.

The Measurement Units triplet is used to specify the units of measure * for a presentation space. */ public static class MeasurementUnits extends Triplet { AFPUnitBase xUnitBase; AFPUnitBase yUnitBase; short xUnitsPerUnitbase; short yUnitsPerUnitbase; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { StructuredField.checkDataLength(sfData, offset, length, 8); super.decodeAFP(sfData, offset, length, config); xUnitBase = AFPUnitBase.valueOf(sfData[offset + 2]); yUnitBase = AFPUnitBase.valueOf(sfData[offset + 3]); xUnitsPerUnitbase = UtilBinaryDecoding.parseShort(sfData, offset + 4, 2); yUnitsPerUnitbase = UtilBinaryDecoding.parseShort(sfData, offset + 6, 2); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 8; os.write(length); os.write(tripletID.toByte()); os.write(xUnitBase.toByte()); os.write(yUnitBase.toByte()); os.write(UtilBinaryDecoding.shortToByteArray(xUnitsPerUnitbase, 2)); os.write(UtilBinaryDecoding.shortToByteArray(yUnitsPerUnitbase, 2)); } } /** * MODCA, page 396.

The Object Area Sizeand Y directions. triplet is used to specify * theextent of an object area in the X */ public static class ObjectAreaSize extends Triplet { byte sizeType_0x02; int xSize; int ySize; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { StructuredField.checkDataLength(sfData, offset, length, 9); super.decodeAFP(sfData, offset, length, config); sizeType_0x02 = sfData[offset + 2]; xSize = UtilBinaryDecoding.parseInt(sfData, offset + 3, 3); ySize = UtilBinaryDecoding.parseInt(sfData, offset + 6, 3); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 9; os.write(length); os.write(tripletID.toByte()); os.write(sizeType_0x02); os.write(UtilBinaryDecoding.intToByteArray(xSize, 3)); os.write(UtilBinaryDecoding.intToByteArray(ySize, 3)); } } /** * MOIDCA, page 397

The Area Definition triplet is used to define the position and size of * a rectangular area on a document component presentation space. The document component may be a * page or overlay, in which case the area is defined on the page or overlay presentation space, * or it may be a data object, in which case the area is defined on the object area presentation * space. */ public static class AreaDefinition extends Triplet { byte reserved2 = 0x00; int xOrigin; int yOrigin; int xSize; int ySize; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { StructuredField.checkDataLength(sfData, offset, length, 15); super.decodeAFP(sfData, offset, length, config); reserved2 = sfData[offset + 2]; xOrigin = UtilBinaryDecoding.parseInt(sfData, offset + 3, 3); yOrigin = UtilBinaryDecoding.parseInt(sfData, offset + 6, 3); xSize = UtilBinaryDecoding.parseInt(sfData, offset + 9, 3); ySize = UtilBinaryDecoding.parseInt(sfData, offset + 12, 3); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 15; os.write(length); os.write(tripletID.toByte()); os.write(reserved2); os.write(UtilBinaryDecoding.intToByteArray(xOrigin, 3)); os.write(UtilBinaryDecoding.intToByteArray(yOrigin, 3)); os.write(UtilBinaryDecoding.intToByteArray(xSize, 3)); os.write(UtilBinaryDecoding.intToByteArray(ySize, 3)); } } /** * MODCA, page 398.

The Color Specification triplet is used to specify a color value and * defines the color space and encoding for that value. */ public static class ColorSpecification extends Triplet { byte reserved2 = 0x00; AFPColorSpace colorSpace; byte[] reserved4_7 = new byte[4]; byte nrOfBitsComponent1; byte nrOfBitsComponent2; byte nrOfBitsComponent3; byte nrOfBitsComponent4; byte[] colorValue; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); reserved2 = sfData[offset + 2]; colorSpace = AFPColorSpace.valueOf(sfData[offset + 3]); reserved4_7 = new byte[4]; System.arraycopy(sfData, offset + 4, reserved4_7, 0, reserved4_7.length); nrOfBitsComponent1 = sfData[offset + 8]; nrOfBitsComponent2 = sfData[offset + 9]; nrOfBitsComponent3 = sfData[offset + 10]; nrOfBitsComponent4 = sfData[offset + 11]; colorValue = new byte[length - 12]; System.arraycopy(sfData, offset + 12, colorValue, 0, colorValue.length); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = (short) (12 + colorValue.length); os.write(length); os.write(tripletID.toByte()); os.write(reserved2); os.write(colorSpace.toByte()); os.write(reserved4_7); os.write(nrOfBitsComponent1); os.write(nrOfBitsComponent2); os.write(nrOfBitsComponent3); os.write(nrOfBitsComponent4); os.write(colorValue); } } /** * MODCA, page 403.

The Encoding Scheme ID triplet is used to specify the encoding scheme * associated with a code page. It may optionally also specify the encoding scheme for the user * data. */ public static class EncodingSchemeID extends Triplet { EnumSet encodingSchemeForCodePage; EnumSet encodingSchemeForUserData; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); encodingSchemeForCodePage = EncodingScheme.valueOf(UtilBinaryDecoding.parseInt(sfData, offset + 2, 2)); if (length > 4) { encodingSchemeForUserData = EncodingScheme.valueOf(UtilBinaryDecoding.parseInt(sfData, offset + 4, 2)); } else { encodingSchemeForUserData = null; } } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = (short) (encodingSchemeForUserData == null ? 4 : 6); os.write(length); os.write(tripletID.toByte()); os.write(EncodingScheme.toBytes(encodingSchemeForCodePage)); if (encodingSchemeForUserData != null) { os.write(EncodingScheme.toBytes(encodingSchemeForUserData)); } } public EnumSet getEncodingSchemeForCodePage() { return encodingSchemeForCodePage; } /** * Sets the given {@link EncodingScheme} and un-sets the mutual exclusive {@link EncodingScheme} * values. */ public void setEncodingSchemeForCodePage(EncodingScheme encodingScheme) { if (encodingSchemeForCodePage == null) { encodingSchemeForCodePage = EnumSet.noneOf(EncodingScheme.class); } EncodingScheme.handler.setFlag(encodingSchemeForCodePage, encodingScheme); } public void setEncodingSchemeForCodePage( EnumSet encodingSchemeForCodePage) { this.encodingSchemeForCodePage = encodingSchemeForCodePage; } public EnumSet getEncodingSchemeForUserData() { return encodingSchemeForUserData; } public void setEncodingSchemeForUserData( EnumSet encodingSchemeForUserData) { this.encodingSchemeForUserData = encodingSchemeForUserData; } public enum EncodingScheme implements IMutualExclusiveGroupedFlag { BasicEncoding_NotSpecified(0), BasicEncoding_IBMPC_Data(0), BasicEncoding_IBMPC_Display(0), BasicEncoding_EBCDIC_Presentation(0), BasicEncoding_UTF16(0), BasicEncoding_UnicodePresentation(0), NumberOfBytes_NotSpecified(1), NumberOfBytes_Fixed_SingleByte(1), NumberOfBytes_Fixed_DoubleByte(1), NumberOfBytes_UTFnVariable(1), CodeExtension_NotSpecified(2), CodeExtension_UTF8(2); public static final MutualExclusiveGroupedFlagHandler handler = new MutualExclusiveGroupedFlagHandler(); int group; EncodingScheme(int code) { this.group = code; } public static EnumSet valueOf(int code) throws AFPParserException { int basicEncoding = code >>> 12; int numberOfBytes = ((code >> 8) & 0x0F); int extension = code & 0xFF; EnumSet result = EnumSet.noneOf(EncodingScheme.class); if (basicEncoding == 0x00) { result.add(BasicEncoding_NotSpecified); } else if (basicEncoding == 0x02) { result.add(BasicEncoding_IBMPC_Data); } else if (basicEncoding == 0x03) { result.add(BasicEncoding_IBMPC_Display); } else if (basicEncoding == 0x06) { result.add(BasicEncoding_EBCDIC_Presentation); } else if (basicEncoding == 0x07) { result.add(BasicEncoding_UTF16); } else if (basicEncoding == 0x08) { result.add(BasicEncoding_UnicodePresentation); } else { throw new AFPParserException(EncodingScheme.class.getSimpleName() + ": basic encoding value 0x" + Integer.toHexString(basicEncoding) + " is undefined."); } if (numberOfBytes == 0x00) { result.add(NumberOfBytes_NotSpecified); } else if (numberOfBytes == 0x01) { result.add(NumberOfBytes_Fixed_SingleByte); } else if (numberOfBytes == 0x02) { result.add(NumberOfBytes_Fixed_SingleByte); } else if (numberOfBytes == 0x08) { result.add(NumberOfBytes_UTFnVariable); } else { throw new AFPParserException(EncodingScheme.class.getSimpleName() + ": number of bytes value 0x" + Integer.toHexString(numberOfBytes) + " is undefined."); } if (extension == 0x00) { result.add(CodeExtension_NotSpecified); } else if (extension == 0x07) { result.add(CodeExtension_UTF8); } return result; } public static byte[] toBytes(EnumSet flags) { int result = 0; if (flags.contains(BasicEncoding_IBMPC_Data)) { result |= 0x02; } else if (flags.contains(BasicEncoding_IBMPC_Display)) { result |= 0x03; } else if (flags.contains(BasicEncoding_EBCDIC_Presentation)) { result |= 0x06; } else if (flags.contains(BasicEncoding_UTF16)) { result |= 0x07; } else if (flags.contains(BasicEncoding_UnicodePresentation)) { result |= 0x08; } result <<= 4; if (flags.contains(NumberOfBytes_Fixed_SingleByte)) { result |= 0x01; } else if (flags.contains(NumberOfBytes_Fixed_DoubleByte)) { result |= 0x02; } else if (flags.contains(NumberOfBytes_UTFnVariable)) { result |= 0x08; } result <<= 8; if (flags.contains(CodeExtension_UTF8)) { result |= 0x7; } return UtilBinaryDecoding.intToByteArray(result, 2); } @Override public int getGroup() { return group; } } } /** * MODCA, page 406.

The Medium Map Page Number triplet is used to specify the sequence * number of the page in the set of sequential pages whose presentation is controlled by the most * recently activated medium map. */ public static class MediumMapPageNumber extends Triplet { int pageNumber; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); pageNumber = UtilBinaryDecoding.parseInt(sfData, offset + 2, 4); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 4; os.write(length); os.write(tripletID.toByte()); os.write(UtilBinaryDecoding.intToByteArray(pageNumber, 4)); } } /** * MODCA, page 407.

The Object Byte Extent triplet is used to specify the number of bytes * contained in an object. */ public static class ObjectByteExtent extends Triplet { long byteExtentLow; long byteExtentHigh; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); byteExtentLow = UtilBinaryDecoding.parseLong(sfData, offset + 2, 4); byteExtentHigh = UtilBinaryDecoding.parseLong(sfData, offset + 6, 4); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 10; os.write(length); os.write(tripletID.toByte()); os.write(UtilBinaryDecoding.longToByteArray(byteExtentLow, 4)); os.write(UtilBinaryDecoding.longToByteArray(byteExtentHigh, 4)); } } /** * MODCA, page 408.

The Object Structured Field Offset triplet is used to specify the * structuredoffset of an indexed object from the beginning of the document. field */ public static class ObjectStructuredFieldOffset extends Triplet { long offsetLow; Long offsetHigh; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); offsetLow = UtilBinaryDecoding.parseLong(sfData, offset + 2, 4); if (this.length > 6) { offsetHigh = UtilBinaryDecoding.parseLong(sfData, offset + 6, 4); } else { offsetHigh = null; } } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = (short) (offsetHigh != null ? 10 : 6); os.write(length); os.write(tripletID.toByte()); os.write(UtilBinaryDecoding.longToByteArray(offsetLow, 4)); if (offsetHigh != null) { os.write(UtilBinaryDecoding.longToByteArray(offsetHigh, 4)); } } } /** * MODCA, page 409.

*

* The Object Structured Field Extent triplet is used to specify the number of structured fields * contained in an object, starting with the Begin Object structured field and ending with the End * Object structured field. */ public static class ObjectStructuredFieldExtent extends Triplet { long numberOfSFLow; Long numberOfSFHigh; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); numberOfSFLow = UtilBinaryDecoding.parseLong(sfData, offset + 2, 4); if (this.length > 6) { numberOfSFHigh = UtilBinaryDecoding.parseLong(sfData, offset + 6, 4); } else { numberOfSFHigh = null; } } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = (short) (numberOfSFHigh != null ? 10 : 6); os.write(length); os.write(tripletID.toByte()); os.write(UtilBinaryDecoding.longToByteArray(numberOfSFLow, 4)); if (numberOfSFHigh != null) { os.write(UtilBinaryDecoding.longToByteArray(numberOfSFHigh, 4)); } } } /** * MODCA, page 410.

*

* The Object Offset triplet specifies the number of objects of a particular type that precede a * selected object in the document. If the object being counted is a document, this triplet * specifies the number of documents that precede the selected object in the print file. */ public static class ObjectOffset extends Triplet { ObjectType objectType; byte reserved3 = 0x00; long nrOfPrecedingObjectsLow; Long nrOfPrecedingObjectsHigh; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); objectType = ObjectType.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + 2, 1)); reserved3 = sfData[offset + 3]; nrOfPrecedingObjectsLow = UtilBinaryDecoding.parseLong(sfData, offset + 4, 4); if (this.length > 7) { nrOfPrecedingObjectsHigh = UtilBinaryDecoding.parseLong(sfData, offset + 8, 4); } else { nrOfPrecedingObjectsHigh = null; } } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = (short) (nrOfPrecedingObjectsHigh != null ? 12 : 8); os.write(length); os.write(tripletID.toByte()); os.write(objectType.toByte()); os.write(reserved3); os.write(UtilBinaryDecoding.longToByteArray(nrOfPrecedingObjectsLow, 4)); if (nrOfPrecedingObjectsHigh != null) { os.write(UtilBinaryDecoding.longToByteArray(nrOfPrecedingObjectsHigh, 4)); } } public enum ObjectType { Document, Page_PaginatedObject; public static ObjectType valueOf(short codeByte) throws AFPParserException { if (codeByte == 0xA8) { return Document; } else if (codeByte == 0xAF) { return Page_PaginatedObject; } else { throw new AFPParserException(ObjectType.class.getSimpleName() + ": object type 0x" + Integer.toHexString(codeByte) + " is undefined."); } } public int toByte() { if (this == Document) { return 0xA8; } else if (this == Page_PaginatedObject) { return 0xAF; } return 0; } } } /** * MODCA, page 413.

*

* The Font Horizontal Scale Factor triplet is used to carry information to support anamorphic * scaling of an outline technology font. */ public static class FontHorizontalScaleFactor extends Triplet { short horizontalScaleFactor; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); horizontalScaleFactor = UtilBinaryDecoding.parseShort(sfData, offset + 2, 2); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 4; os.write(length); os.write(tripletID.toByte()); os.write(UtilBinaryDecoding.shortToByteArray(horizontalScaleFactor, 2)); } } /** * MODCA, page 414.

The Object Count triplet specifies the number of subordinate objects * of a particular type contained in an object. */ public static class ObjectCount extends Triplet { short subordinateObjectType = 0xFA; byte reserved3 = 0x00; long numberOfObjectsLow; Long numberOfObjectsHigh; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); subordinateObjectType = UtilBinaryDecoding.parseShort(sfData, offset + 2, 1); reserved3 = sfData[offset + 3]; numberOfObjectsLow = UtilBinaryDecoding.parseLong(sfData, offset + 4, 4); if (this.length > 8) { numberOfObjectsHigh = UtilBinaryDecoding.parseLong(sfData, offset + 8, 4); } else { numberOfObjectsHigh = null; } } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = (short) (numberOfObjectsHigh != null ? 12 : 8); os.write(length); os.write(tripletID.toByte()); os.write(subordinateObjectType); os.write(reserved3); os.write(UtilBinaryDecoding.longToByteArray(numberOfObjectsLow, 4)); if (numberOfObjectsHigh != null) { os.write(UtilBinaryDecoding.longToByteArray(numberOfObjectsHigh, 4)); } } } /** * MODCA, page 416.

The Local Date and Time Stamp triplet specifies a date and time stamp * to be associated with an object. */ public static class LocalObjectDateAndTimeStamp extends Triplet { DateAndTimeStampType dateAndTimeStampType; short hundreds; int tens; int dayOfYear; int hourOfDay; int minuteOfHour; int secondOfMinute; int hundredthOfSecond; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); dateAndTimeStampType = DateAndTimeStampType.valueOf(sfData[offset + 2]); hundreds = UtilBinaryDecoding.parseShort(sfData, offset + 3, 1); tens = UtilBinaryDecoding.parseInt(sfData, offset + 4, 2); dayOfYear = UtilBinaryDecoding.parseInt(sfData, offset + 6, 3); hourOfDay = UtilBinaryDecoding.parseInt(sfData, offset + 9, 2); minuteOfHour = UtilBinaryDecoding.parseInt(sfData, offset + 11, 2); secondOfMinute = UtilBinaryDecoding.parseInt(sfData, offset + 13, 2); hundredthOfSecond = UtilBinaryDecoding.parseShort(sfData, offset + 15, 2); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 17; os.write(length); os.write(tripletID.toByte()); os.write(dateAndTimeStampType.toByte()); os.write(hundreds); os.write(UtilBinaryDecoding.intToByteArray(tens, 2)); os.write(UtilBinaryDecoding.intToByteArray(dayOfYear, 3)); os.write(UtilBinaryDecoding.intToByteArray(hourOfDay, 2)); os.write(UtilBinaryDecoding.intToByteArray(minuteOfHour, 2)); os.write(UtilBinaryDecoding.intToByteArray(secondOfMinute, 2)); os.write(UtilBinaryDecoding.intToByteArray(hundredthOfSecond, 2)); } public enum DateAndTimeStampType { Creation, RMARK_Retired, Revision; public static DateAndTimeStampType valueOf(byte codeByte) throws AFPParserException { if (codeByte == 0x00) { return Creation; } else if (codeByte == 0x01) { return RMARK_Retired; } else if (codeByte == 0x03) { return Revision; } else { throw new AFPParserException(DateAndTimeStampType.class.getSimpleName() + ": type 0x" + Integer.toHexString(codeByte) + " is undefined."); } } public int toByte() { if (this == Creation) { return 0x00; } else if (this == RMARK_Retired) { return 0x01; } else if (this == Revision) { return 0x03; } else { return 0; } } } } /** * MODCA, page 418.

*

* The Comment triplet is used to include comments for documentation purposes within a structured * field. */ public static class Comment extends Triplet { String comment; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); comment = UtilCharacterEncoding.decodeEBCDIC(sfData, offset + 2, this.length - 2, config); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { byte[] data = comment.getBytes(config.getAfpCharSet()); length = (short) (data.length + 2); os.write(length); os.write(tripletID.toByte()); os.write(data); } } /** * MODCA, page 419. The Medium Orientation triplet may be used to specify the orientation of the * medium presentation space on the physical medium. */ public static class MediumOrientation extends Triplet { MediumOrientationValue mediumOrientation; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); mediumOrientation = MediumOrientationValue.valueOf(sfData[offset + 2]); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 3; os.write(length); os.write(tripletID.toByte()); os.write(mediumOrientation.toByte()); } public enum MediumOrientationValue { Portrait, Landscape, ReversePortrait, ReverseLandscape, Portrait90, Landscape90; public static MediumOrientationValue valueOf(byte codeByte) throws AFPParserException { for (MediumOrientationValue v : values()) { if (v.ordinal() == codeByte) { return v; } } throw new AFPParserException(MediumOrientationValue.class.getSimpleName() + ": medium orientation value 0x" + Integer.toHexString(codeByte) + " is undefined."); } public int toByte() { return ordinal(); } } } /** * MODCA, page 421.

The Resource Object Include triplet identifies an object to be * included on a presentation space at a specified position. */ public static class ResourceObjectInclude extends Triplet { short objectType = 0xDF; String objectName; int xOrigin; int yOrigin; AFPOrientation orientation; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); objectType = UtilBinaryDecoding.parseShort(sfData, offset + 2, 1); objectName = UtilCharacterEncoding.decodeEBCDIC(sfData, offset + 3, 8, config); xOrigin = UtilBinaryDecoding.parseInt(sfData, offset + 11, 3); yOrigin = UtilBinaryDecoding.parseInt(sfData, offset + 14, 3); if (this.length > 17) { orientation = AFPOrientation.valueOf(UtilBinaryDecoding.parseInt(sfData, offset + 17, 2)); } else { orientation = null; } } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = (short) (orientation == null ? 17 : 19); os.write(length); os.write(tripletID.toByte()); os.write(objectType); os.write(UtilCharacterEncoding.stringToByteArray(objectName, config.getAfpCharSet(), 8, Constants.EBCDIC_BLANK)); os.write(UtilBinaryDecoding.intToByteArray(xOrigin, 3)); os.write(UtilBinaryDecoding.intToByteArray(yOrigin, 3)); if (this.orientation != null) { os.write(orientation.toBytes()); } } } /** * MODCA, page 423.

This triplet is used to specify the resulting appearance when data in * a new presentation space is merged with data in an existing presentation space. */ public static class PresentationSpaceResetMixing extends Triplet { BackgroundMixingFlag backgroundMixingFlag; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); backgroundMixingFlag = BackgroundMixingFlag.valueOf(sfData[offset + 2]); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 3; os.write(length); os.write(tripletID.toByte()); os.write(backgroundMixingFlag.toByte()); } public enum BackgroundMixingFlag { DoNotResetColor, ResetColor; public static BackgroundMixingFlag valueOf(byte codeByte) { if (codeByte == 0x00) { return DoNotResetColor; } else { return ResetColor; } } public int toByte() { if (this == DoNotResetColor) { return 0x00; } else { return 0x80; } } } } /** * MODCA, page 425.

*

* This triplet is used to specify the rules for establishing the color attribute of areas formed * by the intersection of two presentation spaces. It is specified on structured fields associated * with a presentation space that is to be merged onto an existing presentation space. */ public static class PresentationSpaceMixingRule extends Triplet { List mixingRules; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); int pos = 2; mixingRules = new ArrayList((this.length - 2) / 2); while (pos < this.length) { MixingKeywordAndRule mr = new MixingKeywordAndRule(); mr.keyword = MixingKeyword.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + pos, 1)); mr.rule = MixingRule.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + pos + 1, 1)); mixingRules.add(mr); pos += 2; } } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = (short) (2 + 2 * mixingRules.size()); os.write(length); os.write(tripletID.toByte()); for (MixingKeywordAndRule mr : mixingRules) { os.write(mr.toBytes()); } } public List getMixingRules() { return mixingRules; } public void setMixingRules(List mixingRules) { this.mixingRules = mixingRules; } public void addMixingRule(MixingKeywordAndRule mixingRule) { if (mixingRule == null) { return; } if (this.mixingRules == null) { this.mixingRules = new ArrayList(); } mixingRules.add(mixingRule); } public void removeMixingRule(MixingKeywordAndRule mixingRule) { if (this.mixingRules == null) { return; } mixingRules.remove(mixingRule); } public enum MixingKeyword { BackgroudOnBackground, BackgroundOnForeground, ForegroundOnBackground, ForegroundOnForeground; public static MixingKeyword valueOf(short codeByte) throws AFPParserException { for (MixingKeyword mk : values()) { if (mk.ordinal() + 0x70 == codeByte) { return mk; } } throw new AFPParserException(MixingKeyword.class.getSimpleName() + ": mixing keyword 0x" + Integer.toHexString(codeByte) + " is undefined."); } public int toByte() { return ordinal() + 0x70; } } public enum MixingRule { Overpaint, Underpaint, Blend, MODCADefaultMixing; public static MixingRule valueOf(short xodeByte) throws AFPParserException { for (MixingRule mr : values()) { if (mr.ordinal() + 1 == xodeByte) { return mr; } } throw new AFPParserException(MixingRule.class.getSimpleName() + ": mixing rule code 0x" + Integer.toHexString(xodeByte) + " is undefined."); } public int toByte() { return ordinal() + 1; } } public static class MixingKeywordAndRule { MixingKeyword keyword; MixingRule rule; public byte[] toBytes() { byte[] result = new byte[] { (byte) keyword.toByte(), (byte) rule.toByte() }; return result; } public MixingKeyword getKeyword() { return keyword; } public void setKeyword(MixingKeyword keyword) { this.keyword = keyword; } public MixingRule getRule() { return rule; } public void setRule(MixingRule rule) { this.rule = rule; } } } /** * MODCA, page 427.

The Universal Date and Time Stamp triplet specifies a date and time in * accordance with the format defined in ISO 8601: 1988 (E). */ public static class UniversalDateAndTimeStamp extends Triplet { byte reserved2 = 0x00; int year; byte monthOfYear; byte dayOfMonth; byte hourOfDay; byte minuteOfHour; byte secondOfMinute; TimeZone timeZone; byte diffHours; byte diffMinutes; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); reserved2 = sfData[offset + 2]; year = UtilBinaryDecoding.parseInt(sfData, offset + 3, 2); monthOfYear = sfData[offset + 5]; dayOfMonth = sfData[offset + 6]; hourOfDay = sfData[offset + 7]; minuteOfHour = sfData[offset + 8]; secondOfMinute = sfData[offset + 9]; timeZone = TimeZone.valueOf(sfData[offset + 10]); diffHours = sfData[offset + 11]; diffMinutes = sfData[offset + 12]; } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 13; os.write(length); os.write(tripletID.toByte()); os.write(reserved2); os.write(UtilBinaryDecoding.intToByteArray(year, 2)); os.write(monthOfYear); os.write(dayOfMonth); os.write(hourOfDay); os.write(minuteOfHour); os.write(secondOfMinute); os.write(timeZone.toByte()); os.write(diffHours); os.write(diffMinutes); } public enum TimeZone { CoordinatedUTC, AheadUTC, BehindUTC; public static TimeZone valueOf(byte codeByte) throws AFPParserException { for (TimeZone tz : values()) { if (tz.ordinal() == codeByte) { return tz; } } throw new AFPParserException(TimeZone.class.getSimpleName() + ": time zone code 0x" + Integer.toHexString(codeByte) + " is undefined."); } public int toByte() { return ordinal(); } } } /** * MODCA, page 430.

*

* The Toner Saver triplet activates a toner saver mode for printing. The toner saver control * specified by this triplet overrides any other toner saver controls that may be active in the * printer. */ public static class TonerSaver extends Triplet { byte reserved2 = 0x00; TonerSaverFunction tonerSaverFunction; byte[] reserved4_5 = new byte[2]; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); reserved2 = sfData[offset + 2]; tonerSaverFunction = TonerSaverFunction.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + 3, 1)); reserved4_5 = new byte[] { sfData[offset + 4], sfData[offset + 5], }; } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 6; os.write(length); os.write(tripletID.toByte()); os.write(reserved2); os.write(tonerSaverFunction.toByte()); os.write(reserved4_5); } public enum TonerSaverFunction { DeactivateTonerSaver, ActivateTonerSaver, DefaultTonerSaverSetting; public static TonerSaverFunction valueOf(short codeByte) throws AFPParserException { if (codeByte == 0x00) { return DeactivateTonerSaver; } else if (codeByte == 0x01) { return ActivateTonerSaver; } else if (codeByte == 0xFF) { return DefaultTonerSaverSetting; } else { throw new AFPParserException(TonerSaverFunction.class.getSimpleName() + ": tonser saver function code 0x" + Integer.toHexString(codeByte) + " is undefined."); } } public int toByte() { if (this == DefaultTonerSaverSetting) { return 0xFF; } else { return ordinal(); } } } } /** * MODCA, page 432.

*/ public static class ColorFidelity extends Triplet { ExceptionContinuationRule exceptionContinuationRule; byte reserved3 = 0x00; ExceptionReportingRule exceptionReportingRule; byte reserved5 = 0x00; ExceptionSubstitutionRule exceptionSubstitutionRule; byte reserved7 = 0x00; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); exceptionContinuationRule = ExceptionContinuationRule.valueOf(sfData[offset + 2]); reserved3 = sfData[offset + 3]; exceptionReportingRule = ExceptionReportingRule.valueOf(sfData[offset + 4]); reserved5 = sfData[offset + 5]; exceptionSubstitutionRule = ExceptionSubstitutionRule.valueOf(sfData[offset + 6]); reserved7 = sfData[offset + 7]; } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 8; os.write(length); os.write(tripletID.toByte()); os.write(exceptionContinuationRule.toByte()); os.write(reserved3); os.write(exceptionReportingRule.toByte()); os.write(reserved5); os.write(exceptionSubstitutionRule.toByte()); os.write(reserved7); } public enum ExceptionContinuationRule { Stop, DoNotStop; public static ExceptionContinuationRule valueOf(byte ruleByte) throws AFPParserException { for (ExceptionContinuationRule ecr : values()) { if (ecr.ordinal() + 1 == ruleByte) { return ecr; } } throw new AFPParserException(ExceptionContinuationRule.class.getSimpleName() + ": continuation rule 0x" + Integer.toHexString(ruleByte) + " is undefined."); } public int toByte() { return ordinal() + 1; } } public enum ExceptionReportingRule { Report, DoNotReport; public static ExceptionReportingRule valueOf(byte ruleByte) throws AFPParserException { for (ExceptionReportingRule ecr : values()) { if (ecr.ordinal() + 1 == ruleByte) { return ecr; } } throw new AFPParserException(ExceptionReportingRule.class.getSimpleName() + ": reporting rule 0x" + Integer.toHexString(ruleByte) + " is undefined."); } public int toByte() { return ordinal() + 1; } } public enum ExceptionSubstitutionRule { AnySubstitution_Default; public static ExceptionSubstitutionRule valueOf(byte ruleByte) throws AFPParserException { for (ExceptionSubstitutionRule ecr : values()) { if (ecr.ordinal() + 1 == ruleByte) { return ecr; } } throw new AFPParserException(ExceptionSubstitutionRule.class.getSimpleName() + ": substitution rule 0x" + Integer.toHexString(ruleByte) + " is undefined."); } public int toByte() { return ordinal() + 1; } } } /** * MODCA, page 435.

The Font Fidelity triplet is used to specify the exception * continuation rules for font resolution exceptions. */ public static class FontFidelity extends Triplet { ExceptionContinuationRule exceptionContinuationRule; byte[] reserved3_6 = new byte[4]; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); exceptionContinuationRule = ExceptionContinuationRule.valueOf(sfData[offset + 2]); reserved3_6 = new byte[] { sfData[offset + 3], sfData[offset + 4], sfData[offset + 5], sfData[offset + 6], }; } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 7; os.write(length); os.write(tripletID.toByte()); os.write(exceptionContinuationRule.toByte()); os.write(reserved3_6); } } /** * MODCA, page 436.

*

* The Attribute Qualifier triplet is used to specify a qualifier for a document attribute. */ public static class AttributeQualifier extends Triplet { int sequenceNumber; int levelNumber; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); sequenceNumber = UtilBinaryDecoding.parseInt(sfData, offset + 2, 4); levelNumber = UtilBinaryDecoding.parseInt(sfData, offset + 6, 4); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 10; os.write(length); os.write(tripletID.toByte()); os.write(UtilBinaryDecoding.intToByteArray(sequenceNumber, 4)); os.write(UtilBinaryDecoding.intToByteArray(levelNumber, 4)); } } /** * MODCA, page 437.

The Page Position Information triplet is used to tag a page with the * Page Position (PGP) structured field repeating group information that is used to present the * page. The PGP is specified in the medium map referenced by the FQN type X'8D'—Begin Medium Map * Reference triplet. This information is used for viewing the page with a particular form map, * which is normally the form map that the document containing this page was archived * with.

This triplet is not used for printing and is ignored by print servers. */ public static class PagePositionInformation extends Triplet { byte repeatingGroupNumber; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); repeatingGroupNumber = sfData[offset + 2]; } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 3; os.write(length); os.write(tripletID.toByte()); os.write(repeatingGroupNumber); } } /** * MODCA, page 438.

*

* The Parameter Value triplet is used to pass parameter values to an executable program such as * an object handler or a system command interpreter. */ public static class ParameterValue extends Triplet { byte reserved2 = 0x00; ParameterSyntax parameterSyntax; byte[] parameterValue; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); reserved2 = sfData[offset + 2]; parameterSyntax = ParameterSyntax.valueOf(sfData[offset + 3]); int actualLength = StructuredField.getActualLength(sfData, offset, length); if (actualLength < 4) { parameterValue = new byte[actualLength - 4]; System.arraycopy(sfData, offset + 4, parameterValue, 0, parameterValue.length); } else { parameterValue = null; } } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = (short) (parameterValue == null ? 4 : 4 + parameterValue.length); os.write(length); os.write(tripletID.toByte()); os.write(parameterSyntax.toByte()); if (parameterValue != null) { os.write(parameterValue); } } public enum ParameterSyntax { Undefined, UnsignedNumber, SignedNumber, BitString, DefinedConstant, CharacterString, Name; public static ParameterSyntax valueOf(byte codeByte) throws AFPParserException { for (ParameterSyntax ps : values()) { if (ps.ordinal() == codeByte) { return ps; } } throw new AFPParserException(ParameterSyntax.class.getSimpleName() + ": systax code 0x" + Integer.toHexString(codeByte) + " is undefined."); } public int toByte() { return ordinal(); } } } /** * MODCA, page 439.

The Presentation Control triplet specifies flags that control the * presentation of an object. */ public static class PresentationControl extends Triplet { EnumSet presentationControlFlags; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); presentationControlFlags = PresentationControlFlags.valueOf(sfData[offset + 2]); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 3; os.write(length); os.write(tripletID.toByte()); os.write(PresentationControlFlags.toByte(presentationControlFlags)); } public EnumSet getPresentationControlFlags() { return presentationControlFlags; } public void setPresentationControlFlags( EnumSet presentationControlFlags) { this.presentationControlFlags = presentationControlFlags; } /** * Sets the given flag and unsets all mutual exclusive flags. */ public void setPresentationControlFlag(PresentationControlFlags presentationControlFlag) { if (presentationControlFlag == null) { return; } if (presentationControlFlags == null) { presentationControlFlags = EnumSet.noneOf(PresentationControlFlags.class); } PresentationControlFlags.handler.setFlag(presentationControlFlags, presentationControlFlag); } public enum PresentationControlFlags implements IMutualExclusiveGroupedFlag { ViewControl_View(0), ViewControl_DoNotView(0), IndexingControl_Indexing(1), IndexingControl_NoIndexing(1); public static final MutualExclusiveGroupedFlagHandler handler = new MutualExclusiveGroupedFlagHandler(); int group; PresentationControlFlags(int group) { this.group = group; } public static EnumSet valueOf(byte codeByte) { EnumSet result = EnumSet.noneOf(PresentationControlFlags.class); if ((codeByte & 0x80) == 0) { result.add(ViewControl_View); } else { result.add(ViewControl_DoNotView); } if ((codeByte & 0x40) == 0) { result.add(IndexingControl_Indexing); } else { result.add(IndexingControl_NoIndexing); } return result; } public static int toByte(EnumSet flags) { if (flags == null) { return 0x00; } int result = 0; if (flags.contains(ViewControl_DoNotView)) { result |= 80; } if (flags.contains(IndexingControl_NoIndexing)) { result |= 40; } return result; } @Override public int getGroup() { return group; } } } /** * MODCA, page 440.

*

* The Font Resolution and Metric Technology specifies certain metric characteristics of a FOCA * raster-technology font character set which may have affected the formatting of the document * with this font. This information, as carried by the X'84' triplet, may be used by presentation * servers and presentation devices to select the best-matching coded font for presentation. */ public static class FontResolutionAndMetricTechnology extends Triplet { MetricTechnology metricTechnology; AFPUnitBase unitBase; short unitsPerUnitBase; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); metricTechnology = MetricTechnology.valueOf(sfData[offset + 2]); unitBase = AFPUnitBase.valueOf(sfData[offset + 3]); unitsPerUnitBase = UtilBinaryDecoding.parseShort(sfData, offset + 4, 2); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 6; os.write(length); os.write(tripletID.toByte()); os.write(metricTechnology.toByte()); os.write(unitBase.toByte()); os.write(UtilBinaryDecoding.shortToByteArray(unitsPerUnitBase, 2)); } public enum MetricTechnology { Fixed, Relative; public static MetricTechnology valueOf(byte codeByte) throws AFPParserException { if (codeByte == 0x01) { return Fixed; } else if (codeByte == 0x02) { return Relative; } else { throw new AFPParserException(MetricTechnology.class.getSimpleName() + ": technology code 0x" + Integer.toHexString(codeByte) + " is undefined."); } } public int toByte() { return ordinal() + 1; } } } /** * MODCA, page 442.

*

* The Finishing Operation triplet is used to specify finishing operations that are to be applied * to media. */ public static class FinishingOperation extends Triplet { OperationType operationType; byte[] reserved3_4 = new byte[2]; ReferenceCorner referenceCorner; byte operationCount; int offsetOfOperation; List positions; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); operationType = OperationType.valueOf(sfData[offset + 2]); reserved3_4 = new byte[2]; System.arraycopy(sfData, offset + 2, reserved3_4, 0, reserved3_4.length); referenceCorner = ReferenceCorner.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + 5, 1)); operationCount = sfData[offset + 6]; offset = UtilBinaryDecoding.parseInt(sfData, offset + 7, 2); if (this.length > 9) { positions = new ArrayList(); int pos = 9; while (pos < this.length) { positions.add(UtilBinaryDecoding.parseShort(sfData, offset + pos, 2)); pos += 2; } } else { positions = null; } } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = (short) (positions == null ? 9 : 9 + positions.size()); os.write(length); os.write(tripletID.toByte()); os.write(operationType.toByte()); os.write(reserved3_4); os.write(referenceCorner.toByte()); os.write(operationCount); os.write(UtilBinaryDecoding.intToByteArray(offsetOfOperation, 2)); if (positions != null) { for (Short s : positions) { os.write(UtilBinaryDecoding.shortToByteArray(s, 2)); } } } public enum OperationType { CornerStaple(0x01), SaddleStitchOut(0x02), EdgeStitch(0x03), FoldIn(0x04), SeparationCut(0x05), PerforationCut(0x06), ZFold(0x07), CenterFoldIn(0x08), TrimAfterCenterFoldOrSaddleStitch(0x09), Punch(0x0A), PerfectBind(0x0C), RingBind(0x0D), SaddleStitchIn(0x12); int code; OperationType(int code) { this.code = code; } public static OperationType valueOf(byte codeValue) throws AFPParserException { for (OperationType ot : values()) { if (ot.code == codeValue) { return ot; } } throw new AFPParserException(OperationType.class.getSimpleName() + ": operation type 0x" + Integer.toHexString(codeValue) + " is undefined."); } public int toByte() { return code; } } public enum ReferenceCorner { BottomRightCorner_BottomEdge(0x00), TopRightCorner_RightEdge(0x01), TopLeftCorner_TopEdge(0x02), BottomLeftCorner_LeftEdge(0x03), DefaultCorner_DefaultEdge(0xFF); int code; ReferenceCorner(int code) { this.code = code; } public static ReferenceCorner valueOf(short codeByte) throws AFPParserException { for (ReferenceCorner rc : values()) { if (rc.code == codeByte) { return rc; } } throw new AFPParserException(ReferenceCorner.class.getSimpleName() + ": corner/edge code 0x" + Integer.toHexString(codeByte) + " is undefined."); } public int toByte() { return code; } } } /** * MODCA, page 450.

*

* The Text Fidelity triplet is used to specify the exception continuation and reporting rules for * text exceptions. A text exception is detected when an unrecognized or unsupported text control * sequence is encountered in a PTOCA text object. */ public static class TextFidelity extends Triplet { ExceptionContinuationRule exceptionContinuationRule; byte reserved3 = 0x00; ExceptionReportingRule exceptionReportingRule; byte[] reserved5_6 = {0x00, 0x00}; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); exceptionContinuationRule = ExceptionContinuationRule.valueOf(sfData[offset + 2]); reserved3 = sfData[offset + 3]; exceptionReportingRule = ExceptionReportingRule.valueOf(sfData[offset + 4]); reserved5_6 = new byte[2]; System.arraycopy(sfData, offset + 5, reserved5_6, 0, reserved5_6.length); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 7; os.write(length); os.write(tripletID.toByte()); os.write(exceptionContinuationRule.toByte()); os.write(reserved3); os.write(exceptionReportingRule.toByte()); os.write(reserved5_6); } } /** * MODCA, page 452.

*

* The Media Fidelity triplet is used to specify the continuation rule if a request for a specific * media or a specific media bin cannot be satisfied. */ public static class MediaFidelity extends Triplet { ExceptionContinuationRule exceptionContinuationRule; byte reserved3 = 0x00; ExceptionReportingRule exceptionReportingRule; byte[] reserved5_6 = {0x00, 0x00}; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); exceptionContinuationRule = ExceptionContinuationRule.valueOf(sfData[offset + 2]); reserved3 = sfData[offset + 3]; exceptionReportingRule = ExceptionReportingRule.valueOf(sfData[offset + 4]); reserved5_6 = new byte[2]; System.arraycopy(sfData, offset + 5, reserved5_6, 0, reserved5_6.length); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 7; os.write(length); os.write(tripletID.toByte()); os.write(exceptionContinuationRule.toByte()); os.write(reserved3); os.write(exceptionReportingRule.toByte()); os.write(reserved5_6); } } /** * MODCA, page 454.

*

* The Finishing Fidelity triplet is used to specify the exception continuation and reporting * rules for finishing exceptions. A finishing exception is detected when the specified finishing * operation cannot be satisfied. */ public static class FinishingFidelity extends Triplet { ExceptionContinuationRule exceptionContinuationRule; byte reserved3 = 0x00; ExceptionReportingRule exceptionReportingRule; byte[] reserved5_6 = {0x00, 0x00}; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); exceptionContinuationRule = ExceptionContinuationRule.valueOf(sfData[offset + 2]); reserved3 = sfData[offset + 3]; exceptionReportingRule = ExceptionReportingRule.valueOf(sfData[offset + 4]); reserved5_6 = new byte[2]; System.arraycopy(sfData, offset + 5, reserved5_6, 0, reserved5_6.length); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 7; os.write(length); os.write(tripletID.toByte()); os.write(exceptionContinuationRule.toByte()); os.write(reserved3); os.write(exceptionReportingRule.toByte()); os.write(reserved5_6); } } /** * MODCA, page 456.

The Data-Object Font Descriptor triplet is used to specify the * parameters needed to render a data-object font. Data-object fonts are non-FOCA font resources, * such as TrueType and OpenType fonts. An MDR structured field is used to map a data-object font * as a resource. */ public static class DataObjectFontDescriptor extends Triplet { EnumSet fontInformationFlags; short fontTechnology; short specifiedVerticalFontSize; short horizontalScaleFactor; AFPOrientation characterOrientation; short encodingEnvironment; short encodingIdentifier; byte[] reserved14_15 = {0x00, 0x00}; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); fontInformationFlags = FontInformationFlag.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + 2, 1)); fontTechnology = UtilBinaryDecoding.parseShort(sfData, offset + 3, 1); specifiedVerticalFontSize = UtilBinaryDecoding.parseShort(sfData, offset + 4, 2); horizontalScaleFactor = UtilBinaryDecoding.parseShort(sfData, offset + 6, 2); characterOrientation = AFPOrientation.valueOf(UtilBinaryDecoding.parseInt(sfData, offset + 8, 2)); encodingEnvironment = UtilBinaryDecoding.parseShort(sfData, offset + 10, 2); encodingIdentifier = UtilBinaryDecoding.parseShort(sfData, offset + 12, 2); reserved14_15 = new byte[2]; System.arraycopy(sfData, offset + 14, reserved14_15, 0, reserved14_15.length); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 16; os.write(length); os.write(tripletID.toByte()); os.write(FontInformationFlag.toByte(fontInformationFlags)); os.write(fontTechnology); os.write(UtilBinaryDecoding.shortToByteArray(specifiedVerticalFontSize, 2)); os.write(UtilBinaryDecoding.shortToByteArray(horizontalScaleFactor, 2)); os.write(characterOrientation.toBytes()); os.write(UtilBinaryDecoding.shortToByteArray(encodingEnvironment, 2)); os.write(UtilBinaryDecoding.shortToByteArray(encodingIdentifier, 2)); os.write(reserved14_15); } public enum FontInformationFlag implements IMutualExclusiveGroupedFlag { MICR_NonMICR(0), MICR_MICR(0), Location_Anyware(1), Location_ResourceGroup(1); public static final MutualExclusiveGroupedFlagHandler handler = new MutualExclusiveGroupedFlagHandler(); int group; FontInformationFlag(int group) { this.group = group; } public static EnumSet valueOf(short codeByte) { EnumSet result = EnumSet.noneOf(FontInformationFlag.class); if ((codeByte & 0x80) == 0) { result.add(MICR_NonMICR); } else { result.add(MICR_MICR); } if ((codeByte & 0x40) == 0) { result.add(Location_Anyware); } else { result.add(Location_ResourceGroup); } return result; } public static int toByte(EnumSet flags) { int result = 0; if (flags.contains(MICR_MICR)) { result |= 0x80; } if (flags.contains(Location_ResourceGroup)) { result |= 0x40; } return result; } public int getGroup() { return group; } } } /** * MODCA, page 461.

*

* The Locale Selector triplet is used to identify the end-user community for presentation text * data. The locale information consists of an ISO-639 based language code, an ISO-15924 based * script code, an ISO-3166 based region code, and an application-specific variant code. The * encoding for all four parameters is UTF-16BE. Additional information on these parameters can be * found at the following urls:

  • The definition of language codes can be found at * http://lcweb.loc.gov/standards/ iso639-2/iso639jac.html
  • The definition of script codes can * be found at http://www.unicode.org/reports/tr24
  • The definition of region codes can be * found at http://www.iso.org/iso/en/prods- services/iso3166ma/index.html
*/ public static class LocaleSelector extends Triplet { byte reserved2 = 0x00; EnumSet flags; String languageCode; String scriptCode; String regionCode; byte[] reserved28_35 = new byte[8]; String variantCode; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); reserved2 = sfData[offset + 2]; flags = LocalSelectorFlag.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + 3, 1)); languageCode = new String(sfData, offset + 4, 8); scriptCode = new String(sfData, offset + 12, 8); regionCode = new String(sfData, offset + 20, 8); reserved28_35 = new byte[8]; System.arraycopy(sfData, offset + 21, reserved28_35, 0, reserved28_35.length); if (this.length > 36) { variantCode = new String(sfData, offset + 36, this.length - 36); } else { variantCode = null; } } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { byte[] variantCodeData = variantCode != null ? variantCode.getBytes(Charset.defaultCharset()) : null; length = (short) (variantCodeData == null ? 36 : 36 + variantCodeData.length); os.write(length); os.write(tripletID.toByte()); os.write(reserved2); os.write(LocalSelectorFlag.toByte(flags)); os.write(UtilCharacterEncoding.stringToByteArray(languageCode, Charset.defaultCharset(), 8, (byte) 0x00)); os.write(UtilCharacterEncoding.stringToByteArray(scriptCode, Charset.defaultCharset(), 8, (byte) 0x00)); os.write(UtilCharacterEncoding.stringToByteArray(regionCode, Charset.defaultCharset(), 8, (byte) 0x00)); os.write(reserved28_35); if (variantCodeData != null) { os.write(variantCodeData); } } public byte getReserved2() { return reserved2; } public void setReserved2(byte reserved2) { this.reserved2 = reserved2; } public EnumSet getFlags() { return flags; } public void setFlags(EnumSet flags) { this.flags = flags; } /** * Sets the given flag and un-sets the corresponding mutual exclusive flags. * * @param flag flag to set. */ public void setFlag(LocalSelectorFlag flag) { if (flag == null) { return; } if (flags == null) { flags = EnumSet.noneOf(LocalSelectorFlag.class); } LocalSelectorFlag.handler.setFlag(flags, flag); } public String getLanguageCode() { return languageCode; } public void setLanguageCode(String languageCode) { this.languageCode = languageCode; } public String getScriptCode() { return scriptCode; } public void setScriptCode(String scriptCode) { this.scriptCode = scriptCode; } public String getRegionCode() { return regionCode; } public void setRegionCode(String regionCode) { this.regionCode = regionCode; } public byte[] getReserved28_35() { return reserved28_35; } public void setReserved28_35(byte[] reserved28_35) { this.reserved28_35 = reserved28_35; } public String getVariantCode() { return variantCode; } public void setVariantCode(String variantCode) { this.variantCode = variantCode; } public enum LocalSelectorFlag implements IMutualExclusiveGroupedFlag { LanguageCode_NotSpecified(0), LanguageCode_TwoBytes(0), LanguageCode_ThreeBytes(0), ScriptCode_NotSpecified(1), ScriptCode_FourCharacter(1), RegionCode_NotSpecified(2), RegionCode_TwoBytes(2), RegionCode_ThreeBytes(3); public static final MutualExclusiveGroupedFlagHandler handler = new MutualExclusiveGroupedFlagHandler(); int group; LocalSelectorFlag(int group) { this.group = group; } public static EnumSet valueOf(short codeByte) { EnumSet result = EnumSet.noneOf(LocalSelectorFlag.class); short languageCode = (short) (codeByte >>> 4); short regionCode = (short) (codeByte & 0x07); if (languageCode == 0x00) { result.add(LanguageCode_NotSpecified); } else if (languageCode == 0x02) { result.add(LanguageCode_TwoBytes); } else if (languageCode == 0x03) { result.add(LanguageCode_ThreeBytes); } if ((codeByte & 0x08) == 0) { result.add(ScriptCode_NotSpecified); } else { result.add(ScriptCode_FourCharacter); } if (regionCode == 0x00) { result.add(RegionCode_NotSpecified); } else if (regionCode == 0x02) { result.add(RegionCode_TwoBytes); } else if (regionCode == 0x03) { result.add(RegionCode_ThreeBytes); } return result; } public static int toByte(EnumSet flags) { int result = 0; if (flags.contains(LanguageCode_TwoBytes)) { result |= 0x02; } else if (flags.contains(LanguageCode_ThreeBytes)) { result |= 0x03; } result <<= 1; if (flags.contains(ScriptCode_FourCharacter)) { result |= 0x01; } result <<= 3; if (flags.contains(RegionCode_TwoBytes)) { result |= 0x02; } else if (flags.contains(RegionCode_ThreeBytes)) { result |= 0x03; } return result; } public int getGroup() { return group; } } } /** * MODCA, page 461.

The UP3i Finishing Operation triplet is used to specify finishing * operations that are to be applied to media. More specifically, this triplet is a carrier for * finishing operations and parameters that are defined by the UP3i consortium in the UP3i * Specification. */ public static class UP3iFinishingOperation extends Triplet { short sequenceNumber; byte reserved3 = 0x00; byte[] up3iData; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); sequenceNumber = UtilBinaryDecoding.parseShort(sfData, offset + 2, 1); reserved3 = sfData[offset + 3]; up3iData = new byte[this.length - 4]; System.arraycopy(sfData, offset + 4, up3iData, 0, up3iData.length); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = (short) (4 + up3iData.length); os.write(length); os.write(tripletID.toByte()); os.write(sequenceNumber); os.write(reserved3); os.write(up3iData); } public short getSequenceNumber() { return sequenceNumber; } public void setSequenceNumber(short sequenceNumber) { this.sequenceNumber = sequenceNumber; } public byte getReserved3() { return reserved3; } public void setReserved3(byte reserved3) { this.reserved3 = reserved3; } public byte[] getUp3iData() { return up3iData; } public void setUp3iData(byte[] up3iData) { this.up3iData = up3iData; } } /** * MODCA, page 466.

*

* The Color Management Resource Descriptor triplet specifies the processing mode and scope for a * Color Management Resource (CMR). */ public static class ColorManagementResourceDescriptor extends Triplet { byte reserved2; CMRProcessingMode cmrProcessingMode; CMRScope cmrScope; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); reserved2 = sfData[offset + 2]; cmrProcessingMode = CMRProcessingMode.valueOf(sfData[offset + 3]); cmrScope = CMRScope.valueOf(sfData[offset + 4]); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 5; os.write(length); os.write(tripletID.toByte()); os.write(cmrProcessingMode.toByte()); os.write(cmrScope.toByte()); } public byte getReserved2() { return reserved2; } public void setReserved2(byte reserved2) { this.reserved2 = reserved2; } public CMRProcessingMode getCmrProcessingMode() { return cmrProcessingMode; } public void setCmrProcessingMode(CMRProcessingMode cmrProcessingMode) { this.cmrProcessingMode = cmrProcessingMode; } public CMRScope getCmrScope() { return cmrScope; } public void setCmrScope(CMRScope cmrScope) { this.cmrScope = cmrScope; } public enum CMRProcessingMode { AuditCMR, InstructionCMR, LinkCMR; public static CMRProcessingMode valueOf(byte codeByte) throws AFPParserException { for (CMRProcessingMode pm : values()) { if (pm.ordinal() + 1 == codeByte) { return pm; } } throw new AFPParserException(CMRProcessingMode.class.getSimpleName() + ": processing mode 0x" + Integer.toHexString(codeByte) + " is undefined."); } public int toByte() { return ordinal() + 1; } } public enum CMRScope { DataObject, PageOrOverlay, Document, PrintFile, PageGroup_SheetGroup; public static CMRScope valueOf(byte codeByte) throws AFPParserException { for (CMRScope sc : values()) { if (sc.ordinal() + 1 == codeByte) { return sc; } } throw new AFPParserException(CMRScope.class.getSimpleName() + ": scope code 0x" + Integer.toHexString(codeByte) + " is undefined."); } public int toByte() { return ordinal() + 1; } } } /** * MODCA, page 468.

*

* The Rendering Intent triplet specifies the rendering intent parameter, which is used to modify * the final appearance of color data. This parameter is based on the rendering intents defined by * the International Color Consortium (ICC). For more information on rendering intents, see the * International Color Consortium Specification ICC.x, File Format for Color Profiles. */ public static class RenderingIntent extends Triplet { byte[] reserved2_3 = new byte[2]; Intent intentForIOCA; Intent intentForContainerNonIOCA; Intent intentForPTOCA; Intent intentForGOCA; byte[] reserved8_9 = new byte[2]; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); reserved2_3 = new byte[] {sfData[offset + 2], sfData[offset + 3]}; intentForIOCA = Intent.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + 4, 1)); intentForContainerNonIOCA = Intent.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + 5, 1)); intentForPTOCA = Intent.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + 6, 1)); intentForGOCA = Intent.valueOf(UtilBinaryDecoding.parseShort(sfData, offset + 7, 1)); reserved8_9 = new byte[] {sfData[offset + 8], sfData[offset + 9]}; } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 10; os.write(length); os.write(tripletID.toByte()); os.write(reserved2_3); os.write(intentForIOCA.toByte()); os.write(intentForContainerNonIOCA.toByte()); os.write(intentForPTOCA.toByte()); os.write(intentForGOCA.toByte()); os.write(reserved8_9); } public enum Intent { Perceptual, MediaRelativeColorimetric, Saturation, iccAbsoluteColorimetric, NotSpecified; public static Intent valueOf(short codeByte) throws AFPParserException { if (codeByte == 0xFF) { return NotSpecified; } for (Intent intent : values()) { if (intent.ordinal() == codeByte) { return intent; } } throw new AFPParserException(Intent.class.getSimpleName() + ": intent code 0x" + Integer.toHexString(codeByte) + " is undefined."); } public int toByte() { if (this == NotSpecified) { return 0xFF; } else { return ordinal(); } } } } /** * MODCA, page 471.

*

* The CMR Tag Fidelity triplet is used to specify the exception continuation and reporting rules * for Color Management Resource (CMR) tag exceptions. A CMR tag exception is detected when an * unsupported CMR tag is encountered in a Color Management Resource (CMR). */ public static class CMRTagFidelity extends Triplet { ExceptionContinuationRule exceptionContinuationRule; byte reserved3 = 0x00; ExceptionReportingRule exceptionReportingRule; byte[] reserved5_6 = {0x00, 0x00}; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); exceptionContinuationRule = ExceptionContinuationRule.valueOf(sfData[offset + 2]); reserved3 = sfData[offset + 3]; exceptionReportingRule = ExceptionReportingRule.valueOf(sfData[offset + 4]); reserved5_6 = new byte[2]; System.arraycopy(sfData, offset + 5, reserved5_6, 0, reserved5_6.length); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 7; os.write(length); os.write(tripletID.toByte()); os.write(exceptionContinuationRule.toByte()); os.write(reserved3); os.write(exceptionReportingRule.toByte()); os.write(reserved5_6); } } /** * MODCA, page 473.

*

* The Device Appearance triplet specifies one of a set of architected appearances to be assumed * by the presentation device. */ public static class DeviceAppearance extends Triplet { byte reserved2 = 0x00; Appearance appearance; byte[] reserved5_6 = {0x00, 0x00}; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); reserved2 = sfData[offset + 2]; appearance = Appearance.valueOf(sfData[offset + 3]); reserved5_6 = new byte[] {sfData[offset + 5], sfData[offset + 6]}; } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 7; os.write(length); os.write(tripletID.toByte()); os.write(reserved2); os.write(appearance.toByte()); os.write(reserved5_6); } public enum Appearance { DeviceDefault, DeviceDefaultMonochrome; public static Appearance valueOf(byte codeByte) { if (codeByte == 0x00) { return DeviceDefault; } else { return DeviceDefaultMonochrome; } } public int toByte() { return ordinal(); } } } /** * MODCA, page 474.

*

* The Image Resolution triplet specifies the resolution of a raster image. */ public static class ImageResolution extends Triplet { byte[] reserved2_3 = {0x00, 0x00}; AFPUnitBase xUnitBase; AFPUnitBase yUnitBase; short xUnitsPerUnitBase; short yUnitsPerUnitBase; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); reserved2_3 = new byte[] {sfData[offset + 2], sfData[offset + 3]}; xUnitBase = AFPUnitBase.valueOf(sfData[offset + 4]); yUnitBase = AFPUnitBase.valueOf(sfData[offset + 5]); xUnitsPerUnitBase = UtilBinaryDecoding.parseShort(sfData, offset + 6, 2); yUnitsPerUnitBase = UtilBinaryDecoding.parseShort(sfData, offset + 8, 2); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 10; os.write(length); os.write(tripletID.toByte()); os.write(reserved2_3); os.write(xUnitBase.toByte()); os.write(yUnitBase.toByte()); os.write(UtilBinaryDecoding.shortToByteArray(xUnitsPerUnitBase, 2)); os.write(UtilBinaryDecoding.shortToByteArray(yUnitsPerUnitBase, 2)); } } /** * MODCA, page 476.

The Object Container Presentation Space Size triplet specifies the * presentation space size, or how such a size is determined, for certain container object types. */ public static class ObjectContainerPresentationSpaceSize extends Triplet { byte[] reserved2_3 = {0x00, 0x00}; PDFPresentationSpace pdfPresentationSpace; @Override public void decodeAFP(byte[] sfData, int offset, int length, AFPParserConfiguration config) throws AFPParserException { super.decodeAFP(sfData, offset, length, config); reserved2_3 = new byte[] {sfData[offset + 2], sfData[offset + 3]}; pdfPresentationSpace = PDFPresentationSpace.valueOf(sfData[offset + 4]); } @Override public void writeAFP(OutputStream os, AFPParserConfiguration config) throws IOException { length = 5; os.write(length); os.write(tripletID.toByte()); os.write(reserved2_3); os.write(pdfPresentationSpace.toByte()); } public enum PDFPresentationSpace { MediaBox, CropBox, BleedBox, TrimBox, ArtBox; public static PDFPresentationSpace valueOf(byte codeByte) throws AFPParserException { for (PDFPresentationSpace ps : values()) { if (ps.ordinal() + 1 == codeByte) { return ps; } } throw new AFPParserException(PDFPresentationSpace.class.getSimpleName() + ": presentation space code 0x" + Integer.toHexString(codeByte) + " is undfined."); } public int toByte() { return ordinal() + 1; } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy