first commit

This commit is contained in:
MaddoScientisto 2026-03-14 20:04:39 +01:00
commit 4d332ef662
27586 changed files with 3281783 additions and 0 deletions

View file

@ -0,0 +1,139 @@
package com.itextpdf.xmp;
public interface XMPConst {
public static final String NS_XML = "http://www.w3.org/XML/1998/namespace";
public static final String NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
public static final String NS_DC = "http://purl.org/dc/elements/1.1/";
public static final String NS_IPTCCORE = "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/";
public static final String NS_IPTCEXT = "http://iptc.org/std/Iptc4xmpExt/2008-02-29/";
public static final String NS_DICOM = "http://ns.adobe.com/DICOM/";
public static final String NS_PLUS = "http://ns.useplus.org/ldf/xmp/1.0/";
public static final String NS_X = "adobe:ns:meta/";
public static final String NS_IX = "http://ns.adobe.com/iX/1.0/";
public static final String NS_XMP = "http://ns.adobe.com/xap/1.0/";
public static final String NS_XMP_RIGHTS = "http://ns.adobe.com/xap/1.0/rights/";
public static final String NS_XMP_MM = "http://ns.adobe.com/xap/1.0/mm/";
public static final String NS_XMP_BJ = "http://ns.adobe.com/xap/1.0/bj/";
public static final String NS_XMP_NOTE = "http://ns.adobe.com/xmp/note/";
public static final String NS_PDF = "http://ns.adobe.com/pdf/1.3/";
public static final String NS_PDFX = "http://ns.adobe.com/pdfx/1.3/";
public static final String NS_PDFX_ID = "http://www.npes.org/pdfx/ns/id/";
public static final String NS_PDFA_SCHEMA = "http://www.aiim.org/pdfa/ns/schema#";
public static final String NS_PDFA_PROPERTY = "http://www.aiim.org/pdfa/ns/property#";
public static final String NS_PDFA_TYPE = "http://www.aiim.org/pdfa/ns/type#";
public static final String NS_PDFA_FIELD = "http://www.aiim.org/pdfa/ns/field#";
public static final String NS_PDFA_ID = "http://www.aiim.org/pdfa/ns/id/";
public static final String NS_PDFUA_ID = "http://www.aiim.org/pdfua/ns/id/";
public static final String NS_PDFA_EXTENSION = "http://www.aiim.org/pdfa/ns/extension/";
public static final String NS_PHOTOSHOP = "http://ns.adobe.com/photoshop/1.0/";
public static final String NS_PSALBUM = "http://ns.adobe.com/album/1.0/";
public static final String NS_EXIF = "http://ns.adobe.com/exif/1.0/";
public static final String NS_EXIFX = "http://cipa.jp/exif/1.0/";
public static final String NS_EXIF_AUX = "http://ns.adobe.com/exif/1.0/aux/";
public static final String NS_TIFF = "http://ns.adobe.com/tiff/1.0/";
public static final String NS_PNG = "http://ns.adobe.com/png/1.0/";
public static final String NS_JPEG = "http://ns.adobe.com/jpeg/1.0/";
public static final String NS_JP2K = "http://ns.adobe.com/jp2k/1.0/";
public static final String NS_CAMERARAW = "http://ns.adobe.com/camera-raw-settings/1.0/";
public static final String NS_ADOBESTOCKPHOTO = "http://ns.adobe.com/StockPhoto/1.0/";
public static final String NS_CREATOR_ATOM = "http://ns.adobe.com/creatorAtom/1.0/";
public static final String NS_ASF = "http://ns.adobe.com/asf/1.0/";
public static final String NS_WAV = "http://ns.adobe.com/xmp/wav/1.0/";
public static final String NS_BWF = "http://ns.adobe.com/bwf/bext/1.0/";
public static final String NS_RIFFINFO = "http://ns.adobe.com/riff/info/";
public static final String NS_SCRIPT = "http://ns.adobe.com/xmp/1.0/Script/";
public static final String NS_TXMP = "http://ns.adobe.com/TransformXMP/";
public static final String NS_SWF = "http://ns.adobe.com/swf/1.0/";
public static final String NS_DM = "http://ns.adobe.com/xmp/1.0/DynamicMedia/";
public static final String NS_TRANSIENT = "http://ns.adobe.com/xmp/transient/1.0/";
public static final String NS_DC_DEPRECATED = "http://purl.org/dc/1.1/";
public static final String TYPE_IDENTIFIERQUAL = "http://ns.adobe.com/xmp/Identifier/qual/1.0/";
public static final String TYPE_DIMENSIONS = "http://ns.adobe.com/xap/1.0/sType/Dimensions#";
public static final String TYPE_TEXT = "http://ns.adobe.com/xap/1.0/t/";
public static final String TYPE_PAGEDFILE = "http://ns.adobe.com/xap/1.0/t/pg/";
public static final String TYPE_GRAPHICS = "http://ns.adobe.com/xap/1.0/g/";
public static final String TYPE_IMAGE = "http://ns.adobe.com/xap/1.0/g/img/";
public static final String TYPE_FONT = "http://ns.adobe.com/xap/1.0/sType/Font#";
public static final String TYPE_RESOURCEEVENT = "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#";
public static final String TYPE_RESOURCEREF = "http://ns.adobe.com/xap/1.0/sType/ResourceRef#";
public static final String TYPE_ST_VERSION = "http://ns.adobe.com/xap/1.0/sType/Version#";
public static final String TYPE_ST_JOB = "http://ns.adobe.com/xap/1.0/sType/Job#";
public static final String TYPE_MANIFESTITEM = "http://ns.adobe.com/xap/1.0/sType/ManifestItem#";
public static final String TRUESTR = "True";
public static final String FALSESTR = "False";
public static final int ARRAY_LAST_ITEM = -1;
public static final String ARRAY_ITEM_NAME = "[]";
public static final String X_DEFAULT = "x-default";
public static final String XML_LANG = "xml:lang";
public static final String RDF_TYPE = "rdf:type";
public static final String XMP_PI = "xpacket";
public static final String TAG_XMPMETA = "xmpmeta";
public static final String TAG_XAPMETA = "xapmeta";
}

View file

@ -0,0 +1,48 @@
package com.itextpdf.xmp;
import java.util.Calendar;
import java.util.TimeZone;
public interface XMPDateTime extends Comparable {
int getYear();
void setYear(int paramInt);
int getMonth();
void setMonth(int paramInt);
int getDay();
void setDay(int paramInt);
int getHour();
void setHour(int paramInt);
int getMinute();
void setMinute(int paramInt);
int getSecond();
void setSecond(int paramInt);
int getNanoSecond();
void setNanoSecond(int paramInt);
TimeZone getTimeZone();
void setTimeZone(TimeZone paramTimeZone);
boolean hasDate();
boolean hasTime();
boolean hasTimeZone();
Calendar getCalendar();
String getISO8601String();
}

View file

@ -0,0 +1,68 @@
package com.itextpdf.xmp;
import com.itextpdf.xmp.impl.XMPDateTimeImpl;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
public final class XMPDateTimeFactory {
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
public static XMPDateTime createFromCalendar(Calendar calendar) {
return new XMPDateTimeImpl(calendar);
}
public static XMPDateTime create() {
return new XMPDateTimeImpl();
}
public static XMPDateTime create(int year, int month, int day) {
XMPDateTime dt = new XMPDateTimeImpl();
dt.setYear(year);
dt.setMonth(month);
dt.setDay(day);
return dt;
}
public static XMPDateTime create(int year, int month, int day, int hour, int minute, int second, int nanoSecond) {
XMPDateTime dt = new XMPDateTimeImpl();
dt.setYear(year);
dt.setMonth(month);
dt.setDay(day);
dt.setHour(hour);
dt.setMinute(minute);
dt.setSecond(second);
dt.setNanoSecond(nanoSecond);
return dt;
}
public static XMPDateTime createFromISO8601(String strValue) throws XMPException {
return new XMPDateTimeImpl(strValue);
}
public static XMPDateTime getCurrentDateTime() {
return new XMPDateTimeImpl(new GregorianCalendar());
}
public static XMPDateTime setLocalTimeZone(XMPDateTime dateTime) {
Calendar cal = dateTime.getCalendar();
cal.setTimeZone(TimeZone.getDefault());
return new XMPDateTimeImpl(cal);
}
public static XMPDateTime convertToUTCTime(XMPDateTime dateTime) {
long timeInMillis = dateTime.getCalendar().getTimeInMillis();
GregorianCalendar cal = new GregorianCalendar(UTC);
cal.setGregorianChange(new Date(Long.MIN_VALUE));
cal.setTimeInMillis(timeInMillis);
return new XMPDateTimeImpl(cal);
}
public static XMPDateTime convertToLocalTime(XMPDateTime dateTime) {
long timeInMillis = dateTime.getCalendar().getTimeInMillis();
GregorianCalendar cal = new GregorianCalendar();
cal.setTimeInMillis(timeInMillis);
return new XMPDateTimeImpl(cal);
}
}

View file

@ -0,0 +1,29 @@
package com.itextpdf.xmp;
public interface XMPError {
public static final int UNKNOWN = 0;
public static final int BADPARAM = 4;
public static final int BADVALUE = 5;
public static final int INTERNALFAILURE = 9;
public static final int BADSCHEMA = 101;
public static final int BADXPATH = 102;
public static final int BADOPTIONS = 103;
public static final int BADINDEX = 104;
public static final int BADSERIALIZE = 107;
public static final int BADXML = 201;
public static final int BADRDF = 202;
public static final int BADXMP = 203;
public static final int BADSTREAM = 204;
}

View file

@ -0,0 +1,19 @@
package com.itextpdf.xmp;
public class XMPException extends Exception {
private int errorCode;
public XMPException(String message, int errorCode) {
super(message);
this.errorCode = errorCode;
}
public XMPException(String message, int errorCode, Throwable t) {
super(message, t);
this.errorCode = errorCode;
}
public int getErrorCode() {
return this.errorCode;
}
}

View file

@ -0,0 +1,9 @@
package com.itextpdf.xmp;
import java.util.Iterator;
public interface XMPIterator extends Iterator {
void skipSubtree();
void skipSiblings();
}

View file

@ -0,0 +1,129 @@
package com.itextpdf.xmp;
import com.itextpdf.xmp.options.IteratorOptions;
import com.itextpdf.xmp.options.ParseOptions;
import com.itextpdf.xmp.options.PropertyOptions;
import com.itextpdf.xmp.properties.XMPProperty;
import java.util.Calendar;
public interface XMPMeta extends Cloneable {
XMPProperty getProperty(String paramString1, String paramString2) throws XMPException;
XMPProperty getArrayItem(String paramString1, String paramString2, int paramInt) throws XMPException;
int countArrayItems(String paramString1, String paramString2) throws XMPException;
XMPProperty getStructField(String paramString1, String paramString2, String paramString3, String paramString4) throws XMPException;
XMPProperty getQualifier(String paramString1, String paramString2, String paramString3, String paramString4) throws XMPException;
void setProperty(String paramString1, String paramString2, Object paramObject, PropertyOptions paramPropertyOptions) throws XMPException;
void setProperty(String paramString1, String paramString2, Object paramObject) throws XMPException;
void setArrayItem(String paramString1, String paramString2, int paramInt, String paramString3, PropertyOptions paramPropertyOptions) throws XMPException;
void setArrayItem(String paramString1, String paramString2, int paramInt, String paramString3) throws XMPException;
void insertArrayItem(String paramString1, String paramString2, int paramInt, String paramString3, PropertyOptions paramPropertyOptions) throws XMPException;
void insertArrayItem(String paramString1, String paramString2, int paramInt, String paramString3) throws XMPException;
void appendArrayItem(String paramString1, String paramString2, PropertyOptions paramPropertyOptions1, String paramString3, PropertyOptions paramPropertyOptions2) throws XMPException;
void appendArrayItem(String paramString1, String paramString2, String paramString3) throws XMPException;
void setStructField(String paramString1, String paramString2, String paramString3, String paramString4, String paramString5, PropertyOptions paramPropertyOptions) throws XMPException;
void setStructField(String paramString1, String paramString2, String paramString3, String paramString4, String paramString5) throws XMPException;
void setQualifier(String paramString1, String paramString2, String paramString3, String paramString4, String paramString5, PropertyOptions paramPropertyOptions) throws XMPException;
void setQualifier(String paramString1, String paramString2, String paramString3, String paramString4, String paramString5) throws XMPException;
void deleteProperty(String paramString1, String paramString2);
void deleteArrayItem(String paramString1, String paramString2, int paramInt);
void deleteStructField(String paramString1, String paramString2, String paramString3, String paramString4);
void deleteQualifier(String paramString1, String paramString2, String paramString3, String paramString4);
boolean doesPropertyExist(String paramString1, String paramString2);
boolean doesArrayItemExist(String paramString1, String paramString2, int paramInt);
boolean doesStructFieldExist(String paramString1, String paramString2, String paramString3, String paramString4);
boolean doesQualifierExist(String paramString1, String paramString2, String paramString3, String paramString4);
XMPProperty getLocalizedText(String paramString1, String paramString2, String paramString3, String paramString4) throws XMPException;
void setLocalizedText(String paramString1, String paramString2, String paramString3, String paramString4, String paramString5, PropertyOptions paramPropertyOptions) throws XMPException;
void setLocalizedText(String paramString1, String paramString2, String paramString3, String paramString4, String paramString5) throws XMPException;
Boolean getPropertyBoolean(String paramString1, String paramString2) throws XMPException;
Integer getPropertyInteger(String paramString1, String paramString2) throws XMPException;
Long getPropertyLong(String paramString1, String paramString2) throws XMPException;
Double getPropertyDouble(String paramString1, String paramString2) throws XMPException;
XMPDateTime getPropertyDate(String paramString1, String paramString2) throws XMPException;
Calendar getPropertyCalendar(String paramString1, String paramString2) throws XMPException;
byte[] getPropertyBase64(String paramString1, String paramString2) throws XMPException;
String getPropertyString(String paramString1, String paramString2) throws XMPException;
void setPropertyBoolean(String paramString1, String paramString2, boolean paramBoolean, PropertyOptions paramPropertyOptions) throws XMPException;
void setPropertyBoolean(String paramString1, String paramString2, boolean paramBoolean) throws XMPException;
void setPropertyInteger(String paramString1, String paramString2, int paramInt, PropertyOptions paramPropertyOptions) throws XMPException;
void setPropertyInteger(String paramString1, String paramString2, int paramInt) throws XMPException;
void setPropertyLong(String paramString1, String paramString2, long paramLong, PropertyOptions paramPropertyOptions) throws XMPException;
void setPropertyLong(String paramString1, String paramString2, long paramLong) throws XMPException;
void setPropertyDouble(String paramString1, String paramString2, double paramDouble, PropertyOptions paramPropertyOptions) throws XMPException;
void setPropertyDouble(String paramString1, String paramString2, double paramDouble) throws XMPException;
void setPropertyDate(String paramString1, String paramString2, XMPDateTime paramXMPDateTime, PropertyOptions paramPropertyOptions) throws XMPException;
void setPropertyDate(String paramString1, String paramString2, XMPDateTime paramXMPDateTime) throws XMPException;
void setPropertyCalendar(String paramString1, String paramString2, Calendar paramCalendar, PropertyOptions paramPropertyOptions) throws XMPException;
void setPropertyCalendar(String paramString1, String paramString2, Calendar paramCalendar) throws XMPException;
void setPropertyBase64(String paramString1, String paramString2, byte[] paramArrayOfbyte, PropertyOptions paramPropertyOptions) throws XMPException;
void setPropertyBase64(String paramString1, String paramString2, byte[] paramArrayOfbyte) throws XMPException;
XMPIterator iterator() throws XMPException;
XMPIterator iterator(IteratorOptions paramIteratorOptions) throws XMPException;
XMPIterator iterator(String paramString1, String paramString2, IteratorOptions paramIteratorOptions) throws XMPException;
String getObjectName();
void setObjectName(String paramString);
String getPacketHeader();
Object clone();
void sort();
void normalize(ParseOptions paramParseOptions) throws XMPException;
String dumpObject();
}

View file

@ -0,0 +1,120 @@
package com.itextpdf.xmp;
import com.itextpdf.xmp.impl.XMPMetaImpl;
import com.itextpdf.xmp.impl.XMPMetaParser;
import com.itextpdf.xmp.impl.XMPSchemaRegistryImpl;
import com.itextpdf.xmp.impl.XMPSerializerHelper;
import com.itextpdf.xmp.options.ParseOptions;
import com.itextpdf.xmp.options.SerializeOptions;
import java.io.InputStream;
import java.io.OutputStream;
public final class XMPMetaFactory {
private static XMPSchemaRegistry schema = new XMPSchemaRegistryImpl();
private static XMPVersionInfo versionInfo = null;
public static XMPSchemaRegistry getSchemaRegistry() {
return schema;
}
public static XMPMeta create() {
return new XMPMetaImpl();
}
public static XMPMeta parse(InputStream in) throws XMPException {
return parse(in, null);
}
public static XMPMeta parse(InputStream in, ParseOptions options) throws XMPException {
return XMPMetaParser.parse(in, options);
}
public static XMPMeta parseFromString(String packet) throws XMPException {
return parseFromString(packet, null);
}
public static XMPMeta parseFromString(String packet, ParseOptions options) throws XMPException {
return XMPMetaParser.parse(packet, options);
}
public static XMPMeta parseFromBuffer(byte[] buffer) throws XMPException {
return parseFromBuffer(buffer, null);
}
public static XMPMeta parseFromBuffer(byte[] buffer, ParseOptions options) throws XMPException {
return XMPMetaParser.parse(buffer, options);
}
public static void serialize(XMPMeta xmp, OutputStream out) throws XMPException {
serialize(xmp, out, null);
}
public static void serialize(XMPMeta xmp, OutputStream out, SerializeOptions options) throws XMPException {
assertImplementation(xmp);
XMPSerializerHelper.serialize((XMPMetaImpl)xmp, out, options);
}
public static byte[] serializeToBuffer(XMPMeta xmp, SerializeOptions options) throws XMPException {
assertImplementation(xmp);
return XMPSerializerHelper.serializeToBuffer((XMPMetaImpl)xmp, options);
}
public static String serializeToString(XMPMeta xmp, SerializeOptions options) throws XMPException {
assertImplementation(xmp);
return XMPSerializerHelper.serializeToString((XMPMetaImpl)xmp, options);
}
private static void assertImplementation(XMPMeta xmp) {
if (!(xmp instanceof XMPMetaImpl))
throw new UnsupportedOperationException("The serializing service works onlywith the XMPMeta implementation of this library");
}
public static void reset() {
schema = new XMPSchemaRegistryImpl();
}
public static synchronized XMPVersionInfo getVersionInfo() {
if (versionInfo == null)
try {
int major = 5;
int minor = 1;
int micro = 0;
int engBuild = 3;
boolean debug = false;
String message = "Adobe XMP Core 5.1.0-jc003";
versionInfo = new XMPVersionInfo() {
public int getMajor() {
return 5;
}
public int getMinor() {
return 1;
}
public int getMicro() {
return 0;
}
public boolean isDebug() {
return false;
}
public int getBuild() {
return 3;
}
public String getMessage() {
return "Adobe XMP Core 5.1.0-jc003";
}
public String toString() {
return "Adobe XMP Core 5.1.0-jc003";
}
};
} catch (Throwable e) {
System.out.println(e);
}
return versionInfo;
}
}

View file

@ -0,0 +1,64 @@
package com.itextpdf.xmp;
import com.itextpdf.xmp.impl.Utils;
import com.itextpdf.xmp.impl.xpath.XMPPath;
import com.itextpdf.xmp.impl.xpath.XMPPathParser;
public final class XMPPathFactory {
public static String composeArrayItemPath(String arrayName, int itemIndex) throws XMPException {
if (itemIndex > 0)
return arrayName + '[' + itemIndex + ']';
if (itemIndex == -1)
return arrayName + "[last()]";
throw new XMPException("Array index must be larger than zero", 104);
}
public static String composeStructFieldPath(String fieldNS, String fieldName) throws XMPException {
assertFieldNS(fieldNS);
assertFieldName(fieldName);
XMPPath fieldPath = XMPPathParser.expandXPath(fieldNS, fieldName);
if (fieldPath.size() != 2)
throw new XMPException("The field name must be simple", 102);
return '/' + fieldPath.getSegment(1).getName();
}
public static String composeQualifierPath(String qualNS, String qualName) throws XMPException {
assertQualNS(qualNS);
assertQualName(qualName);
XMPPath qualPath = XMPPathParser.expandXPath(qualNS, qualName);
if (qualPath.size() != 2)
throw new XMPException("The qualifier name must be simple", 102);
return "/?" + qualPath.getSegment(1).getName();
}
public static String composeLangSelector(String arrayName, String langName) {
return arrayName + "[?xml:lang=\"" + Utils.normalizeLangValue(langName) + "\"]";
}
public static String composeFieldSelector(String arrayName, String fieldNS, String fieldName, String fieldValue) throws XMPException {
XMPPath fieldPath = XMPPathParser.expandXPath(fieldNS, fieldName);
if (fieldPath.size() != 2)
throw new XMPException("The fieldName name must be simple", 102);
return arrayName + '[' + fieldPath.getSegment(1).getName() + "=\"" + fieldValue + "\"]";
}
private static void assertQualNS(String qualNS) throws XMPException {
if (qualNS == null || qualNS.length() == 0)
throw new XMPException("Empty qualifier namespace URI", 101);
}
private static void assertQualName(String qualName) throws XMPException {
if (qualName == null || qualName.length() == 0)
throw new XMPException("Empty qualifier name", 102);
}
private static void assertFieldNS(String fieldNS) throws XMPException {
if (fieldNS == null || fieldNS.length() == 0)
throw new XMPException("Empty field namespace URI", 101);
}
private static void assertFieldName(String fieldName) throws XMPException {
if (fieldName == null || fieldName.length() == 0)
throw new XMPException("Empty f name", 102);
}
}

View file

@ -0,0 +1,26 @@
package com.itextpdf.xmp;
import com.itextpdf.xmp.properties.XMPAliasInfo;
import java.util.Map;
public interface XMPSchemaRegistry {
String registerNamespace(String paramString1, String paramString2) throws XMPException;
String getNamespacePrefix(String paramString);
String getNamespaceURI(String paramString);
Map getNamespaces();
Map getPrefixes();
void deleteNamespace(String paramString);
XMPAliasInfo resolveAlias(String paramString1, String paramString2);
XMPAliasInfo[] findAliases(String paramString);
XMPAliasInfo findAlias(String paramString);
Map getAliases();
}

View file

@ -0,0 +1,115 @@
package com.itextpdf.xmp;
import com.itextpdf.xmp.impl.Base64;
import com.itextpdf.xmp.impl.ISO8601Converter;
import com.itextpdf.xmp.impl.XMPUtilsImpl;
import com.itextpdf.xmp.options.PropertyOptions;
public class XMPUtils {
public static String catenateArrayItems(XMPMeta xmp, String schemaNS, String arrayName, String separator, String quotes, boolean allowCommas) throws XMPException {
return XMPUtilsImpl.catenateArrayItems(xmp, schemaNS, arrayName, separator, quotes, allowCommas);
}
public static void separateArrayItems(XMPMeta xmp, String schemaNS, String arrayName, String catedStr, PropertyOptions arrayOptions, boolean preserveCommas) throws XMPException {
XMPUtilsImpl.separateArrayItems(xmp, schemaNS, arrayName, catedStr, arrayOptions, preserveCommas);
}
public static void removeProperties(XMPMeta xmp, String schemaNS, String propName, boolean doAllProperties, boolean includeAliases) throws XMPException {
XMPUtilsImpl.removeProperties(xmp, schemaNS, propName, doAllProperties, includeAliases);
}
public static void appendProperties(XMPMeta source, XMPMeta dest, boolean doAllProperties, boolean replaceOldValues) throws XMPException {
appendProperties(source, dest, doAllProperties, replaceOldValues, false);
}
public static void appendProperties(XMPMeta source, XMPMeta dest, boolean doAllProperties, boolean replaceOldValues, boolean deleteEmptyValues) throws XMPException {
XMPUtilsImpl.appendProperties(source, dest, doAllProperties, replaceOldValues, deleteEmptyValues);
}
public static boolean convertToBoolean(String value) throws XMPException {
if (value == null || value.length() == 0)
throw new XMPException("Empty convert-string", 5);
value = value.toLowerCase();
try {
return (Integer.parseInt(value) != 0);
} catch (NumberFormatException e) {
return ("true"
.equals(value) || "t"
.equals(value) || "on"
.equals(value) || "yes"
.equals(value));
}
}
public static String convertFromBoolean(boolean value) {
return value ? "True" : "False";
}
public static int convertToInteger(String rawValue) throws XMPException {
try {
if (rawValue == null || rawValue.length() == 0)
throw new XMPException("Empty convert-string", 5);
if (rawValue.startsWith("0x"))
return Integer.parseInt(rawValue.substring(2), 16);
return Integer.parseInt(rawValue);
} catch (NumberFormatException e) {
throw new XMPException("Invalid integer string", 5);
}
}
public static String convertFromInteger(int value) {
return String.valueOf(value);
}
public static long convertToLong(String rawValue) throws XMPException {
try {
if (rawValue == null || rawValue.length() == 0)
throw new XMPException("Empty convert-string", 5);
if (rawValue.startsWith("0x"))
return Long.parseLong(rawValue.substring(2), 16);
return Long.parseLong(rawValue);
} catch (NumberFormatException e) {
throw new XMPException("Invalid long string", 5);
}
}
public static String convertFromLong(long value) {
return String.valueOf(value);
}
public static double convertToDouble(String rawValue) throws XMPException {
try {
if (rawValue == null || rawValue.length() == 0)
throw new XMPException("Empty convert-string", 5);
return Double.parseDouble(rawValue);
} catch (NumberFormatException e) {
throw new XMPException("Invalid double string", 5);
}
}
public static String convertFromDouble(double value) {
return String.valueOf(value);
}
public static XMPDateTime convertToDate(String rawValue) throws XMPException {
if (rawValue == null || rawValue.length() == 0)
throw new XMPException("Empty convert-string", 5);
return ISO8601Converter.parse(rawValue);
}
public static String convertFromDate(XMPDateTime value) {
return ISO8601Converter.render(value);
}
public static String encodeBase64(byte[] buffer) {
return new String(Base64.encode(buffer));
}
public static byte[] decodeBase64(String base64String) throws XMPException {
try {
return Base64.decode(base64String.getBytes());
} catch (Throwable e) {
throw new XMPException("Invalid base64 string", 5, e);
}
}
}

View file

@ -0,0 +1,15 @@
package com.itextpdf.xmp;
public interface XMPVersionInfo {
int getMajor();
int getMinor();
int getMicro();
int getBuild();
boolean isDebug();
String getMessage();
}

View file

@ -0,0 +1,120 @@
package com.itextpdf.xmp.impl;
public class Base64 {
private static final byte INVALID = -1;
private static final byte WHITESPACE = -2;
private static final byte EQUAL = -3;
private static byte[] base64 = new byte[] {
65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
85, 86, 87, 88, 89, 90, 97, 98, 99, 100,
101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
121, 122, 48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 43, 47 };
private static byte[] ascii = new byte[255];
static {
for (int i = 0; i < 255; i++)
ascii[i] = -1;
for (int idx = 0; idx < base64.length; idx++)
ascii[base64[idx]] = (byte)idx;
ascii[9] = -2;
ascii[10] = -2;
ascii[13] = -2;
ascii[32] = -2;
ascii[61] = -3;
}
public static final byte[] encode(byte[] src) {
return encode(src, 0);
}
public static final byte[] encode(byte[] src, int lineFeed) {
lineFeed = lineFeed / 4 * 4;
if (lineFeed < 0)
lineFeed = 0;
int codeLength = (src.length + 2) / 3 * 4;
if (lineFeed > 0)
codeLength += (codeLength - 1) / lineFeed;
byte[] dst = new byte[codeLength];
int didx = 0;
int sidx = 0;
int lf = 0;
while (sidx + 3 <= src.length) {
int bits24 = (src[sidx++] & 0xFF) << 16;
bits24 |= (src[sidx++] & 0xFF) << 8;
bits24 |= (src[sidx++] & 0xFF) << 0;
int bits6 = (bits24 & 0xFC0000) >> 18;
dst[didx++] = base64[bits6];
bits6 = (bits24 & 0x3F000) >> 12;
dst[didx++] = base64[bits6];
bits6 = (bits24 & 0xFC0) >> 6;
dst[didx++] = base64[bits6];
bits6 = bits24 & 0x3F;
dst[didx++] = base64[bits6];
lf += 4;
if (didx < codeLength && lineFeed > 0 && lf % lineFeed == 0)
dst[didx++] = 10;
}
if (src.length - sidx == 2) {
int bits24 = (src[sidx] & 0xFF) << 16;
bits24 |= (src[sidx + 1] & 0xFF) << 8;
int bits6 = (bits24 & 0xFC0000) >> 18;
dst[didx++] = base64[bits6];
bits6 = (bits24 & 0x3F000) >> 12;
dst[didx++] = base64[bits6];
bits6 = (bits24 & 0xFC0) >> 6;
dst[didx++] = base64[bits6];
dst[didx++] = 61;
} else if (src.length - sidx == 1) {
int bits24 = (src[sidx] & 0xFF) << 16;
int bits6 = (bits24 & 0xFC0000) >> 18;
dst[didx++] = base64[bits6];
bits6 = (bits24 & 0x3F000) >> 12;
dst[didx++] = base64[bits6];
dst[didx++] = 61;
dst[didx++] = 61;
}
return dst;
}
public static final String encode(String src) {
return new String(encode(src.getBytes()));
}
public static final byte[] decode(byte[] src) throws IllegalArgumentException {
int srcLen = 0;
int sidx;
for (sidx = 0; sidx < src.length; sidx++) {
byte val = ascii[src[sidx]];
if (val >= 0) {
src[srcLen++] = val;
} else if (val == -1) {
throw new IllegalArgumentException("Invalid base 64 string");
}
}
while (srcLen > 0 && src[srcLen - 1] == -3)
srcLen--;
byte[] dst = new byte[srcLen * 3 / 4];
int didx;
for (sidx = 0, didx = 0; didx < dst.length - 2; sidx += 4, didx += 3) {
dst[didx] = (byte)(src[sidx] << 2 & 0xFF | src[sidx + 1] >>> 4 & 0x3);
dst[didx + 1] = (byte)(src[sidx + 1] << 4 & 0xFF | src[sidx + 2] >>> 2 & 0xF);
dst[didx + 2] = (byte)(src[sidx + 2] << 6 & 0xFF | src[sidx + 3] & 0x3F);
}
if (didx < dst.length)
dst[didx] = (byte)(src[sidx] << 2 & 0xFF | src[sidx + 1] >>> 4 & 0x3);
if (++didx < dst.length)
dst[didx] = (byte)(src[sidx + 1] << 4 & 0xFF | src[sidx + 2] >>> 2 & 0xF);
return dst;
}
public static final String decode(String src) {
return new String(decode(src.getBytes()));
}
}

View file

@ -0,0 +1,132 @@
package com.itextpdf.xmp.impl;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
public class ByteBuffer {
private byte[] buffer;
private int length;
private String encoding = null;
public ByteBuffer(int initialCapacity) {
this.buffer = new byte[initialCapacity];
this.length = 0;
}
public ByteBuffer(byte[] buffer) {
this.buffer = buffer;
this.length = buffer.length;
}
public ByteBuffer(byte[] buffer, int length) {
if (length > buffer.length)
throw new ArrayIndexOutOfBoundsException("Valid length exceeds the buffer length.");
this.buffer = buffer;
this.length = length;
}
public ByteBuffer(InputStream in) throws IOException {
int chunk = 16384;
this.length = 0;
this.buffer = new byte[chunk];
int read;
while ((read = in.read(this.buffer, this.length, chunk)) > 0) {
this.length += read;
if (read == chunk) {
ensureCapacity(this.length + chunk);
continue;
}
break;
}
}
public ByteBuffer(byte[] buffer, int offset, int length) {
if (length > buffer.length - offset)
throw new ArrayIndexOutOfBoundsException("Valid length exceeds the buffer length.");
this.buffer = new byte[length];
System.arraycopy(buffer, offset, this.buffer, 0, length);
this.length = length;
}
public InputStream getByteStream() {
return new ByteArrayInputStream(this.buffer, 0, this.length);
}
public int length() {
return this.length;
}
public byte byteAt(int index) {
if (index < this.length)
return this.buffer[index];
throw new IndexOutOfBoundsException("The index exceeds the valid buffer area");
}
public int charAt(int index) {
if (index < this.length)
return this.buffer[index] & 0xFF;
throw new IndexOutOfBoundsException("The index exceeds the valid buffer area");
}
public void append(byte b) {
ensureCapacity(this.length + 1);
this.buffer[this.length++] = b;
}
public void append(byte[] bytes, int offset, int len) {
ensureCapacity(this.length + len);
System.arraycopy(bytes, offset, this.buffer, this.length, len);
this.length += len;
}
public void append(byte[] bytes) {
append(bytes, 0, bytes.length);
}
public void append(ByteBuffer anotherBuffer) {
append(anotherBuffer.buffer, 0, anotherBuffer.length);
}
public String getEncoding() {
if (this.encoding == null)
if (this.length < 2) {
this.encoding = "UTF-8";
} else if (this.buffer[0] == 0) {
if (this.length < 4 || this.buffer[1] != 0) {
this.encoding = "UTF-16BE";
} else if ((this.buffer[2] & 0xFF) == 254 && (this.buffer[3] & 0xFF) == 255) {
this.encoding = "UTF-32BE";
} else {
this.encoding = "UTF-32";
}
} else if ((this.buffer[0] & 0xFF) < 128) {
if (this.buffer[1] != 0) {
this.encoding = "UTF-8";
} else if (this.length < 4 || this.buffer[2] != 0) {
this.encoding = "UTF-16LE";
} else {
this.encoding = "UTF-32LE";
}
} else if ((this.buffer[0] & 0xFF) == 239) {
this.encoding = "UTF-8";
} else if ((this.buffer[0] & 0xFF) == 254) {
this.encoding = "UTF-16";
} else if (this.length < 4 || this.buffer[2] != 0) {
this.encoding = "UTF-16";
} else {
this.encoding = "UTF-32";
}
return this.encoding;
}
private void ensureCapacity(int requestedLength) {
if (requestedLength > this.buffer.length) {
byte[] oldBuf = this.buffer;
this.buffer = new byte[oldBuf.length * 2];
System.arraycopy(oldBuf, 0, this.buffer, 0, oldBuf.length);
}
}
}

View file

@ -0,0 +1,33 @@
package com.itextpdf.xmp.impl;
import java.io.IOException;
import java.io.OutputStream;
public final class CountOutputStream extends OutputStream {
private final OutputStream out;
private int bytesWritten = 0;
CountOutputStream(OutputStream out) {
this.out = out;
}
public void write(byte[] buf, int off, int len) throws IOException {
this.out.write(buf, off, len);
this.bytesWritten += len;
}
public void write(byte[] buf) throws IOException {
this.out.write(buf);
this.bytesWritten += buf.length;
}
public void write(int b) throws IOException {
this.out.write(b);
this.bytesWritten++;
}
public int getBytesWritten() {
return this.bytesWritten;
}
}

View file

@ -0,0 +1,134 @@
package com.itextpdf.xmp.impl;
import java.io.IOException;
import java.io.PushbackReader;
import java.io.Reader;
public class FixASCIIControlsReader extends PushbackReader {
private static final int STATE_START = 0;
private static final int STATE_AMP = 1;
private static final int STATE_HASH = 2;
private static final int STATE_HEX = 3;
private static final int STATE_DIG1 = 4;
private static final int STATE_ERROR = 5;
private static final int BUFFER_SIZE = 8;
private int state = 0;
private int control = 0;
private int digits = 0;
public FixASCIIControlsReader(Reader in) {
super(in, 8);
}
public int read(char[] cbuf, int off, int len) throws IOException {
int readAhead = 0;
int read = 0;
int pos = off;
char[] readAheadBuffer = new char[8];
boolean available = true;
while (available && read < len) {
available = (super.read(readAheadBuffer, readAhead, 1) == 1);
if (available) {
char c = processChar(readAheadBuffer[readAhead]);
if (this.state == 0) {
if (Utils.isControlChar(c))
c = ' ';
cbuf[pos++] = c;
readAhead = 0;
read++;
continue;
}
if (this.state == 5) {
unread(readAheadBuffer, 0, readAhead + 1);
readAhead = 0;
continue;
}
readAhead++;
continue;
}
if (readAhead > 0) {
unread(readAheadBuffer, 0, readAhead);
this.state = 5;
readAhead = 0;
available = true;
}
}
return (read > 0 || available) ? read : -1;
}
private char processChar(char ch) {
switch (this.state) {
case 0:
if (ch == '&')
this.state = 1;
return ch;
case 1:
if (ch == '#') {
this.state = 2;
} else {
this.state = 5;
}
return ch;
case 2:
if (ch == 'x') {
this.control = 0;
this.digits = 0;
this.state = 3;
} else if ('0' <= ch && ch <= '9') {
this.control = Character.digit(ch, 10);
this.digits = 1;
this.state = 4;
} else {
this.state = 5;
}
return ch;
case 4:
if ('0' <= ch && ch <= '9') {
this.control = this.control * 10 + Character.digit(ch, 10);
this.digits++;
if (this.digits <= 5) {
this.state = 4;
} else {
this.state = 5;
}
} else {
if (ch == ';' && Utils.isControlChar((char)this.control)) {
this.state = 0;
return (char)this.control;
}
this.state = 5;
}
return ch;
case 3:
if (('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')) {
this.control = this.control * 16 + Character.digit(ch, 16);
this.digits++;
if (this.digits <= 4) {
this.state = 3;
} else {
this.state = 5;
}
} else {
if (ch == ';' && Utils.isControlChar((char)this.control)) {
this.state = 0;
return (char)this.control;
}
this.state = 5;
}
return ch;
case 5:
this.state = 0;
return ch;
}
return ch;
}
}

View file

@ -0,0 +1,157 @@
package com.itextpdf.xmp.impl;
import com.itextpdf.xmp.XMPDateTime;
import com.itextpdf.xmp.XMPException;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import java.util.SimpleTimeZone;
public final class ISO8601Converter {
public static XMPDateTime parse(String iso8601String) throws XMPException {
return parse(iso8601String, new XMPDateTimeImpl());
}
public static XMPDateTime parse(String iso8601String, XMPDateTime binValue) throws XMPException {
if (iso8601String == null)
throw new XMPException("Parameter must not be null", 4);
if (iso8601String.length() == 0)
return binValue;
ParseState input = new ParseState(iso8601String);
if (input.ch(0) == '-')
input.skip();
int value = input.gatherInt("Invalid year in date string", 9999);
if (input.hasNext() && input.ch() != '-')
throw new XMPException("Invalid date string, after year", 5);
if (input.ch(0) == '-')
value = -value;
binValue.setYear(value);
if (!input.hasNext())
return binValue;
input.skip();
value = input.gatherInt("Invalid month in date string", 12);
if (input.hasNext() && input.ch() != '-')
throw new XMPException("Invalid date string, after month", 5);
binValue.setMonth(value);
if (!input.hasNext())
return binValue;
input.skip();
value = input.gatherInt("Invalid day in date string", 31);
if (input.hasNext() && input.ch() != 'T')
throw new XMPException("Invalid date string, after day", 5);
binValue.setDay(value);
if (!input.hasNext())
return binValue;
input.skip();
value = input.gatherInt("Invalid hour in date string", 23);
binValue.setHour(value);
if (!input.hasNext())
return binValue;
if (input.ch() == ':') {
input.skip();
value = input.gatherInt("Invalid minute in date string", 59);
if (input.hasNext() &&
input.ch() != ':' && input.ch() != 'Z' && input.ch() != '+' && input.ch() != '-')
throw new XMPException("Invalid date string, after minute", 5);
binValue.setMinute(value);
}
if (!input.hasNext())
return binValue;
if (input.hasNext() && input.ch() == ':') {
input.skip();
value = input.gatherInt("Invalid whole seconds in date string", 59);
if (input.hasNext() && input.ch() != '.' && input.ch() != 'Z' &&
input.ch() != '+' && input.ch() != '-')
throw new XMPException("Invalid date string, after whole seconds", 5);
binValue.setSecond(value);
if (input.ch() == '.') {
input.skip();
int digits = input.pos();
value = input.gatherInt("Invalid fractional seconds in date string", 999999999);
if (input.hasNext() &&
input.ch() != 'Z' && input.ch() != '+' && input.ch() != '-')
throw new XMPException("Invalid date string, after fractional second", 5);
digits = input.pos() - digits;
for (; digits > 9; digits--)
value /= 10;
for (; digits < 9; digits++)
value *= 10;
binValue.setNanoSecond(value);
}
} else if (input.ch() != 'Z' && input.ch() != '+' && input.ch() != '-') {
throw new XMPException("Invalid date string, after time", 5);
}
int tzSign = 0;
int tzHour = 0;
int tzMinute = 0;
if (!input.hasNext())
return binValue;
if (input.ch() == 'Z') {
input.skip();
} else if (input.hasNext()) {
if (input.ch() == '+') {
tzSign = 1;
} else if (input.ch() == '-') {
tzSign = -1;
} else {
throw new XMPException("Time zone must begin with 'Z', '+', or '-'", 5);
}
input.skip();
tzHour = input.gatherInt("Invalid time zone hour in date string", 23);
if (input.hasNext())
if (input.ch() == ':') {
input.skip();
tzMinute = input.gatherInt("Invalid time zone minute in date string", 59);
} else {
throw new XMPException("Invalid date string, after time zone hour", 5);
}
}
int offset = (tzHour * 3600 * 1000 + tzMinute * 60 * 1000) * tzSign;
binValue.setTimeZone(new SimpleTimeZone(offset, ""));
if (input.hasNext())
throw new XMPException("Invalid date string, extra chars at end", 5);
return binValue;
}
public static String render(XMPDateTime dateTime) {
StringBuffer buffer = new StringBuffer();
if (dateTime.hasDate()) {
DecimalFormat df = new DecimalFormat("0000", new DecimalFormatSymbols(Locale.ENGLISH));
buffer.append(df.format((long)dateTime.getYear()));
if (dateTime.getMonth() == 0)
return buffer.toString();
df.applyPattern("'-'00");
buffer.append(df.format((long)dateTime.getMonth()));
if (dateTime.getDay() == 0)
return buffer.toString();
buffer.append(df.format((long)dateTime.getDay()));
if (dateTime.hasTime()) {
buffer.append('T');
df.applyPattern("00");
buffer.append(df.format((long)dateTime.getHour()));
buffer.append(':');
buffer.append(df.format((long)dateTime.getMinute()));
if (dateTime.getSecond() != 0 || dateTime.getNanoSecond() != 0) {
double seconds = (double)dateTime.getSecond() + (double)dateTime.getNanoSecond() / 1.0E9D;
df.applyPattern(":00.#########");
buffer.append(df.format(seconds));
}
if (dateTime.hasTimeZone()) {
long timeInMillis = dateTime.getCalendar().getTimeInMillis();
int offset = dateTime.getTimeZone().getOffset(timeInMillis);
if (offset == 0) {
buffer.append('Z');
} else {
int thours = offset / 3600000;
int tminutes = Math.abs(offset % 3600000 / 60000);
df.applyPattern("+00;-00");
buffer.append(df.format((long)thours));
df.applyPattern(":00");
buffer.append(df.format((long)tminutes));
}
}
}
}
return buffer.toString();
}
}

View file

@ -0,0 +1,76 @@
package com.itextpdf.xmp.impl;
import java.io.UnsupportedEncodingException;
public class Latin1Converter {
private static final int STATE_START = 0;
private static final int STATE_UTF8CHAR = 11;
public static ByteBuffer convert(ByteBuffer buffer) {
if ("UTF-8".equals(buffer.getEncoding())) {
byte[] readAheadBuffer = new byte[8];
int readAhead = 0;
int expectedBytes = 0;
ByteBuffer out = new ByteBuffer(buffer.length() * 4 / 3);
int state = 0;
for (int i = 0; i < buffer.length(); i++) {
int b = buffer.charAt(i);
switch (state) {
default:
if (b < 127) {
out.append((byte)b);
} else if (b >= 192) {
expectedBytes = -1;
int test = b;
for (; expectedBytes < 8 && (test & 0x80) == 128; test <<= 1)
expectedBytes++;
readAheadBuffer[readAhead++] = (byte)b;
state = 11;
} else {
byte[] utf8 = convertToUTF8((byte)b);
out.append(utf8);
}
break;
case 11:
if (expectedBytes > 0 && (b & 0xC0) == 128) {
readAheadBuffer[readAhead++] = (byte)b;
expectedBytes--;
if (expectedBytes == 0) {
out.append(readAheadBuffer, 0, readAhead);
readAhead = 0;
state = 0;
}
} else {
byte[] utf8 = convertToUTF8(readAheadBuffer[0]);
out.append(utf8);
i -= readAhead;
readAhead = 0;
state = 0;
}
break;
}
}
if (state == 11)
for (int j = 0; j < readAhead; j++) {
byte b = readAheadBuffer[j];
byte[] utf8 = convertToUTF8(b);
out.append(utf8);
}
return out;
}
return buffer;
}
private static byte[] convertToUTF8(byte ch) {
int c = ch & 0xFF;
try {
if (c >= 128) {
if (c == 129 || c == 141 || c == 143 || c == 144 || c == 157)
return new byte[] { 32 };
return new String(new byte[] { (byte)ch }, "cp1252").getBytes("UTF-8");
}
} catch (UnsupportedEncodingException e) {}
return new byte[] { ch };
}
}

View file

@ -0,0 +1,51 @@
package com.itextpdf.xmp.impl;
import com.itextpdf.xmp.XMPConst;
import com.itextpdf.xmp.XMPException;
import com.itextpdf.xmp.XMPMeta;
class ParameterAsserts implements XMPConst {
public static void assertArrayName(String arrayName) throws XMPException {
if (arrayName == null || arrayName.length() == 0)
throw new XMPException("Empty array name", 4);
}
public static void assertPropName(String propName) throws XMPException {
if (propName == null || propName.length() == 0)
throw new XMPException("Empty property name", 4);
}
public static void assertSchemaNS(String schemaNS) throws XMPException {
if (schemaNS == null || schemaNS.length() == 0)
throw new XMPException("Empty schema namespace URI", 4);
}
public static void assertPrefix(String prefix) throws XMPException {
if (prefix == null || prefix.length() == 0)
throw new XMPException("Empty prefix", 4);
}
public static void assertSpecificLang(String specificLang) throws XMPException {
if (specificLang == null || specificLang.length() == 0)
throw new XMPException("Empty specific language", 4);
}
public static void assertStructName(String structName) throws XMPException {
if (structName == null || structName.length() == 0)
throw new XMPException("Empty array name", 4);
}
public static void assertNotNull(Object param) throws XMPException {
if (param == null)
throw new XMPException("Parameter must not be null", 4);
if (param instanceof String && ((String)param).length() == 0)
throw new XMPException("Parameter must not be null or empty", 4);
}
public static void assertImplementation(XMPMeta xmp) throws XMPException {
if (xmp == null)
throw new XMPException("Parameter must not be null", 4);
if (!(xmp instanceof XMPMetaImpl))
throw new XMPException("The XMPMeta-object is not compatible with this implementation", 4);
}
}

View file

@ -0,0 +1,551 @@
package com.itextpdf.xmp.impl;
import com.itextpdf.xmp.XMPConst;
import com.itextpdf.xmp.XMPError;
import com.itextpdf.xmp.XMPException;
import com.itextpdf.xmp.XMPMetaFactory;
import com.itextpdf.xmp.XMPSchemaRegistry;
import com.itextpdf.xmp.options.PropertyOptions;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.w3c.dom.Attr;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
public class ParseRDF implements XMPError, XMPConst {
public static final int RDFTERM_OTHER = 0;
public static final int RDFTERM_RDF = 1;
public static final int RDFTERM_ID = 2;
public static final int RDFTERM_ABOUT = 3;
public static final int RDFTERM_PARSE_TYPE = 4;
public static final int RDFTERM_RESOURCE = 5;
public static final int RDFTERM_NODE_ID = 6;
public static final int RDFTERM_DATATYPE = 7;
public static final int RDFTERM_DESCRIPTION = 8;
public static final int RDFTERM_LI = 9;
public static final int RDFTERM_ABOUT_EACH = 10;
public static final int RDFTERM_ABOUT_EACH_PREFIX = 11;
public static final int RDFTERM_BAG_ID = 12;
public static final int RDFTERM_FIRST_CORE = 1;
public static final int RDFTERM_LAST_CORE = 7;
public static final int RDFTERM_FIRST_SYNTAX = 1;
public static final int RDFTERM_LAST_SYNTAX = 9;
public static final int RDFTERM_FIRST_OLD = 10;
public static final int RDFTERM_LAST_OLD = 12;
public static final String DEFAULT_PREFIX = "_dflt";
static XMPMetaImpl parse(Node xmlRoot) throws XMPException {
XMPMetaImpl xmp = new XMPMetaImpl();
rdf_RDF(xmp, xmlRoot);
return xmp;
}
static void rdf_RDF(XMPMetaImpl xmp, Node rdfRdfNode) throws XMPException {
if (rdfRdfNode.hasAttributes()) {
rdf_NodeElementList(xmp, xmp.getRoot(), rdfRdfNode);
} else {
throw new XMPException("Invalid attributes of rdf:RDF element", 202);
}
}
private static void rdf_NodeElementList(XMPMetaImpl xmp, XMPNode xmpParent, Node rdfRdfNode) throws XMPException {
for (int i = 0; i < rdfRdfNode.getChildNodes().getLength(); i++) {
Node child = rdfRdfNode.getChildNodes().item(i);
if (!isWhitespaceNode(child))
rdf_NodeElement(xmp, xmpParent, child, true);
}
}
private static void rdf_NodeElement(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode, boolean isTopLevel) throws XMPException {
int nodeTerm = getRDFTermKind(xmlNode);
if (nodeTerm != 8 && nodeTerm != 0)
throw new XMPException("Node element must be rdf:Description or typed node", 202);
if (isTopLevel && nodeTerm == 0)
throw new XMPException("Top level typed node not allowed", 203);
rdf_NodeElementAttrs(xmp, xmpParent, xmlNode, isTopLevel);
rdf_PropertyElementList(xmp, xmpParent, xmlNode, isTopLevel);
}
private static void rdf_NodeElementAttrs(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode, boolean isTopLevel) throws XMPException {
int exclusiveAttrs = 0;
for (int i = 0; i < xmlNode.getAttributes().getLength(); i++) {
Node attribute = xmlNode.getAttributes().item(i);
if (!"xmlns".equals(attribute.getPrefix()) && (
attribute.getPrefix() != null || !"xmlns".equals(attribute.getNodeName()))) {
int attrTerm = getRDFTermKind(attribute);
switch (attrTerm) {
case 2:
case 3:
case 6:
if (exclusiveAttrs > 0)
throw new XMPException("Mutally exclusive about, ID, nodeID attributes", 202);
exclusiveAttrs++;
if (isTopLevel && attrTerm == 3) {
if (xmpParent.getName() != null && xmpParent.getName().length() > 0) {
if (!xmpParent.getName().equals(attribute.getNodeValue()))
throw new XMPException("Mismatched top level rdf:about values", 203);
break;
}
xmpParent.setName(attribute.getNodeValue());
}
break;
case 0:
addChildNode(xmp, xmpParent, attribute, attribute.getNodeValue(), isTopLevel);
break;
default:
throw new XMPException("Invalid nodeElement attribute", 202);
}
}
}
}
private static void rdf_PropertyElementList(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlParent, boolean isTopLevel) throws XMPException {
for (int i = 0; i < xmlParent.getChildNodes().getLength(); i++) {
Node currChild = xmlParent.getChildNodes().item(i);
if (!isWhitespaceNode(currChild)) {
if (currChild.getNodeType() != 1)
throw new XMPException("Expected property element node not found", 202);
rdf_PropertyElement(xmp, xmpParent, currChild, isTopLevel);
}
}
}
private static void rdf_PropertyElement(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode, boolean isTopLevel) throws XMPException {
int nodeTerm = getRDFTermKind(xmlNode);
if (!isPropertyElementName(nodeTerm))
throw new XMPException("Invalid property element name", 202);
NamedNodeMap attributes = xmlNode.getAttributes();
List<String> nsAttrs = null;
for (int i = 0; i < attributes.getLength(); i++) {
Node attribute = attributes.item(i);
if ("xmlns".equals(attribute.getPrefix()) || (
attribute.getPrefix() == null && "xmlns".equals(attribute.getNodeName()))) {
if (nsAttrs == null)
nsAttrs = new ArrayList();
nsAttrs.add(attribute.getNodeName());
}
}
if (nsAttrs != null)
for (String ns : nsAttrs)
attributes.removeNamedItem(ns);
if (attributes.getLength() > 3) {
rdf_EmptyPropertyElement(xmp, xmpParent, xmlNode, isTopLevel);
} else {
for (int j = 0; j < attributes.getLength(); j++) {
Node attribute = attributes.item(j);
String attrLocal = attribute.getLocalName();
String attrNS = attribute.getNamespaceURI();
String attrValue = attribute.getNodeValue();
if (!"xml:lang".equals(attribute.getNodeName()) || ("ID"
.equals(attrLocal) && "http://www.w3.org/1999/02/22-rdf-syntax-ns#".equals(attrNS))) {
if ("datatype".equals(attrLocal) && "http://www.w3.org/1999/02/22-rdf-syntax-ns#".equals(attrNS)) {
rdf_LiteralPropertyElement(xmp, xmpParent, xmlNode, isTopLevel);
} else if (!"parseType".equals(attrLocal) || !"http://www.w3.org/1999/02/22-rdf-syntax-ns#".equals(attrNS)) {
rdf_EmptyPropertyElement(xmp, xmpParent, xmlNode, isTopLevel);
} else if ("Literal".equals(attrValue)) {
rdf_ParseTypeLiteralPropertyElement();
} else if ("Resource".equals(attrValue)) {
rdf_ParseTypeResourcePropertyElement(xmp, xmpParent, xmlNode, isTopLevel);
} else if ("Collection".equals(attrValue)) {
rdf_ParseTypeCollectionPropertyElement();
} else {
rdf_ParseTypeOtherPropertyElement();
}
return;
}
}
if (xmlNode.hasChildNodes()) {
for (int k = 0; k < xmlNode.getChildNodes().getLength(); k++) {
Node currChild = xmlNode.getChildNodes().item(k);
if (currChild.getNodeType() != 3) {
rdf_ResourcePropertyElement(xmp, xmpParent, xmlNode, isTopLevel);
return;
}
}
rdf_LiteralPropertyElement(xmp, xmpParent, xmlNode, isTopLevel);
} else {
rdf_EmptyPropertyElement(xmp, xmpParent, xmlNode, isTopLevel);
}
}
}
private static void rdf_ResourcePropertyElement(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode, boolean isTopLevel) throws XMPException {
if (isTopLevel && "iX:changes".equals(xmlNode.getNodeName()))
return;
XMPNode newCompound = addChildNode(xmp, xmpParent, xmlNode, "", isTopLevel);
for (int i = 0; i < xmlNode.getAttributes().getLength(); i++) {
Node attribute = xmlNode.getAttributes().item(i);
if (!"xmlns".equals(attribute.getPrefix()) && (
attribute.getPrefix() != null || !"xmlns".equals(attribute.getNodeName()))) {
String attrLocal = attribute.getLocalName();
String attrNS = attribute.getNamespaceURI();
if ("xml:lang".equals(attribute.getNodeName())) {
addQualifierNode(newCompound, "xml:lang", attribute.getNodeValue());
} else if (!"ID".equals(attrLocal) || !"http://www.w3.org/1999/02/22-rdf-syntax-ns#".equals(attrNS)) {
throw new XMPException("Invalid attribute for resource property element", 202);
}
}
}
Node currChild = null;
boolean found = false;
for (int j = 0; j < xmlNode.getChildNodes().getLength(); j++) {
currChild = xmlNode.getChildNodes().item(j);
if (!isWhitespaceNode(currChild))
if (currChild.getNodeType() == 1 && !found) {
boolean isRDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#".equals(currChild.getNamespaceURI());
String childLocal = currChild.getLocalName();
if (isRDF && "Bag".equals(childLocal)) {
newCompound.getOptions().setArray(true);
} else if (isRDF && "Seq".equals(childLocal)) {
newCompound.getOptions().setArray(true).setArrayOrdered(true);
} else if (isRDF && "Alt".equals(childLocal)) {
newCompound.getOptions().setArray(true).setArrayOrdered(true)
.setArrayAlternate(true);
} else {
newCompound.getOptions().setStruct(true);
if (!isRDF && !"Description".equals(childLocal)) {
String typeName = currChild.getNamespaceURI();
if (typeName == null)
throw new XMPException("All XML elements must be in a namespace", 203);
typeName = typeName + ':' + childLocal;
addQualifierNode(newCompound, "rdf:type", typeName);
}
}
rdf_NodeElement(xmp, newCompound, currChild, false);
if (newCompound.getHasValueChild()) {
fixupQualifiedNode(newCompound);
} else if (newCompound.getOptions().isArrayAlternate()) {
XMPNodeUtils.detectAltText(newCompound);
}
found = true;
} else {
if (found)
throw new XMPException("Invalid child of resource property element", 202);
throw new XMPException("Children of resource property element must be XML elements", 202);
}
}
if (!found)
throw new XMPException("Missing child of resource property element", 202);
}
private static void rdf_LiteralPropertyElement(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode, boolean isTopLevel) throws XMPException {
XMPNode newChild = addChildNode(xmp, xmpParent, xmlNode, null, isTopLevel);
for (int i = 0; i < xmlNode.getAttributes().getLength(); i++) {
Node attribute = xmlNode.getAttributes().item(i);
if (!"xmlns".equals(attribute.getPrefix()) && (
attribute.getPrefix() != null || !"xmlns".equals(attribute.getNodeName()))) {
String attrNS = attribute.getNamespaceURI();
String attrLocal = attribute.getLocalName();
if ("xml:lang".equals(attribute.getNodeName())) {
addQualifierNode(newChild, "xml:lang", attribute.getNodeValue());
} else if (!"http://www.w3.org/1999/02/22-rdf-syntax-ns#".equals(attrNS) || (
!"ID".equals(attrLocal) && !"datatype".equals(attrLocal))) {
throw new XMPException("Invalid attribute for literal property element", 202);
}
}
}
String textValue = "";
for (int j = 0; j < xmlNode.getChildNodes().getLength(); j++) {
Node child = xmlNode.getChildNodes().item(j);
if (child.getNodeType() == 3) {
textValue = textValue + child.getNodeValue();
} else {
throw new XMPException("Invalid child of literal property element", 202);
}
}
newChild.setValue(textValue);
}
private static void rdf_ParseTypeLiteralPropertyElement() throws XMPException {
throw new XMPException("ParseTypeLiteral property element not allowed", 203);
}
private static void rdf_ParseTypeResourcePropertyElement(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode, boolean isTopLevel) throws XMPException {
XMPNode newStruct = addChildNode(xmp, xmpParent, xmlNode, "", isTopLevel);
newStruct.getOptions().setStruct(true);
for (int i = 0; i < xmlNode.getAttributes().getLength(); i++) {
Node attribute = xmlNode.getAttributes().item(i);
if (!"xmlns".equals(attribute.getPrefix()) && (
attribute.getPrefix() != null || !"xmlns".equals(attribute.getNodeName()))) {
String attrLocal = attribute.getLocalName();
String attrNS = attribute.getNamespaceURI();
if ("xml:lang".equals(attribute.getNodeName())) {
addQualifierNode(newStruct, "xml:lang", attribute.getNodeValue());
} else if (!"http://www.w3.org/1999/02/22-rdf-syntax-ns#".equals(attrNS) || (
!"ID".equals(attrLocal) && !"parseType".equals(attrLocal))) {
throw new XMPException("Invalid attribute for ParseTypeResource property element", 202);
}
}
}
rdf_PropertyElementList(xmp, newStruct, xmlNode, false);
if (newStruct.getHasValueChild())
fixupQualifiedNode(newStruct);
}
private static void rdf_ParseTypeCollectionPropertyElement() throws XMPException {
throw new XMPException("ParseTypeCollection property element not allowed", 203);
}
private static void rdf_ParseTypeOtherPropertyElement() throws XMPException {
throw new XMPException("ParseTypeOther property element not allowed", 203);
}
private static void rdf_EmptyPropertyElement(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode, boolean isTopLevel) throws XMPException {
boolean hasPropertyAttrs = false;
boolean hasResourceAttr = false;
boolean hasNodeIDAttr = false;
boolean hasValueAttr = false;
Node valueNode = null;
if (xmlNode.hasChildNodes())
throw new XMPException("Nested content not allowed with rdf:resource or property attributes", 202);
for (int i = 0; i < xmlNode.getAttributes().getLength(); i++) {
Node attribute = xmlNode.getAttributes().item(i);
if (!"xmlns".equals(attribute.getPrefix()) && (
attribute.getPrefix() != null || !"xmlns".equals(attribute.getNodeName()))) {
int attrTerm = getRDFTermKind(attribute);
switch (attrTerm) {
case 2:
break;
case 5:
if (hasNodeIDAttr)
throw new XMPException("Empty property element can't have both rdf:resource and rdf:nodeID", 202);
if (hasValueAttr)
throw new XMPException("Empty property element can't have both rdf:value and rdf:resource", 203);
hasResourceAttr = true;
if (!hasValueAttr)
valueNode = attribute;
break;
case 6:
if (hasResourceAttr)
throw new XMPException("Empty property element can't have both rdf:resource and rdf:nodeID", 202);
hasNodeIDAttr = true;
break;
case 0:
if ("value".equals(attribute.getLocalName()) && "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
.equals(attribute.getNamespaceURI())) {
if (hasResourceAttr)
throw new XMPException("Empty property element can't have both rdf:value and rdf:resource", 203);
hasValueAttr = true;
valueNode = attribute;
break;
}
if (!"xml:lang".equals(attribute.getNodeName()))
hasPropertyAttrs = true;
break;
default:
throw new XMPException("Unrecognized attribute of empty property element", 202);
}
}
}
XMPNode childNode = addChildNode(xmp, xmpParent, xmlNode, "", isTopLevel);
boolean childIsStruct = false;
if (hasValueAttr || hasResourceAttr) {
childNode.setValue((valueNode != null) ? valueNode.getNodeValue() : "");
if (!hasValueAttr)
childNode.getOptions().setURI(true);
} else if (hasPropertyAttrs) {
childNode.getOptions().setStruct(true);
childIsStruct = true;
}
for (int j = 0; j < xmlNode.getAttributes().getLength(); j++) {
Node attribute = xmlNode.getAttributes().item(j);
if (attribute != valueNode &&
!"xmlns".equals(attribute.getPrefix()) && (
attribute.getPrefix() != null || !"xmlns".equals(attribute.getNodeName()))) {
int attrTerm = getRDFTermKind(attribute);
switch (attrTerm) {
case 2:
case 6:
break;
case 5:
addQualifierNode(childNode, "rdf:resource", attribute.getNodeValue());
break;
case 0:
if (!childIsStruct) {
addQualifierNode(childNode,
attribute.getNodeName(), attribute.getNodeValue());
} else if ("xml:lang".equals(attribute.getNodeName())) {
addQualifierNode(childNode, "xml:lang", attribute.getNodeValue());
} else {
addChildNode(xmp, childNode, attribute, attribute.getNodeValue(), false);
}
break;
default:
throw new XMPException("Unrecognized attribute of empty property element", 202);
}
}
}
}
private static XMPNode addChildNode(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode, String value, boolean isTopLevel) throws XMPException {
String childName;
XMPSchemaRegistry registry = XMPMetaFactory.getSchemaRegistry();
String namespace = xmlNode.getNamespaceURI();
if (namespace != null) {
if ("http://purl.org/dc/1.1/".equals(namespace))
namespace = "http://purl.org/dc/elements/1.1/";
String prefix = registry.getNamespacePrefix(namespace);
if (prefix == null) {
prefix = (xmlNode.getPrefix() != null) ? xmlNode.getPrefix() : "_dflt";
prefix = registry.registerNamespace(namespace, prefix);
}
childName = prefix + xmlNode.getLocalName();
} else {
throw new XMPException("XML namespace required for all elements and attributes", 202);
}
PropertyOptions childOptions = new PropertyOptions();
boolean isAlias = false;
if (isTopLevel) {
XMPNode schemaNode = XMPNodeUtils.findSchemaNode(xmp.getRoot(), namespace, "_dflt", true);
schemaNode.setImplicit(false);
xmpParent = schemaNode;
if (registry.findAlias(childName) != null) {
isAlias = true;
xmp.getRoot().setHasAliases(true);
schemaNode.setHasAliases(true);
}
}
boolean isArrayItem = "rdf:li".equals(childName);
boolean isValueNode = "rdf:value".equals(childName);
XMPNode newChild = new XMPNode(childName, value, childOptions);
newChild.setAlias(isAlias);
if (!isValueNode) {
xmpParent.addChild(newChild);
} else {
xmpParent.addChild(1, newChild);
}
if (isValueNode) {
if (isTopLevel || !xmpParent.getOptions().isStruct())
throw new XMPException("Misplaced rdf:value element", 202);
xmpParent.setHasValueChild(true);
}
if (isArrayItem) {
if (!xmpParent.getOptions().isArray())
throw new XMPException("Misplaced rdf:li element", 202);
newChild.setName("[]");
}
return newChild;
}
private static XMPNode addQualifierNode(XMPNode xmpParent, String name, String value) throws XMPException {
boolean isLang = "xml:lang".equals(name);
XMPNode newQual = null;
newQual = new XMPNode(name, isLang ? Utils.normalizeLangValue(value) : value, null);
xmpParent.addQualifier(newQual);
return newQual;
}
private static void fixupQualifiedNode(XMPNode xmpParent) throws XMPException {
assert xmpParent.getOptions().isStruct() && xmpParent.hasChildren();
XMPNode valueNode = xmpParent.getChild(1);
assert "rdf:value".equals(valueNode.getName());
if (valueNode.getOptions().getHasLanguage()) {
if (xmpParent.getOptions().getHasLanguage())
throw new XMPException("Redundant xml:lang for rdf:value element", 203);
XMPNode langQual = valueNode.getQualifier(1);
valueNode.removeQualifier(langQual);
xmpParent.addQualifier(langQual);
}
for (int j = 1; j <= valueNode.getQualifierLength(); j++) {
XMPNode qualifier = valueNode.getQualifier(j);
xmpParent.addQualifier(qualifier);
}
for (int i = 2; i <= xmpParent.getChildrenLength(); i++) {
XMPNode qualifier = xmpParent.getChild(i);
xmpParent.addQualifier(qualifier);
}
assert xmpParent.getOptions().isStruct() || xmpParent.getHasValueChild();
xmpParent.setHasValueChild(false);
xmpParent.getOptions().setStruct(false);
xmpParent.getOptions().mergeWith(valueNode.getOptions());
xmpParent.setValue(valueNode.getValue());
xmpParent.removeChildren();
for (Iterator<XMPNode> it = valueNode.iterateChildren(); it.hasNext(); ) {
XMPNode child = it.next();
xmpParent.addChild(child);
}
}
private static boolean isWhitespaceNode(Node node) {
if (node.getNodeType() != 3)
return false;
String value = node.getNodeValue();
for (int i = 0; i < value.length(); i++) {
if (!Character.isWhitespace(value.charAt(i)))
return false;
}
return true;
}
private static boolean isPropertyElementName(int term) {
if (term == 8 || isOldTerm(term))
return false;
return !isCoreSyntaxTerm(term);
}
private static boolean isOldTerm(int term) {
return (10 <= term && term <= 12);
}
private static boolean isCoreSyntaxTerm(int term) {
return (1 <= term && term <= 7);
}
private static int getRDFTermKind(Node node) {
String localName = node.getLocalName();
String namespace = node.getNamespaceURI();
if (namespace == null && ("about"
.equals(localName) || "ID".equals(localName)) && node instanceof Attr && "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
.equals(((Attr)node).getOwnerElement().getNamespaceURI()))
namespace = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
if ("http://www.w3.org/1999/02/22-rdf-syntax-ns#".equals(namespace)) {
if ("li".equals(localName))
return 9;
if ("parseType".equals(localName))
return 4;
if ("Description".equals(localName))
return 8;
if ("about".equals(localName))
return 3;
if ("resource".equals(localName))
return 5;
if ("RDF".equals(localName))
return 1;
if ("ID".equals(localName))
return 2;
if ("nodeID".equals(localName))
return 6;
if ("datatype".equals(localName))
return 7;
if ("aboutEach".equals(localName))
return 10;
if ("aboutEachPrefix".equals(localName))
return 11;
if ("bagID".equals(localName))
return 12;
}
return 0;
}
}

View file

@ -0,0 +1,59 @@
package com.itextpdf.xmp.impl;
import com.itextpdf.xmp.XMPException;
class ParseState {
private String str;
private int pos = 0;
public ParseState(String str) {
this.str = str;
}
public int length() {
return this.str.length();
}
public boolean hasNext() {
return (this.pos < this.str.length());
}
public char ch(int index) {
return (index < this.str.length()) ?
this.str.charAt(index) : '\000';
}
public char ch() {
return (this.pos < this.str.length()) ?
this.str.charAt(this.pos) : '\000';
}
public void skip() {
this.pos++;
}
public int pos() {
return this.pos;
}
public int gatherInt(String errorMsg, int maxValue) throws XMPException {
int value = 0;
boolean success = false;
char ch = ch(this.pos);
while ('0' <= ch && ch <= '9') {
value = value * 10 + ch - 48;
success = true;
this.pos++;
ch = ch(this.pos);
}
if (success) {
if (value > maxValue)
return maxValue;
if (value < 0)
return 0;
return value;
}
throw new XMPException(errorMsg, 5);
}
}

View file

@ -0,0 +1,35 @@
package com.itextpdf.xmp.impl;
public class QName {
private String prefix;
private String localName;
public QName(String qname) {
int colon = qname.indexOf(':');
if (colon >= 0) {
this.prefix = qname.substring(0, colon);
this.localName = qname.substring(colon + 1);
} else {
this.prefix = "";
this.localName = qname;
}
}
public QName(String prefix, String localName) {
this.prefix = prefix;
this.localName = localName;
}
public boolean hasPrefix() {
return (this.prefix != null && this.prefix.length() > 0);
}
public String getLocalName() {
return this.localName;
}
public String getPrefix() {
return this.prefix;
}
}

View file

@ -0,0 +1,222 @@
package com.itextpdf.xmp.impl;
import com.itextpdf.xmp.XMPConst;
public class Utils implements XMPConst {
public static final int UUID_SEGMENT_COUNT = 4;
public static final int UUID_LENGTH = 36;
private static boolean[] xmlNameStartChars;
private static boolean[] xmlNameChars;
static {
initCharTables();
}
public static String normalizeLangValue(String value) {
if ("x-default".equals(value))
return value;
int subTag = 1;
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < value.length(); i++) {
switch (value.charAt(i)) {
case '-':
case '_':
buffer.append('-');
subTag++;
break;
case ' ':
break;
default:
if (subTag != 2) {
buffer.append(Character.toLowerCase(value.charAt(i)));
} else {
buffer.append(Character.toUpperCase(value.charAt(i)));
}
break;
}
}
return buffer.toString();
}
static String[] splitNameAndValue(String selector) {
int eq = selector.indexOf('=');
int pos = 1;
if (selector.charAt(pos) == '?')
pos++;
String name = selector.substring(pos, eq);
pos = eq + 1;
char quote = selector.charAt(pos);
pos++;
int end = selector.length() - 2;
StringBuffer value = new StringBuffer(end - eq);
while (pos < end) {
value.append(selector.charAt(pos));
pos++;
if (selector.charAt(pos) == quote)
pos++;
}
return new String[] { name, value.toString() };
}
static boolean isInternalProperty(String schema, String prop) {
boolean isInternal = false;
if ("http://purl.org/dc/elements/1.1/".equals(schema)) {
if ("dc:format".equals(prop) || "dc:language".equals(prop))
isInternal = true;
} else if ("http://ns.adobe.com/xap/1.0/".equals(schema)) {
if ("xmp:BaseURL".equals(prop) || "xmp:CreatorTool".equals(prop) || "xmp:Format"
.equals(prop) || "xmp:Locale".equals(prop) || "xmp:MetadataDate"
.equals(prop) || "xmp:ModifyDate".equals(prop))
isInternal = true;
} else if ("http://ns.adobe.com/pdf/1.3/".equals(schema)) {
if ("pdf:BaseURL".equals(prop) || "pdf:Creator".equals(prop) || "pdf:ModDate"
.equals(prop) || "pdf:PDFVersion".equals(prop) || "pdf:Producer"
.equals(prop))
isInternal = true;
} else if ("http://ns.adobe.com/tiff/1.0/".equals(schema)) {
isInternal = true;
if ("tiff:ImageDescription".equals(prop) || "tiff:Artist".equals(prop) || "tiff:Copyright"
.equals(prop))
isInternal = false;
} else if ("http://ns.adobe.com/exif/1.0/".equals(schema)) {
isInternal = true;
if ("exif:UserComment".equals(prop))
isInternal = false;
} else if ("http://ns.adobe.com/exif/1.0/aux/".equals(schema)) {
isInternal = true;
} else if ("http://ns.adobe.com/photoshop/1.0/".equals(schema)) {
if ("photoshop:ICCProfile".equals(prop))
isInternal = true;
} else if ("http://ns.adobe.com/camera-raw-settings/1.0/".equals(schema)) {
if ("crs:Version".equals(prop) || "crs:RawFileName".equals(prop) || "crs:ToneCurveName"
.equals(prop))
isInternal = true;
} else if ("http://ns.adobe.com/StockPhoto/1.0/".equals(schema)) {
isInternal = true;
} else if ("http://ns.adobe.com/xap/1.0/mm/".equals(schema)) {
isInternal = true;
} else if ("http://ns.adobe.com/xap/1.0/t/".equals(schema)) {
isInternal = true;
} else if ("http://ns.adobe.com/xap/1.0/t/pg/".equals(schema)) {
isInternal = true;
} else if ("http://ns.adobe.com/xap/1.0/g/".equals(schema)) {
isInternal = true;
} else if ("http://ns.adobe.com/xap/1.0/g/img/".equals(schema)) {
isInternal = true;
} else if ("http://ns.adobe.com/xap/1.0/sType/Font#".equals(schema)) {
isInternal = true;
}
return isInternal;
}
static boolean checkUUIDFormat(String uuid) {
boolean result = true;
int delimCnt = 0;
int delimPos = 0;
if (uuid == null)
return false;
for (delimPos = 0; delimPos < uuid.length(); delimPos++) {
if (uuid.charAt(delimPos) == '-') {
delimCnt++;
result = (result && (delimPos == 8 || delimPos == 13 || delimPos == 18 || delimPos == 23));
}
}
return (result && 4 == delimCnt && 36 == delimPos);
}
public static boolean isXMLName(String name) {
if (name.length() > 0 && !isNameStartChar(name.charAt(0)))
return false;
for (int i = 1; i < name.length(); i++) {
if (!isNameChar(name.charAt(i)))
return false;
}
return true;
}
public static boolean isXMLNameNS(String name) {
if (name.length() > 0 && (!isNameStartChar(name.charAt(0)) || name.charAt(0) == ':'))
return false;
for (int i = 1; i < name.length(); i++) {
if (!isNameChar(name.charAt(i)) || name.charAt(i) == ':')
return false;
}
return true;
}
static boolean isControlChar(char c) {
return ((c <= '\037' || c == '\u007F') && c != '\t' && c != '\n' && c != '\r');
}
public static String escapeXML(String value, boolean forAttribute, boolean escapeWhitespaces) {
boolean needsEscaping = false;
for (int i = 0; i < value.length(); i++) {
char c = value.charAt(i);
if (c == '<' || c == '>' || c == '&' || (escapeWhitespaces && (c == '\t' || c == '\n' || c == '\r')) || (forAttribute && c == '"')) {
needsEscaping = true;
break;
}
}
if (!needsEscaping)
return value;
StringBuffer buffer = new StringBuffer(value.length() * 4 / 3);
for (int j = 0; j < value.length(); j++) {
char c = value.charAt(j);
if (!escapeWhitespaces || (c != '\t' && c != '\n' && c != '\r')) {
switch (c) {
case '<':
buffer.append("&lt;");
break;
case '>':
buffer.append("&gt;");
break;
case '&':
buffer.append("&amp;");
break;
case '"':
buffer.append(forAttribute ? "&quot;" : "\"");
break;
default:
buffer.append(c);
break;
}
} else {
buffer.append("&#x");
buffer.append(Integer.toHexString(c).toUpperCase());
buffer.append(';');
}
}
return buffer.toString();
}
static String removeControlChars(String value) {
StringBuffer buffer = new StringBuffer(value);
for (int i = 0; i < buffer.length(); i++) {
if (isControlChar(buffer.charAt(i)))
buffer.setCharAt(i, ' ');
}
return buffer.toString();
}
private static boolean isNameStartChar(char ch) {
return ((ch <= 'ÿ' && xmlNameStartChars[ch]) || (ch >= 'Ā' && ch <= '˿') || (ch >= 'Ͱ' && ch <= 'ͽ') || (ch >= 'Ϳ' && ch <= '῿') || (ch >= '' && ch <= '') || (ch >= '⁰' && ch <= '↏') || (ch >= 'Ⰰ' && ch <= '⿯') || (ch >= '、' && ch <= '퟿') || (ch >= '豈' && ch <= '﷏') || (ch >= 'ﷰ' && ch <= '<27>') || (ch >= 65536 && ch <= 983039));
}
private static boolean isNameChar(char ch) {
return ((ch <= 'ÿ' && xmlNameChars[ch]) ||
isNameStartChar(ch) || (ch >= '̀' && ch <= 'ͯ') || (ch >= '‿' && ch <= '⁀'));
}
private static void initCharTables() {
xmlNameChars = new boolean[256];
xmlNameStartChars = new boolean[256];
for (char ch = '\000'; ch < xmlNameChars.length; ch = (char)(ch + 1)) {
xmlNameStartChars[ch] = (ch == ':' || ('A' <= ch && ch <= 'Z') || ch == '_' || ('a' <= ch && ch <= 'z') || ('À' <= ch && ch <= 'Ö') || ('Ø' <= ch && ch <= 'ö') || ('ø' <= ch && ch <= 'ÿ'));
xmlNameChars[ch] = (xmlNameStartChars[ch] || ch == '-' || ch == '.' || ('0' <= ch && ch <= '9') || ch == '·');
}
}
}

View file

@ -0,0 +1,200 @@
package com.itextpdf.xmp.impl;
import com.itextpdf.xmp.XMPDateTime;
import com.itextpdf.xmp.XMPException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
public class XMPDateTimeImpl implements XMPDateTime {
private int year = 0;
private int month = 0;
private int day = 0;
private int hour = 0;
private int minute = 0;
private int second = 0;
private TimeZone timeZone = null;
private int nanoSeconds;
private boolean hasDate = false;
private boolean hasTime = false;
private boolean hasTimeZone = false;
public XMPDateTimeImpl() {}
public XMPDateTimeImpl(Calendar calendar) {
Date date = calendar.getTime();
TimeZone zone = calendar.getTimeZone();
GregorianCalendar intCalendar = (GregorianCalendar)Calendar.getInstance(Locale.US);
intCalendar.setGregorianChange(new Date(Long.MIN_VALUE));
intCalendar.setTimeZone(zone);
intCalendar.setTime(date);
this.year = intCalendar.get(1);
this.month = intCalendar.get(2) + 1;
this.day = intCalendar.get(5);
this.hour = intCalendar.get(11);
this.minute = intCalendar.get(12);
this.second = intCalendar.get(13);
this.nanoSeconds = intCalendar.get(14) * 1000000;
this.timeZone = intCalendar.getTimeZone();
this.hasDate = this.hasTime = this.hasTimeZone = true;
}
public XMPDateTimeImpl(Date date, TimeZone timeZone) {
GregorianCalendar calendar = new GregorianCalendar(timeZone);
calendar.setTime(date);
this.year = calendar.get(1);
this.month = calendar.get(2) + 1;
this.day = calendar.get(5);
this.hour = calendar.get(11);
this.minute = calendar.get(12);
this.second = calendar.get(13);
this.nanoSeconds = calendar.get(14) * 1000000;
this.timeZone = timeZone;
this.hasDate = this.hasTime = this.hasTimeZone = true;
}
public XMPDateTimeImpl(String strValue) throws XMPException {
ISO8601Converter.parse(strValue, this);
}
public int getYear() {
return this.year;
}
public void setYear(int year) {
this.year = Math.min(Math.abs(year), 9999);
this.hasDate = true;
}
public int getMonth() {
return this.month;
}
public void setMonth(int month) {
if (month < 1) {
this.month = 1;
} else if (month > 12) {
this.month = 12;
} else {
this.month = month;
}
this.hasDate = true;
}
public int getDay() {
return this.day;
}
public void setDay(int day) {
if (day < 1) {
this.day = 1;
} else if (day > 31) {
this.day = 31;
} else {
this.day = day;
}
this.hasDate = true;
}
public int getHour() {
return this.hour;
}
public void setHour(int hour) {
this.hour = Math.min(Math.abs(hour), 23);
this.hasTime = true;
}
public int getMinute() {
return this.minute;
}
public void setMinute(int minute) {
this.minute = Math.min(Math.abs(minute), 59);
this.hasTime = true;
}
public int getSecond() {
return this.second;
}
public void setSecond(int second) {
this.second = Math.min(Math.abs(second), 59);
this.hasTime = true;
}
public int getNanoSecond() {
return this.nanoSeconds;
}
public void setNanoSecond(int nanoSecond) {
this.nanoSeconds = nanoSecond;
this.hasTime = true;
}
public int compareTo(Object dt) {
long d = getCalendar().getTimeInMillis() - ((XMPDateTime)dt)
.getCalendar().getTimeInMillis();
if (d != 0L)
return (int)Math.signum((float)d);
d = (long)(this.nanoSeconds - ((XMPDateTime)dt).getNanoSecond());
return (int)Math.signum((float)d);
}
public TimeZone getTimeZone() {
return this.timeZone;
}
public void setTimeZone(TimeZone timeZone) {
this.timeZone = timeZone;
this.hasTime = true;
this.hasTimeZone = true;
}
public boolean hasDate() {
return this.hasDate;
}
public boolean hasTime() {
return this.hasTime;
}
public boolean hasTimeZone() {
return this.hasTimeZone;
}
public Calendar getCalendar() {
GregorianCalendar calendar = (GregorianCalendar)Calendar.getInstance(Locale.US);
calendar.setGregorianChange(new Date(Long.MIN_VALUE));
if (this.hasTimeZone)
calendar.setTimeZone(this.timeZone);
calendar.set(1, this.year);
calendar.set(2, this.month - 1);
calendar.set(5, this.day);
calendar.set(11, this.hour);
calendar.set(12, this.minute);
calendar.set(13, this.second);
calendar.set(14, this.nanoSeconds / 1000000);
return calendar;
}
public String getISO8601String() {
return ISO8601Converter.render(this);
}
public String toString() {
return getISO8601String();
}
}

View file

@ -0,0 +1,286 @@
package com.itextpdf.xmp.impl;
import com.itextpdf.xmp.XMPException;
import com.itextpdf.xmp.XMPIterator;
import com.itextpdf.xmp.XMPMetaFactory;
import com.itextpdf.xmp.impl.xpath.XMPPath;
import com.itextpdf.xmp.impl.xpath.XMPPathParser;
import com.itextpdf.xmp.options.IteratorOptions;
import com.itextpdf.xmp.options.PropertyOptions;
import com.itextpdf.xmp.properties.XMPPropertyInfo;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class XMPIteratorImpl implements XMPIterator {
private IteratorOptions options;
private String baseNS = null;
protected boolean skipSiblings = false;
protected boolean skipSubtree = false;
private Iterator nodeIterator = null;
public XMPIteratorImpl(XMPMetaImpl xmp, String schemaNS, String propPath, IteratorOptions options) throws XMPException {
this.options = (options != null) ? options : new IteratorOptions();
XMPNode startNode = null;
String initialPath = null;
boolean baseSchema = (schemaNS != null && schemaNS.length() > 0);
boolean baseProperty = (propPath != null && propPath.length() > 0);
if (!baseSchema && !baseProperty) {
startNode = xmp.getRoot();
} else if (baseSchema && baseProperty) {
XMPPath path = XMPPathParser.expandXPath(schemaNS, propPath);
XMPPath basePath = new XMPPath();
for (int i = 0; i < path.size() - 1; i++)
basePath.add(path.getSegment(i));
startNode = XMPNodeUtils.findNode(xmp.getRoot(), path, false, null);
this.baseNS = schemaNS;
initialPath = basePath.toString();
} else if (baseSchema && !baseProperty) {
startNode = XMPNodeUtils.findSchemaNode(xmp.getRoot(), schemaNS, false);
} else {
throw new XMPException("Schema namespace URI is required", 101);
}
if (startNode != null) {
if (!this.options.isJustChildren()) {
this.nodeIterator = new NodeIterator(startNode, initialPath, 1);
} else {
this.nodeIterator = new NodeIteratorChildren(startNode, initialPath);
}
} else {
this.nodeIterator = Collections.EMPTY_LIST.iterator();
}
}
public void skipSubtree() {
this.skipSubtree = true;
}
public void skipSiblings() {
skipSubtree();
this.skipSiblings = true;
}
public boolean hasNext() {
return this.nodeIterator.hasNext();
}
public Object next() {
return this.nodeIterator.next();
}
public void remove() {
throw new UnsupportedOperationException("The XMPIterator does not support remove().");
}
protected IteratorOptions getOptions() {
return this.options;
}
protected String getBaseNS() {
return this.baseNS;
}
protected void setBaseNS(String baseNS) {
this.baseNS = baseNS;
}
private class NodeIterator implements Iterator {
protected static final int ITERATE_NODE = 0;
protected static final int ITERATE_CHILDREN = 1;
protected static final int ITERATE_QUALIFIER = 2;
private int state = 0;
private XMPNode visitedNode;
private String path;
private Iterator childrenIterator = null;
private int index = 0;
private Iterator subIterator = Collections.EMPTY_LIST.iterator();
private XMPPropertyInfo returnProperty = null;
public NodeIterator() {}
public NodeIterator(XMPNode visitedNode, String parentPath, int index) {
this.visitedNode = visitedNode;
this.state = 0;
if (visitedNode.getOptions().isSchemaNode())
XMPIteratorImpl.this.setBaseNS(visitedNode.getName());
this.path = accumulatePath(visitedNode, parentPath, index);
}
public boolean hasNext() {
if (this.returnProperty != null)
return true;
if (this.state == 0)
return reportNode();
if (this.state == 1) {
if (this.childrenIterator == null)
this.childrenIterator = this.visitedNode.iterateChildren();
boolean hasNext = iterateChildren(this.childrenIterator);
if (!hasNext && this.visitedNode.hasQualifier() && !XMPIteratorImpl.this.getOptions().isOmitQualifiers()) {
this.state = 2;
this.childrenIterator = null;
hasNext = hasNext();
}
return hasNext;
}
if (this.childrenIterator == null)
this.childrenIterator = this.visitedNode.iterateQualifier();
return iterateChildren(this.childrenIterator);
}
protected boolean reportNode() {
this.state = 1;
if (this.visitedNode.getParent() != null && (
!XMPIteratorImpl.this.getOptions().isJustLeafnodes() || !this.visitedNode.hasChildren())) {
this.returnProperty = createPropertyInfo(this.visitedNode, XMPIteratorImpl.this.getBaseNS(), this.path);
return true;
}
return hasNext();
}
private boolean iterateChildren(Iterator iterator) {
if (XMPIteratorImpl.this.skipSiblings) {
XMPIteratorImpl.this.skipSiblings = false;
this.subIterator = Collections.EMPTY_LIST.iterator();
}
if (!this.subIterator.hasNext() && iterator.hasNext()) {
XMPNode child = iterator.next();
this.index++;
this.subIterator = new NodeIterator(child, this.path, this.index);
}
if (this.subIterator.hasNext()) {
this.returnProperty = this.subIterator.next();
return true;
}
return false;
}
public Object next() {
if (hasNext()) {
XMPPropertyInfo result = this.returnProperty;
this.returnProperty = null;
return result;
}
throw new NoSuchElementException("There are no more nodes to return");
}
public void remove() {
throw new UnsupportedOperationException();
}
protected String accumulatePath(XMPNode currNode, String parentPath, int currentIndex) {
String separator, segmentName;
if (currNode.getParent() == null || currNode.getOptions().isSchemaNode())
return null;
if (currNode.getParent().getOptions().isArray()) {
separator = "";
segmentName = "[" + String.valueOf(currentIndex) + "]";
} else {
separator = "/";
segmentName = currNode.getName();
}
if (parentPath == null || parentPath.length() == 0)
return segmentName;
if (XMPIteratorImpl.this.getOptions().isJustLeafname())
return !segmentName.startsWith("?") ? segmentName :
segmentName.substring(1);
return parentPath + separator + segmentName;
}
protected XMPPropertyInfo createPropertyInfo(final XMPNode node, final String baseNS, final String path) {
final String value = node.getOptions().isSchemaNode() ? null : node.getValue();
return new XMPPropertyInfo() {
public String getNamespace() {
if (!node.getOptions().isSchemaNode()) {
QName qname = new QName(node.getName());
return XMPMetaFactory.getSchemaRegistry().getNamespaceURI(qname.getPrefix());
}
return baseNS;
}
public String getPath() {
return path;
}
public String getValue() {
return value;
}
public PropertyOptions getOptions() {
return node.getOptions();
}
public String getLanguage() {
return null;
}
};
}
protected Iterator getChildrenIterator() {
return this.childrenIterator;
}
protected void setChildrenIterator(Iterator childrenIterator) {
this.childrenIterator = childrenIterator;
}
protected XMPPropertyInfo getReturnProperty() {
return this.returnProperty;
}
protected void setReturnProperty(XMPPropertyInfo returnProperty) {
this.returnProperty = returnProperty;
}
}
private class NodeIteratorChildren extends NodeIterator {
private String parentPath;
private Iterator childrenIterator;
private int index = 0;
public NodeIteratorChildren(XMPNode parentNode, String parentPath) {
if (parentNode.getOptions().isSchemaNode())
XMPIteratorImpl.this.setBaseNS(parentNode.getName());
this.parentPath = accumulatePath(parentNode, parentPath, 1);
this.childrenIterator = parentNode.iterateChildren();
}
public boolean hasNext() {
if (getReturnProperty() != null)
return true;
if (XMPIteratorImpl.this.skipSiblings)
return false;
if (this.childrenIterator.hasNext()) {
XMPNode child = this.childrenIterator.next();
this.index++;
String path = null;
if (child.getOptions().isSchemaNode()) {
XMPIteratorImpl.this.setBaseNS(child.getName());
} else if (child.getParent() != null) {
path = accumulatePath(child, this.parentPath, this.index);
}
if (!XMPIteratorImpl.this.getOptions().isJustLeafnodes() || !child.hasChildren()) {
setReturnProperty(createPropertyInfo(child, XMPIteratorImpl.this.getBaseNS(), path));
return true;
}
return hasNext();
}
return false;
}
}
}

View file

@ -0,0 +1,645 @@
package com.itextpdf.xmp.impl;
import com.itextpdf.xmp.XMPConst;
import com.itextpdf.xmp.XMPDateTime;
import com.itextpdf.xmp.XMPException;
import com.itextpdf.xmp.XMPIterator;
import com.itextpdf.xmp.XMPMeta;
import com.itextpdf.xmp.XMPPathFactory;
import com.itextpdf.xmp.XMPUtils;
import com.itextpdf.xmp.impl.xpath.XMPPath;
import com.itextpdf.xmp.impl.xpath.XMPPathParser;
import com.itextpdf.xmp.options.IteratorOptions;
import com.itextpdf.xmp.options.ParseOptions;
import com.itextpdf.xmp.options.PropertyOptions;
import com.itextpdf.xmp.properties.XMPProperty;
import java.util.Calendar;
import java.util.Iterator;
public class XMPMetaImpl implements XMPMeta, XMPConst {
private static final int VALUE_STRING = 0;
private static final int VALUE_BOOLEAN = 1;
private static final int VALUE_INTEGER = 2;
private static final int VALUE_LONG = 3;
private static final int VALUE_DOUBLE = 4;
private static final int VALUE_DATE = 5;
private static final int VALUE_CALENDAR = 6;
private static final int VALUE_BASE64 = 7;
private XMPNode tree;
private String packetHeader = null;
public XMPMetaImpl() {
this.tree = new XMPNode(null, null, null);
}
public XMPMetaImpl(XMPNode tree) {
this.tree = tree;
}
public void appendArrayItem(String schemaNS, String arrayName, PropertyOptions arrayOptions, String itemValue, PropertyOptions itemOptions) throws XMPException {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertArrayName(arrayName);
if (arrayOptions == null)
arrayOptions = new PropertyOptions();
if (!arrayOptions.isOnlyArrayOptions())
throw new XMPException("Only array form flags allowed for arrayOptions", 103);
arrayOptions = XMPNodeUtils.verifySetOptions(arrayOptions, null);
XMPPath arrayPath = XMPPathParser.expandXPath(schemaNS, arrayName);
XMPNode arrayNode = XMPNodeUtils.findNode(this.tree, arrayPath, false, null);
if (arrayNode != null) {
if (!arrayNode.getOptions().isArray())
throw new XMPException("The named property is not an array", 102);
} else if (arrayOptions.isArray()) {
arrayNode = XMPNodeUtils.findNode(this.tree, arrayPath, true, arrayOptions);
if (arrayNode == null)
throw new XMPException("Failure creating array node", 102);
} else {
throw new XMPException("Explicit arrayOptions required to create new array", 103);
}
doSetArrayItem(arrayNode, -1, itemValue, itemOptions, true);
}
public void appendArrayItem(String schemaNS, String arrayName, String itemValue) throws XMPException {
appendArrayItem(schemaNS, arrayName, null, itemValue, null);
}
public int countArrayItems(String schemaNS, String arrayName) throws XMPException {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertArrayName(arrayName);
XMPPath arrayPath = XMPPathParser.expandXPath(schemaNS, arrayName);
XMPNode arrayNode = XMPNodeUtils.findNode(this.tree, arrayPath, false, null);
if (arrayNode == null)
return 0;
if (arrayNode.getOptions().isArray())
return arrayNode.getChildrenLength();
throw new XMPException("The named property is not an array", 102);
}
public void deleteArrayItem(String schemaNS, String arrayName, int itemIndex) {
try {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertArrayName(arrayName);
String itemPath = XMPPathFactory.composeArrayItemPath(arrayName, itemIndex);
deleteProperty(schemaNS, itemPath);
} catch (XMPException e) {}
}
public void deleteProperty(String schemaNS, String propName) {
try {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertPropName(propName);
XMPPath expPath = XMPPathParser.expandXPath(schemaNS, propName);
XMPNode propNode = XMPNodeUtils.findNode(this.tree, expPath, false, null);
if (propNode != null)
XMPNodeUtils.deleteNode(propNode);
} catch (XMPException e) {}
}
public void deleteQualifier(String schemaNS, String propName, String qualNS, String qualName) {
try {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertPropName(propName);
String qualPath = propName + XMPPathFactory.composeQualifierPath(qualNS, qualName);
deleteProperty(schemaNS, qualPath);
} catch (XMPException e) {}
}
public void deleteStructField(String schemaNS, String structName, String fieldNS, String fieldName) {
try {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertStructName(structName);
String fieldPath = structName +
XMPPathFactory.composeStructFieldPath(fieldNS, fieldName);
deleteProperty(schemaNS, fieldPath);
} catch (XMPException e) {}
}
public boolean doesPropertyExist(String schemaNS, String propName) {
try {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertPropName(propName);
XMPPath expPath = XMPPathParser.expandXPath(schemaNS, propName);
XMPNode propNode = XMPNodeUtils.findNode(this.tree, expPath, false, null);
return (propNode != null);
} catch (XMPException e) {
return false;
}
}
public boolean doesArrayItemExist(String schemaNS, String arrayName, int itemIndex) {
try {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertArrayName(arrayName);
String path = XMPPathFactory.composeArrayItemPath(arrayName, itemIndex);
return doesPropertyExist(schemaNS, path);
} catch (XMPException e) {
return false;
}
}
public boolean doesStructFieldExist(String schemaNS, String structName, String fieldNS, String fieldName) {
try {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertStructName(structName);
String path = XMPPathFactory.composeStructFieldPath(fieldNS, fieldName);
return doesPropertyExist(schemaNS, structName + path);
} catch (XMPException e) {
return false;
}
}
public boolean doesQualifierExist(String schemaNS, String propName, String qualNS, String qualName) {
try {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertPropName(propName);
String path = XMPPathFactory.composeQualifierPath(qualNS, qualName);
return doesPropertyExist(schemaNS, propName + path);
} catch (XMPException e) {
return false;
}
}
public XMPProperty getArrayItem(String schemaNS, String arrayName, int itemIndex) throws XMPException {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertArrayName(arrayName);
String itemPath = XMPPathFactory.composeArrayItemPath(arrayName, itemIndex);
return getProperty(schemaNS, itemPath);
}
public XMPProperty getLocalizedText(String schemaNS, String altTextName, String genericLang, String specificLang) throws XMPException {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertArrayName(altTextName);
ParameterAsserts.assertSpecificLang(specificLang);
genericLang = (genericLang != null) ? Utils.normalizeLangValue(genericLang) : null;
specificLang = Utils.normalizeLangValue(specificLang);
XMPPath arrayPath = XMPPathParser.expandXPath(schemaNS, altTextName);
XMPNode arrayNode = XMPNodeUtils.findNode(this.tree, arrayPath, false, null);
if (arrayNode == null)
return null;
Object[] result = XMPNodeUtils.chooseLocalizedText(arrayNode, genericLang, specificLang);
int match = (Integer)result[0];
final XMPNode itemNode = (XMPNode)result[1];
if (match != 0)
return new XMPProperty() {
public String getValue() {
return itemNode.getValue();
}
public PropertyOptions getOptions() {
return itemNode.getOptions();
}
public String getLanguage() {
return itemNode.getQualifier(1).getValue();
}
public String toString() {
return itemNode.getValue().toString();
}
};
return null;
}
public void setLocalizedText(String schemaNS, String altTextName, String genericLang, String specificLang, String itemValue, PropertyOptions options) throws XMPException {
Iterator<XMPNode> iterator1;
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertArrayName(altTextName);
ParameterAsserts.assertSpecificLang(specificLang);
genericLang = (genericLang != null) ? Utils.normalizeLangValue(genericLang) : null;
specificLang = Utils.normalizeLangValue(specificLang);
XMPPath arrayPath = XMPPathParser.expandXPath(schemaNS, altTextName);
XMPNode arrayNode = XMPNodeUtils.findNode(this.tree, arrayPath, true, new PropertyOptions(7680));
if (arrayNode == null)
throw new XMPException("Failed to find or create array node", 102);
if (!arrayNode.getOptions().isArrayAltText())
if (!arrayNode.hasChildren() && arrayNode.getOptions().isArrayAlternate()) {
arrayNode.getOptions().setArrayAltText(true);
} else {
throw new XMPException("Specified property is no alt-text array", 102);
}
boolean haveXDefault = false;
XMPNode xdItem = null;
for (Iterator<XMPNode> it = arrayNode.iterateChildren(); it.hasNext(); ) {
XMPNode currItem = it.next();
if (!currItem.hasQualifier() ||
!"xml:lang".equals(currItem.getQualifier(1).getName()))
throw new XMPException("Language qualifier must be first", 102);
if ("x-default".equals(currItem.getQualifier(1).getValue())) {
xdItem = currItem;
haveXDefault = true;
break;
}
}
if (xdItem != null && arrayNode.getChildrenLength() > 1) {
arrayNode.removeChild(xdItem);
arrayNode.addChild(1, xdItem);
}
Object[] result = XMPNodeUtils.chooseLocalizedText(arrayNode, genericLang, specificLang);
int match = (Integer)result[0];
XMPNode itemNode = (XMPNode)result[1];
boolean specificXDefault = "x-default".equals(specificLang);
switch (match) {
case 0:
XMPNodeUtils.appendLangItem(arrayNode, "x-default", itemValue);
haveXDefault = true;
if (!specificXDefault)
XMPNodeUtils.appendLangItem(arrayNode, specificLang, itemValue);
break;
case 1:
if (!specificXDefault) {
if (haveXDefault && xdItem != itemNode && xdItem != null &&
xdItem.getValue().equals(itemNode.getValue()))
xdItem.setValue(itemValue);
itemNode.setValue(itemValue);
break;
}
assert haveXDefault && xdItem == itemNode;
for (iterator1 = arrayNode.iterateChildren(); iterator1.hasNext(); ) {
XMPNode currItem = iterator1.next();
if (currItem != xdItem) {
if (!currItem.getValue().equals((xdItem != null) ?
xdItem.getValue() : null))
continue;
currItem.setValue(itemValue);
}
}
if (xdItem != null)
xdItem.setValue(itemValue);
break;
case 2:
if (haveXDefault && xdItem != itemNode && xdItem != null &&
xdItem.getValue().equals(itemNode.getValue()))
xdItem.setValue(itemValue);
itemNode.setValue(itemValue);
break;
case 3:
XMPNodeUtils.appendLangItem(arrayNode, specificLang, itemValue);
if (specificXDefault)
haveXDefault = true;
break;
case 4:
if (xdItem != null && arrayNode.getChildrenLength() == 1)
xdItem.setValue(itemValue);
XMPNodeUtils.appendLangItem(arrayNode, specificLang, itemValue);
break;
case 5:
XMPNodeUtils.appendLangItem(arrayNode, specificLang, itemValue);
if (specificXDefault)
haveXDefault = true;
break;
default:
throw new XMPException("Unexpected result from ChooseLocalizedText", 9);
}
if (!haveXDefault && arrayNode.getChildrenLength() == 1)
XMPNodeUtils.appendLangItem(arrayNode, "x-default", itemValue);
}
public void setLocalizedText(String schemaNS, String altTextName, String genericLang, String specificLang, String itemValue) throws XMPException {
setLocalizedText(schemaNS, altTextName, genericLang, specificLang, itemValue, null);
}
public XMPProperty getProperty(String schemaNS, String propName) throws XMPException {
return getProperty(schemaNS, propName, 0);
}
protected XMPProperty getProperty(String schemaNS, String propName, int valueType) throws XMPException {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertPropName(propName);
XMPPath expPath = XMPPathParser.expandXPath(schemaNS, propName);
final XMPNode propNode = XMPNodeUtils.findNode(this.tree, expPath, false, null);
if (propNode != null) {
if (valueType != 0 && propNode.getOptions().isCompositeProperty())
throw new XMPException("Property must be simple when a value type is requested", 102);
final Object value = evaluateNodeValue(valueType, propNode);
return new XMPProperty() {
public String getValue() {
return (value != null) ? value.toString() : null;
}
public PropertyOptions getOptions() {
return propNode.getOptions();
}
public String getLanguage() {
return null;
}
public String toString() {
return value.toString();
}
};
}
return null;
}
protected Object getPropertyObject(String schemaNS, String propName, int valueType) throws XMPException {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertPropName(propName);
XMPPath expPath = XMPPathParser.expandXPath(schemaNS, propName);
XMPNode propNode = XMPNodeUtils.findNode(this.tree, expPath, false, null);
if (propNode != null) {
if (valueType != 0 && propNode.getOptions().isCompositeProperty())
throw new XMPException("Property must be simple when a value type is requested", 102);
return evaluateNodeValue(valueType, propNode);
}
return null;
}
public Boolean getPropertyBoolean(String schemaNS, String propName) throws XMPException {
return (Boolean)getPropertyObject(schemaNS, propName, 1);
}
public void setPropertyBoolean(String schemaNS, String propName, boolean propValue, PropertyOptions options) throws XMPException {
setProperty(schemaNS, propName, propValue ? "True" : "False", options);
}
public void setPropertyBoolean(String schemaNS, String propName, boolean propValue) throws XMPException {
setProperty(schemaNS, propName, propValue ? "True" : "False", null);
}
public Integer getPropertyInteger(String schemaNS, String propName) throws XMPException {
return (Integer)getPropertyObject(schemaNS, propName, 2);
}
public void setPropertyInteger(String schemaNS, String propName, int propValue, PropertyOptions options) throws XMPException {
setProperty(schemaNS, propName, new Integer(propValue), options);
}
public void setPropertyInteger(String schemaNS, String propName, int propValue) throws XMPException {
setProperty(schemaNS, propName, new Integer(propValue), null);
}
public Long getPropertyLong(String schemaNS, String propName) throws XMPException {
return (Long)getPropertyObject(schemaNS, propName, 3);
}
public void setPropertyLong(String schemaNS, String propName, long propValue, PropertyOptions options) throws XMPException {
setProperty(schemaNS, propName, new Long(propValue), options);
}
public void setPropertyLong(String schemaNS, String propName, long propValue) throws XMPException {
setProperty(schemaNS, propName, new Long(propValue), null);
}
public Double getPropertyDouble(String schemaNS, String propName) throws XMPException {
return (Double)getPropertyObject(schemaNS, propName, 4);
}
public void setPropertyDouble(String schemaNS, String propName, double propValue, PropertyOptions options) throws XMPException {
setProperty(schemaNS, propName, new Double(propValue), options);
}
public void setPropertyDouble(String schemaNS, String propName, double propValue) throws XMPException {
setProperty(schemaNS, propName, new Double(propValue), null);
}
public XMPDateTime getPropertyDate(String schemaNS, String propName) throws XMPException {
return (XMPDateTime)getPropertyObject(schemaNS, propName, 5);
}
public void setPropertyDate(String schemaNS, String propName, XMPDateTime propValue, PropertyOptions options) throws XMPException {
setProperty(schemaNS, propName, propValue, options);
}
public void setPropertyDate(String schemaNS, String propName, XMPDateTime propValue) throws XMPException {
setProperty(schemaNS, propName, propValue, null);
}
public Calendar getPropertyCalendar(String schemaNS, String propName) throws XMPException {
return (Calendar)getPropertyObject(schemaNS, propName, 6);
}
public void setPropertyCalendar(String schemaNS, String propName, Calendar propValue, PropertyOptions options) throws XMPException {
setProperty(schemaNS, propName, propValue, options);
}
public void setPropertyCalendar(String schemaNS, String propName, Calendar propValue) throws XMPException {
setProperty(schemaNS, propName, propValue, null);
}
public byte[] getPropertyBase64(String schemaNS, String propName) throws XMPException {
return (byte[])getPropertyObject(schemaNS, propName, 7);
}
public String getPropertyString(String schemaNS, String propName) throws XMPException {
return (String)getPropertyObject(schemaNS, propName, 0);
}
public void setPropertyBase64(String schemaNS, String propName, byte[] propValue, PropertyOptions options) throws XMPException {
setProperty(schemaNS, propName, propValue, options);
}
public void setPropertyBase64(String schemaNS, String propName, byte[] propValue) throws XMPException {
setProperty(schemaNS, propName, propValue, null);
}
public XMPProperty getQualifier(String schemaNS, String propName, String qualNS, String qualName) throws XMPException {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertPropName(propName);
String qualPath = propName + XMPPathFactory.composeQualifierPath(qualNS, qualName);
return getProperty(schemaNS, qualPath);
}
public XMPProperty getStructField(String schemaNS, String structName, String fieldNS, String fieldName) throws XMPException {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertStructName(structName);
String fieldPath = structName + XMPPathFactory.composeStructFieldPath(fieldNS, fieldName);
return getProperty(schemaNS, fieldPath);
}
public XMPIterator iterator() throws XMPException {
return iterator(null, null, null);
}
public XMPIterator iterator(IteratorOptions options) throws XMPException {
return iterator(null, null, options);
}
public XMPIterator iterator(String schemaNS, String propName, IteratorOptions options) throws XMPException {
return new XMPIteratorImpl(this, schemaNS, propName, options);
}
public void setArrayItem(String schemaNS, String arrayName, int itemIndex, String itemValue, PropertyOptions options) throws XMPException {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertArrayName(arrayName);
XMPPath arrayPath = XMPPathParser.expandXPath(schemaNS, arrayName);
XMPNode arrayNode = XMPNodeUtils.findNode(this.tree, arrayPath, false, null);
if (arrayNode != null) {
doSetArrayItem(arrayNode, itemIndex, itemValue, options, false);
} else {
throw new XMPException("Specified array does not exist", 102);
}
}
public void setArrayItem(String schemaNS, String arrayName, int itemIndex, String itemValue) throws XMPException {
setArrayItem(schemaNS, arrayName, itemIndex, itemValue, null);
}
public void insertArrayItem(String schemaNS, String arrayName, int itemIndex, String itemValue, PropertyOptions options) throws XMPException {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertArrayName(arrayName);
XMPPath arrayPath = XMPPathParser.expandXPath(schemaNS, arrayName);
XMPNode arrayNode = XMPNodeUtils.findNode(this.tree, arrayPath, false, null);
if (arrayNode != null) {
doSetArrayItem(arrayNode, itemIndex, itemValue, options, true);
} else {
throw new XMPException("Specified array does not exist", 102);
}
}
public void insertArrayItem(String schemaNS, String arrayName, int itemIndex, String itemValue) throws XMPException {
insertArrayItem(schemaNS, arrayName, itemIndex, itemValue, null);
}
public void setProperty(String schemaNS, String propName, Object propValue, PropertyOptions options) throws XMPException {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertPropName(propName);
options = XMPNodeUtils.verifySetOptions(options, propValue);
XMPPath expPath = XMPPathParser.expandXPath(schemaNS, propName);
XMPNode propNode = XMPNodeUtils.findNode(this.tree, expPath, true, options);
if (propNode != null) {
setNode(propNode, propValue, options, false);
} else {
throw new XMPException("Specified property does not exist", 102);
}
}
public void setProperty(String schemaNS, String propName, Object propValue) throws XMPException {
setProperty(schemaNS, propName, propValue, null);
}
public void setQualifier(String schemaNS, String propName, String qualNS, String qualName, String qualValue, PropertyOptions options) throws XMPException {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertPropName(propName);
if (!doesPropertyExist(schemaNS, propName))
throw new XMPException("Specified property does not exist!", 102);
String qualPath = propName + XMPPathFactory.composeQualifierPath(qualNS, qualName);
setProperty(schemaNS, qualPath, qualValue, options);
}
public void setQualifier(String schemaNS, String propName, String qualNS, String qualName, String qualValue) throws XMPException {
setQualifier(schemaNS, propName, qualNS, qualName, qualValue, null);
}
public void setStructField(String schemaNS, String structName, String fieldNS, String fieldName, String fieldValue, PropertyOptions options) throws XMPException {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertStructName(structName);
String fieldPath = structName + XMPPathFactory.composeStructFieldPath(fieldNS, fieldName);
setProperty(schemaNS, fieldPath, fieldValue, options);
}
public void setStructField(String schemaNS, String structName, String fieldNS, String fieldName, String fieldValue) throws XMPException {
setStructField(schemaNS, structName, fieldNS, fieldName, fieldValue, null);
}
public String getObjectName() {
return (this.tree.getName() != null) ? this.tree.getName() : "";
}
public void setObjectName(String name) {
this.tree.setName(name);
}
public String getPacketHeader() {
return this.packetHeader;
}
public void setPacketHeader(String packetHeader) {
this.packetHeader = packetHeader;
}
public Object clone() {
XMPNode clonedTree = (XMPNode)this.tree.clone();
return new XMPMetaImpl(clonedTree);
}
public String dumpObject() {
return getRoot().dumpNode(true);
}
public void sort() {
this.tree.sort();
}
public void normalize(ParseOptions options) throws XMPException {
if (options == null)
options = new ParseOptions();
XMPNormalizer.process(this, options);
}
public XMPNode getRoot() {
return this.tree;
}
private void doSetArrayItem(XMPNode arrayNode, int itemIndex, String itemValue, PropertyOptions itemOptions, boolean insert) throws XMPException {
XMPNode itemNode = new XMPNode("[]", null);
itemOptions = XMPNodeUtils.verifySetOptions(itemOptions, itemValue);
int maxIndex = insert ? (arrayNode.getChildrenLength() + 1) : arrayNode.getChildrenLength();
if (itemIndex == -1)
itemIndex = maxIndex;
if (1 <= itemIndex && itemIndex <= maxIndex) {
if (!insert)
arrayNode.removeChild(itemIndex);
arrayNode.addChild(itemIndex, itemNode);
setNode(itemNode, itemValue, itemOptions, false);
} else {
throw new XMPException("Array index out of bounds", 104);
}
}
void setNode(XMPNode node, Object value, PropertyOptions newOptions, boolean deleteExisting) throws XMPException {
if (deleteExisting)
node.clear();
node.getOptions().mergeWith(newOptions);
if (!node.getOptions().isCompositeProperty()) {
XMPNodeUtils.setNodeValue(node, value);
} else {
if (value != null && value.toString().length() > 0)
throw new XMPException("Composite nodes can't have values", 102);
node.removeChildren();
}
}
private Object evaluateNodeValue(int valueType, XMPNode propNode) throws XMPException {
Object value;
XMPDateTime dt;
String rawValue = propNode.getValue();
switch (valueType) {
case 1:
value = new Boolean(XMPUtils.convertToBoolean(rawValue));
break;
case 2:
value = new Integer(XMPUtils.convertToInteger(rawValue));
break;
case 3:
value = new Long(XMPUtils.convertToLong(rawValue));
break;
case 4:
value = new Double(XMPUtils.convertToDouble(rawValue));
break;
case 5:
value = XMPUtils.convertToDate(rawValue);
break;
case 6:
dt = XMPUtils.convertToDate(rawValue);
value = dt.getCalendar();
break;
case 7:
value = XMPUtils.decodeBase64(rawValue);
break;
default:
value = (rawValue != null || propNode.getOptions().isCompositeProperty()) ? rawValue : "";
break;
}
return value;
}
}

View file

@ -0,0 +1,165 @@
package com.itextpdf.xmp.impl;
import com.itextpdf.xmp.XMPException;
import com.itextpdf.xmp.XMPMeta;
import com.itextpdf.xmp.options.ParseOptions;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ProcessingInstruction;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class XMPMetaParser {
private static final Object XMP_RDF = new Object();
private static DocumentBuilderFactory factory = createDocumentBuilderFactory();
public static XMPMeta parse(Object input, ParseOptions options) throws XMPException {
ParameterAsserts.assertNotNull(input);
options = (options != null) ? options : new ParseOptions();
Document document = parseXml(input, options);
boolean xmpmetaRequired = options.getRequireXMPMeta();
Object[] result = new Object[3];
result = findRootNode(document, xmpmetaRequired, result);
if (result != null && result[1] == XMP_RDF) {
XMPMetaImpl xmp = ParseRDF.parse((Node)result[0]);
xmp.setPacketHeader((String)result[2]);
if (!options.getOmitNormalization())
return XMPNormalizer.process(xmp, options);
return xmp;
}
return new XMPMetaImpl();
}
private static Document parseXml(Object input, ParseOptions options) throws XMPException {
if (input instanceof InputStream)
return parseXmlFromInputStream((InputStream)input, options);
if (input instanceof byte[])
return parseXmlFromBytebuffer(new ByteBuffer((byte[])input), options);
return parseXmlFromString((String)input, options);
}
private static Document parseXmlFromInputStream(InputStream stream, ParseOptions options) throws XMPException {
if (!options.getAcceptLatin1() && !options.getFixControlChars())
return parseInputSource(new InputSource(stream));
try {
ByteBuffer buffer = new ByteBuffer(stream);
return parseXmlFromBytebuffer(buffer, options);
} catch (IOException e) {
throw new XMPException("Error reading the XML-file", 204, e);
}
}
private static Document parseXmlFromBytebuffer(ByteBuffer buffer, ParseOptions options) throws XMPException {
InputSource source = new InputSource(buffer.getByteStream());
try {
return parseInputSource(source);
} catch (XMPException e) {
if (e.getErrorCode() == 201 ||
e.getErrorCode() == 204) {
if (options.getAcceptLatin1())
buffer = Latin1Converter.convert(buffer);
if (options.getFixControlChars())
try {
String encoding = buffer.getEncoding();
Reader fixReader = new FixASCIIControlsReader(new InputStreamReader(
buffer.getByteStream(), encoding));
return parseInputSource(new InputSource(fixReader));
} catch (UnsupportedEncodingException e1) {
throw new XMPException("Unsupported Encoding", 9, e);
}
source = new InputSource(buffer.getByteStream());
return parseInputSource(source);
}
throw e;
}
}
private static Document parseXmlFromString(String input, ParseOptions options) throws XMPException {
InputSource source = new InputSource(new StringReader(input));
try {
return parseInputSource(source);
} catch (XMPException e) {
if (e.getErrorCode() == 201 && options.getFixControlChars()) {
source = new InputSource(new FixASCIIControlsReader(new StringReader(input)));
return parseInputSource(source);
}
throw e;
}
}
private static Document parseInputSource(InputSource source) throws XMPException {
try {
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setErrorHandler(null);
return builder.parse(source);
} catch (SAXException e) {
throw new XMPException("XML parsing failure", 201, e);
} catch (ParserConfigurationException e) {
throw new XMPException("XML Parser not correctly configured", 0, e);
} catch (IOException e) {
throw new XMPException("Error reading the XML-file", 204, e);
}
}
private static Object[] findRootNode(Node root, boolean xmpmetaRequired, Object[] result) {
NodeList children = root.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
root = children.item(i);
if (7 == root.getNodeType() && "xpacket"
.equals(((ProcessingInstruction)root).getTarget())) {
if (result != null)
result[2] = ((ProcessingInstruction)root).getData();
} else if (3 != root.getNodeType() && 7 !=
root.getNodeType()) {
String rootNS = root.getNamespaceURI();
String rootLocal = root.getLocalName();
if (("xmpmeta"
.equals(rootLocal) || "xapmeta"
.equals(rootLocal)) && "adobe:ns:meta/"
.equals(rootNS))
return findRootNode(root, false, result);
if (!xmpmetaRequired && "RDF"
.equals(rootLocal) && "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
.equals(rootNS)) {
if (result != null) {
result[0] = root;
result[1] = XMP_RDF;
}
return result;
}
Object[] newResult = findRootNode(root, xmpmetaRequired, result);
if (newResult != null)
return newResult;
}
}
return null;
}
private static DocumentBuilderFactory createDocumentBuilderFactory() {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setIgnoringComments(true);
try {
factory.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
} catch (Exception e) {}
return factory;
}
}

View file

@ -0,0 +1,424 @@
package com.itextpdf.xmp.impl;
import com.itextpdf.xmp.XMPException;
import com.itextpdf.xmp.options.PropertyOptions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
class XMPNode implements Comparable {
private String name;
private String value;
private XMPNode parent;
private List children = null;
private List qualifier = null;
private PropertyOptions options = null;
private boolean implicit;
private boolean hasAliases;
private boolean alias;
private boolean hasValueChild;
public XMPNode(String name, String value, PropertyOptions options) {
this.name = name;
this.value = value;
this.options = options;
}
public XMPNode(String name, PropertyOptions options) {
this(name, null, options);
}
public void clear() {
this.options = null;
this.name = null;
this.value = null;
this.children = null;
this.qualifier = null;
}
public XMPNode getParent() {
return this.parent;
}
public XMPNode getChild(int index) {
return getChildren().get(index - 1);
}
public void addChild(XMPNode node) throws XMPException {
assertChildNotExisting(node.getName());
node.setParent(this);
getChildren().add(node);
}
public void addChild(int index, XMPNode node) throws XMPException {
assertChildNotExisting(node.getName());
node.setParent(this);
getChildren().add(index - 1, node);
}
public void replaceChild(int index, XMPNode node) {
node.setParent(this);
getChildren().set(index - 1, node);
}
public void removeChild(int itemIndex) {
getChildren().remove(itemIndex - 1);
cleanupChildren();
}
public void removeChild(XMPNode node) {
getChildren().remove(node);
cleanupChildren();
}
protected void cleanupChildren() {
if (this.children.isEmpty())
this.children = null;
}
public void removeChildren() {
this.children = null;
}
public int getChildrenLength() {
return (this.children != null) ?
this.children.size() : 0;
}
public XMPNode findChildByName(String expr) {
return find(getChildren(), expr);
}
public XMPNode getQualifier(int index) {
return getQualifier().get(index - 1);
}
public int getQualifierLength() {
return (this.qualifier != null) ?
this.qualifier.size() : 0;
}
public void addQualifier(XMPNode qualNode) throws XMPException {
assertQualifierNotExisting(qualNode.getName());
qualNode.setParent(this);
qualNode.getOptions().setQualifier(true);
getOptions().setHasQualifiers(true);
if (qualNode.isLanguageNode()) {
this.options.setHasLanguage(true);
getQualifier().add(0, qualNode);
} else if (qualNode.isTypeNode()) {
this.options.setHasType(true);
getQualifier().add(
!this.options.getHasLanguage() ? 0 : 1, qualNode);
} else {
getQualifier().add(qualNode);
}
}
public void removeQualifier(XMPNode qualNode) {
PropertyOptions opts = getOptions();
if (qualNode.isLanguageNode()) {
opts.setHasLanguage(false);
} else if (qualNode.isTypeNode()) {
opts.setHasType(false);
}
getQualifier().remove(qualNode);
if (this.qualifier.isEmpty()) {
opts.setHasQualifiers(false);
this.qualifier = null;
}
}
public void removeQualifiers() {
PropertyOptions opts = getOptions();
opts.setHasQualifiers(false);
opts.setHasLanguage(false);
opts.setHasType(false);
this.qualifier = null;
}
public XMPNode findQualifierByName(String expr) {
return find(this.qualifier, expr);
}
public boolean hasChildren() {
return (this.children != null && this.children.size() > 0);
}
public Iterator iterateChildren() {
if (this.children != null)
return getChildren().iterator();
return Collections.EMPTY_LIST.listIterator();
}
public boolean hasQualifier() {
return (this.qualifier != null && this.qualifier.size() > 0);
}
public Iterator iterateQualifier() {
if (this.qualifier != null) {
final Iterator it = getQualifier().iterator();
return new Iterator() {
public boolean hasNext() {
return it.hasNext();
}
public Object next() {
return it.next();
}
public void remove() {
throw new UnsupportedOperationException("remove() is not allowed due to the internal contraints");
}
};
}
return Collections.EMPTY_LIST.iterator();
}
public Object clone() {
PropertyOptions newOptions;
try {
newOptions = new PropertyOptions(getOptions().getOptions());
} catch (XMPException e) {
newOptions = new PropertyOptions();
}
XMPNode newNode = new XMPNode(this.name, this.value, newOptions);
cloneSubtree(newNode);
return newNode;
}
public void cloneSubtree(XMPNode destination) {
try {
for (Iterator<XMPNode> iterator1 = iterateChildren(); iterator1.hasNext(); ) {
XMPNode child = iterator1.next();
destination.addChild((XMPNode)child.clone());
}
for (Iterator<XMPNode> it = iterateQualifier(); it.hasNext(); ) {
XMPNode qualifier = it.next();
destination.addQualifier((XMPNode)qualifier.clone());
}
} catch (XMPException e) {
assert false;
}
}
public String dumpNode(boolean recursive) {
StringBuffer result = new StringBuffer(512);
dumpNode(result, recursive, 0, 0);
return result.toString();
}
public int compareTo(Object xmpNode) {
if (getOptions().isSchemaNode())
return this.value.compareTo(((XMPNode)xmpNode).getValue());
return this.name.compareTo(((XMPNode)xmpNode).getName());
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return this.value;
}
public void setValue(String value) {
this.value = value;
}
public PropertyOptions getOptions() {
if (this.options == null)
this.options = new PropertyOptions();
return this.options;
}
public void setOptions(PropertyOptions options) {
this.options = options;
}
public boolean isImplicit() {
return this.implicit;
}
public void setImplicit(boolean implicit) {
this.implicit = implicit;
}
public boolean getHasAliases() {
return this.hasAliases;
}
public void setHasAliases(boolean hasAliases) {
this.hasAliases = hasAliases;
}
public boolean isAlias() {
return this.alias;
}
public void setAlias(boolean alias) {
this.alias = alias;
}
public boolean getHasValueChild() {
return this.hasValueChild;
}
public void setHasValueChild(boolean hasValueChild) {
this.hasValueChild = hasValueChild;
}
public void sort() {
if (hasQualifier()) {
XMPNode[] quals = (XMPNode[])getQualifier()
.toArray(new XMPNode[getQualifierLength()]);
int sortFrom = 0;
while (quals.length > sortFrom && ("xml:lang"
.equals(quals[sortFrom].getName()) || "rdf:type"
.equals(quals[sortFrom].getName()))) {
quals[sortFrom].sort();
sortFrom++;
}
Arrays.sort(quals, sortFrom, quals.length);
ListIterator<XMPNode> it = this.qualifier.listIterator();
for (int j = 0; j < quals.length; j++) {
it.next();
it.set(quals[j]);
quals[j].sort();
}
}
if (hasChildren()) {
if (!getOptions().isArray())
Collections.sort(this.children);
for (Iterator<XMPNode> it = iterateChildren(); it.hasNext();)
it.next().sort();
}
}
private void dumpNode(StringBuffer result, boolean recursive, int indent, int index) {
for (int i = 0; i < indent; i++)
result.append('\t');
if (this.parent != null) {
if (getOptions().isQualifier()) {
result.append('?');
result.append(this.name);
} else if (getParent().getOptions().isArray()) {
result.append('[');
result.append(index);
result.append(']');
} else {
result.append(this.name);
}
} else {
result.append("ROOT NODE");
if (this.name != null && this.name.length() > 0) {
result.append(" (");
result.append(this.name);
result.append(')');
}
}
if (this.value != null && this.value.length() > 0) {
result.append(" = \"");
result.append(this.value);
result.append('"');
}
if (getOptions().containsOneOf(-1)) {
result.append("\t(");
result.append(getOptions().toString());
result.append(" : ");
result.append(getOptions().getOptionsString());
result.append(')');
}
result.append('\n');
if (recursive && hasQualifier()) {
XMPNode[] quals = (XMPNode[])getQualifier()
.toArray(new XMPNode[getQualifierLength()]);
int j = 0;
while (quals.length > j && ("xml:lang"
.equals(quals[j].getName()) || "rdf:type"
.equals(quals[j].getName())))
j++;
Arrays.sort(quals, j, quals.length);
for (j = 0; j < quals.length; j++) {
XMPNode qualifier = quals[j];
qualifier.dumpNode(result, recursive, indent + 2, j + 1);
}
}
if (recursive && hasChildren()) {
XMPNode[] children = (XMPNode[])getChildren()
.toArray(new XMPNode[getChildrenLength()]);
if (!getOptions().isArray())
Arrays.sort(children);
for (int j = 0; j < children.length; j++) {
XMPNode child = children[j];
child.dumpNode(result, recursive, indent + 1, j + 1);
}
}
}
private boolean isLanguageNode() {
return "xml:lang".equals(this.name);
}
private boolean isTypeNode() {
return "rdf:type".equals(this.name);
}
private List getChildren() {
if (this.children == null)
this.children = new ArrayList(0);
return this.children;
}
public List getUnmodifiableChildren() {
return Collections.unmodifiableList(new ArrayList(getChildren()));
}
private List getQualifier() {
if (this.qualifier == null)
this.qualifier = new ArrayList(0);
return this.qualifier;
}
protected void setParent(XMPNode parent) {
this.parent = parent;
}
private XMPNode find(List list, String expr) {
if (list != null)
for (XMPNode child : (Iterable<XMPNode>)list) {
if (child.getName().equals(expr))
return child;
}
return null;
}
private void assertChildNotExisting(String childName) throws XMPException {
if (!"[]".equals(childName) &&
findChildByName(childName) != null)
throw new XMPException("Duplicate property or field node '" + childName + "'", 203);
}
private void assertQualifierNotExisting(String qualifierName) throws XMPException {
if (!"[]".equals(qualifierName) &&
findQualifierByName(qualifierName) != null)
throw new XMPException("Duplicate '" + qualifierName + "' qualifier", 203);
}
}

View file

@ -0,0 +1,383 @@
package com.itextpdf.xmp.impl;
import com.itextpdf.xmp.XMPConst;
import com.itextpdf.xmp.XMPDateTime;
import com.itextpdf.xmp.XMPDateTimeFactory;
import com.itextpdf.xmp.XMPException;
import com.itextpdf.xmp.XMPMetaFactory;
import com.itextpdf.xmp.XMPUtils;
import com.itextpdf.xmp.impl.xpath.XMPPath;
import com.itextpdf.xmp.impl.xpath.XMPPathSegment;
import com.itextpdf.xmp.options.PropertyOptions;
import java.util.GregorianCalendar;
import java.util.Iterator;
public class XMPNodeUtils implements XMPConst {
static final int CLT_NO_VALUES = 0;
static final int CLT_SPECIFIC_MATCH = 1;
static final int CLT_SINGLE_GENERIC = 2;
static final int CLT_MULTIPLE_GENERIC = 3;
static final int CLT_XDEFAULT = 4;
static final int CLT_FIRST_ITEM = 5;
static XMPNode findSchemaNode(XMPNode tree, String namespaceURI, boolean createNodes) throws XMPException {
return findSchemaNode(tree, namespaceURI, null, createNodes);
}
static XMPNode findSchemaNode(XMPNode tree, String namespaceURI, String suggestedPrefix, boolean createNodes) throws XMPException {
assert tree.getParent() == null;
XMPNode schemaNode = tree.findChildByName(namespaceURI);
if (schemaNode == null && createNodes) {
schemaNode = new XMPNode(namespaceURI, new PropertyOptions().setSchemaNode(true));
schemaNode.setImplicit(true);
String prefix = XMPMetaFactory.getSchemaRegistry().getNamespacePrefix(namespaceURI);
if (prefix == null)
if (suggestedPrefix != null && suggestedPrefix.length() != 0) {
prefix = XMPMetaFactory.getSchemaRegistry().registerNamespace(namespaceURI, suggestedPrefix);
} else {
throw new XMPException("Unregistered schema namespace URI", 101);
}
schemaNode.setValue(prefix);
tree.addChild(schemaNode);
}
return schemaNode;
}
static XMPNode findChildNode(XMPNode parent, String childName, boolean createNodes) throws XMPException {
if (!parent.getOptions().isSchemaNode() && !parent.getOptions().isStruct()) {
if (!parent.isImplicit())
throw new XMPException("Named children only allowed for schemas and structs", 102);
if (parent.getOptions().isArray())
throw new XMPException("Named children not allowed for arrays", 102);
if (createNodes)
parent.getOptions().setStruct(true);
}
XMPNode childNode = parent.findChildByName(childName);
if (childNode == null && createNodes) {
PropertyOptions options = new PropertyOptions();
childNode = new XMPNode(childName, options);
childNode.setImplicit(true);
parent.addChild(childNode);
}
assert childNode != null || !createNodes;
return childNode;
}
static XMPNode findNode(XMPNode xmpTree, XMPPath xpath, boolean createNodes, PropertyOptions leafOptions) throws XMPException {
if (xpath == null || xpath.size() == 0)
throw new XMPException("Empty XMPPath", 102);
XMPNode rootImplicitNode = null;
XMPNode currNode = null;
currNode = findSchemaNode(xmpTree,
xpath.getSegment(0).getName(), createNodes);
if (currNode == null)
return null;
if (currNode.isImplicit()) {
currNode.setImplicit(false);
rootImplicitNode = currNode;
}
try {
for (int i = 1; i < xpath.size(); i++) {
currNode = followXPathStep(currNode, xpath.getSegment(i), createNodes);
if (currNode == null) {
if (createNodes)
deleteNode(rootImplicitNode);
return null;
}
if (currNode.isImplicit()) {
currNode.setImplicit(false);
if (i == 1 &&
xpath.getSegment(i).isAlias() &&
xpath.getSegment(i).getAliasForm() != 0) {
currNode.getOptions().setOption(xpath.getSegment(i).getAliasForm(), true);
} else if (i < xpath.size() - 1 &&
xpath.getSegment(i).getKind() == 1 &&
!currNode.getOptions().isCompositeProperty()) {
currNode.getOptions().setStruct(true);
}
if (rootImplicitNode == null)
rootImplicitNode = currNode;
}
}
} catch (XMPException e) {
if (rootImplicitNode != null)
deleteNode(rootImplicitNode);
throw e;
}
if (rootImplicitNode != null) {
currNode.getOptions().mergeWith(leafOptions);
currNode.setOptions(currNode.getOptions());
}
return currNode;
}
static void deleteNode(XMPNode node) {
XMPNode parent = node.getParent();
if (node.getOptions().isQualifier()) {
parent.removeQualifier(node);
} else {
parent.removeChild(node);
}
if (!parent.hasChildren() && parent.getOptions().isSchemaNode())
parent.getParent().removeChild(parent);
}
static void setNodeValue(XMPNode node, Object value) {
String strValue = serializeNodeValue(value);
if (!node.getOptions().isQualifier() || !"xml:lang".equals(node.getName())) {
node.setValue(strValue);
} else {
node.setValue(Utils.normalizeLangValue(strValue));
}
}
static PropertyOptions verifySetOptions(PropertyOptions options, Object itemValue) throws XMPException {
if (options == null)
options = new PropertyOptions();
if (options.isArrayAltText())
options.setArrayAlternate(true);
if (options.isArrayAlternate())
options.setArrayOrdered(true);
if (options.isArrayOrdered())
options.setArray(true);
if (options.isCompositeProperty() && itemValue != null && itemValue.toString().length() > 0)
throw new XMPException("Structs and arrays can't have values", 103);
options.assertConsistency(options.getOptions());
return options;
}
static String serializeNodeValue(Object value) {
String strValue;
if (value == null) {
strValue = null;
} else if (value instanceof Boolean) {
strValue = XMPUtils.convertFromBoolean(((Boolean)value).booleanValue());
} else if (value instanceof Integer) {
strValue = XMPUtils.convertFromInteger(((Integer)value).intValue());
} else if (value instanceof Long) {
strValue = XMPUtils.convertFromLong(((Long)value).longValue());
} else if (value instanceof Double) {
strValue = XMPUtils.convertFromDouble(((Double)value).doubleValue());
} else if (value instanceof XMPDateTime) {
strValue = XMPUtils.convertFromDate((XMPDateTime)value);
} else if (value instanceof GregorianCalendar) {
XMPDateTime dt = XMPDateTimeFactory.createFromCalendar((GregorianCalendar)value);
strValue = XMPUtils.convertFromDate(dt);
} else if (value instanceof byte[]) {
strValue = XMPUtils.encodeBase64((byte[])value);
} else {
strValue = value.toString();
}
return (strValue != null) ? Utils.removeControlChars(strValue) : null;
}
private static XMPNode followXPathStep(XMPNode parentNode, XMPPathSegment nextStep, boolean createNodes) throws XMPException {
XMPNode nextNode = null;
int index = 0;
int stepKind = nextStep.getKind();
if (stepKind == 1) {
nextNode = findChildNode(parentNode, nextStep.getName(), createNodes);
} else if (stepKind == 2) {
nextNode = findQualifierNode(parentNode,
nextStep.getName().substring(1), createNodes);
} else {
if (!parentNode.getOptions().isArray())
throw new XMPException("Indexing applied to non-array", 102);
if (stepKind == 3) {
index = findIndexedItem(parentNode, nextStep.getName(), createNodes);
} else if (stepKind == 4) {
index = parentNode.getChildrenLength();
} else if (stepKind == 6) {
String[] result = Utils.splitNameAndValue(nextStep.getName());
String fieldName = result[0];
String fieldValue = result[1];
index = lookupFieldSelector(parentNode, fieldName, fieldValue);
} else if (stepKind == 5) {
String[] result = Utils.splitNameAndValue(nextStep.getName());
String qualName = result[0];
String qualValue = result[1];
index = lookupQualSelector(parentNode, qualName, qualValue,
nextStep.getAliasForm());
} else {
throw new XMPException("Unknown array indexing step in FollowXPathStep", 9);
}
if (1 <= index && index <= parentNode.getChildrenLength())
nextNode = parentNode.getChild(index);
}
return nextNode;
}
private static XMPNode findQualifierNode(XMPNode parent, String qualName, boolean createNodes) throws XMPException {
assert !qualName.startsWith("?");
XMPNode qualNode = parent.findQualifierByName(qualName);
if (qualNode == null && createNodes) {
qualNode = new XMPNode(qualName, null);
qualNode.setImplicit(true);
parent.addQualifier(qualNode);
}
return qualNode;
}
private static int findIndexedItem(XMPNode arrayNode, String segment, boolean createNodes) throws XMPException {
int index = 0;
try {
segment = segment.substring(1, segment.length() - 1);
index = Integer.parseInt(segment);
if (index < 1)
throw new XMPException("Array index must be larger than zero", 102);
} catch (NumberFormatException e) {
throw new XMPException("Array index not digits.", 102);
}
if (createNodes && index == arrayNode.getChildrenLength() + 1) {
XMPNode newItem = new XMPNode("[]", null);
newItem.setImplicit(true);
arrayNode.addChild(newItem);
}
return index;
}
private static int lookupFieldSelector(XMPNode arrayNode, String fieldName, String fieldValue) throws XMPException {
int result = -1;
for (int index = 1; index <= arrayNode.getChildrenLength() && result < 0; index++) {
XMPNode currItem = arrayNode.getChild(index);
if (!currItem.getOptions().isStruct())
throw new XMPException("Field selector must be used on array of struct", 102);
for (int f = 1; f <= currItem.getChildrenLength(); f++) {
XMPNode currField = currItem.getChild(f);
if (fieldName.equals(currField.getName()))
if (fieldValue.equals(currField.getValue())) {
result = index;
break;
}
}
}
return result;
}
private static int lookupQualSelector(XMPNode arrayNode, String qualName, String qualValue, int aliasForm) throws XMPException {
if ("xml:lang".equals(qualName)) {
qualValue = Utils.normalizeLangValue(qualValue);
int i = lookupLanguageItem(arrayNode, qualValue);
if (i < 0 && (aliasForm & 0x1000) > 0) {
XMPNode langNode = new XMPNode("[]", null);
XMPNode xdefault = new XMPNode("xml:lang", "x-default", null);
langNode.addQualifier(xdefault);
arrayNode.addChild(1, langNode);
return 1;
}
return i;
}
for (int index = 1; index < arrayNode.getChildrenLength(); index++) {
XMPNode currItem = arrayNode.getChild(index);
for (Iterator<XMPNode> it = currItem.iterateQualifier(); it.hasNext(); ) {
XMPNode qualifier = it.next();
if (qualName.equals(qualifier.getName()) &&
qualValue.equals(qualifier.getValue()))
return index;
}
}
return -1;
}
static void normalizeLangArray(XMPNode arrayNode) {
if (!arrayNode.getOptions().isArrayAltText())
return;
for (int i = 2; i <= arrayNode.getChildrenLength(); i++) {
XMPNode child = arrayNode.getChild(i);
if (child.hasQualifier() && "x-default".equals(child.getQualifier(1).getValue())) {
try {
arrayNode.removeChild(i);
arrayNode.addChild(1, child);
} catch (XMPException e) {
assert false;
}
if (i == 2) {
arrayNode.getChild(2).setValue(child.getValue());
break;
}
break;
}
}
}
static void detectAltText(XMPNode arrayNode) {
if (arrayNode.getOptions().isArrayAlternate() && arrayNode.hasChildren()) {
boolean isAltText = false;
for (Iterator<XMPNode> it = arrayNode.iterateChildren(); it.hasNext(); ) {
XMPNode child = it.next();
if (child.getOptions().getHasLanguage()) {
isAltText = true;
break;
}
}
if (isAltText) {
arrayNode.getOptions().setArrayAltText(true);
normalizeLangArray(arrayNode);
}
}
}
static void appendLangItem(XMPNode arrayNode, String itemLang, String itemValue) throws XMPException {
XMPNode newItem = new XMPNode("[]", itemValue, null);
XMPNode langQual = new XMPNode("xml:lang", itemLang, null);
newItem.addQualifier(langQual);
if (!"x-default".equals(langQual.getValue())) {
arrayNode.addChild(newItem);
} else {
arrayNode.addChild(1, newItem);
}
}
static Object[] chooseLocalizedText(XMPNode arrayNode, String genericLang, String specificLang) throws XMPException {
if (!arrayNode.getOptions().isArrayAltText())
throw new XMPException("Localized text array is not alt-text", 102);
if (!arrayNode.hasChildren())
return new Object[] { new Integer(0), null };
int foundGenericMatches = 0;
XMPNode resultNode = null;
XMPNode xDefault = null;
for (Iterator<XMPNode> it = arrayNode.iterateChildren(); it.hasNext(); ) {
XMPNode currItem = it.next();
if (currItem.getOptions().isCompositeProperty())
throw new XMPException("Alt-text array item is not simple", 102);
if (!currItem.hasQualifier() ||
!"xml:lang".equals(currItem.getQualifier(1).getName()))
throw new XMPException("Alt-text array item has no language qualifier", 102);
String currLang = currItem.getQualifier(1).getValue();
if (specificLang.equals(currLang))
return new Object[] { new Integer(1), currItem };
if (genericLang != null && currLang.startsWith(genericLang)) {
if (resultNode == null)
resultNode = currItem;
foundGenericMatches++;
continue;
}
if ("x-default".equals(currLang))
xDefault = currItem;
}
if (foundGenericMatches == 1)
return new Object[] { new Integer(2), resultNode };
if (foundGenericMatches > 1)
return new Object[] { new Integer(3), resultNode };
if (xDefault != null)
return new Object[] { new Integer(4), xDefault };
return new Object[] { new Integer(5), arrayNode.getChild(1) };
}
static int lookupLanguageItem(XMPNode arrayNode, String language) throws XMPException {
if (!arrayNode.getOptions().isArray())
throw new XMPException("Language item must be used on array", 102);
for (int index = 1; index <= arrayNode.getChildrenLength(); index++) {
XMPNode child = arrayNode.getChild(index);
if (child.hasQualifier() && "xml:lang".equals(child.getQualifier(1).getName()))
if (language.equals(child.getQualifier(1).getValue()))
return index;
}
return -1;
}
}

View file

@ -0,0 +1,317 @@
package com.itextpdf.xmp.impl;
import com.itextpdf.xmp.XMPDateTime;
import com.itextpdf.xmp.XMPException;
import com.itextpdf.xmp.XMPMeta;
import com.itextpdf.xmp.XMPMetaFactory;
import com.itextpdf.xmp.XMPUtils;
import com.itextpdf.xmp.impl.xpath.XMPPath;
import com.itextpdf.xmp.impl.xpath.XMPPathParser;
import com.itextpdf.xmp.options.ParseOptions;
import com.itextpdf.xmp.options.PropertyOptions;
import com.itextpdf.xmp.properties.XMPAliasInfo;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class XMPNormalizer {
private static Map dcArrayForms;
static {
initDCArrays();
}
static XMPMeta process(XMPMetaImpl xmp, ParseOptions options) throws XMPException {
XMPNode tree = xmp.getRoot();
touchUpDataModel(xmp);
moveExplicitAliases(tree, options);
tweakOldXMP(tree);
deleteEmptySchemas(tree);
return xmp;
}
private static void tweakOldXMP(XMPNode tree) throws XMPException {
if (tree.getName() != null && tree.getName().length() >= 36) {
String nameStr = tree.getName().toLowerCase();
if (nameStr.startsWith("uuid:"))
nameStr = nameStr.substring(5);
if (Utils.checkUUIDFormat(nameStr)) {
XMPPath path = XMPPathParser.expandXPath("http://ns.adobe.com/xap/1.0/mm/", "InstanceID");
XMPNode idNode = XMPNodeUtils.findNode(tree, path, true, null);
if (idNode != null) {
idNode.setOptions(null);
idNode.setValue("uuid:" + nameStr);
idNode.removeChildren();
idNode.removeQualifiers();
tree.setName(null);
} else {
throw new XMPException("Failure creating xmpMM:InstanceID", 9);
}
}
}
}
private static void touchUpDataModel(XMPMetaImpl xmp) throws XMPException {
XMPNodeUtils.findSchemaNode(xmp.getRoot(), "http://purl.org/dc/elements/1.1/", true);
for (Iterator<XMPNode> it = xmp.getRoot().iterateChildren(); it.hasNext(); ) {
XMPNode currSchema = it.next();
if ("http://purl.org/dc/elements/1.1/".equals(currSchema.getName())) {
normalizeDCArrays(currSchema);
continue;
}
if ("http://ns.adobe.com/exif/1.0/".equals(currSchema.getName())) {
fixGPSTimeStamp(currSchema);
XMPNode arrayNode = XMPNodeUtils.findChildNode(currSchema, "exif:UserComment", false);
if (arrayNode != null)
repairAltText(arrayNode);
continue;
}
if ("http://ns.adobe.com/xmp/1.0/DynamicMedia/".equals(currSchema.getName())) {
XMPNode dmCopyright = XMPNodeUtils.findChildNode(currSchema, "xmpDM:copyright", false);
if (dmCopyright != null)
migrateAudioCopyright(xmp, dmCopyright);
continue;
}
if ("http://ns.adobe.com/xap/1.0/rights/".equals(currSchema.getName())) {
XMPNode arrayNode = XMPNodeUtils.findChildNode(currSchema, "xmpRights:UsageTerms", false);
if (arrayNode != null)
repairAltText(arrayNode);
}
}
}
private static void normalizeDCArrays(XMPNode dcSchema) throws XMPException {
for (int i = 1; i <= dcSchema.getChildrenLength(); i++) {
XMPNode currProp = dcSchema.getChild(i);
PropertyOptions arrayForm = (PropertyOptions)dcArrayForms.get(currProp.getName());
if (arrayForm != null)
if (currProp.getOptions().isSimple()) {
XMPNode newArray = new XMPNode(currProp.getName(), arrayForm);
currProp.setName("[]");
newArray.addChild(currProp);
dcSchema.replaceChild(i, newArray);
if (arrayForm.isArrayAltText() && !currProp.getOptions().getHasLanguage()) {
XMPNode newLang = new XMPNode("xml:lang", "x-default", null);
currProp.addQualifier(newLang);
}
} else {
currProp.getOptions().setOption(7680, false);
currProp.getOptions().mergeWith(arrayForm);
if (arrayForm.isArrayAltText())
repairAltText(currProp);
}
}
}
private static void repairAltText(XMPNode arrayNode) throws XMPException {
if (arrayNode == null ||
!arrayNode.getOptions().isArray())
return;
arrayNode.getOptions().setArrayOrdered(true).setArrayAlternate(true).setArrayAltText(true);
for (Iterator<XMPNode> it = arrayNode.iterateChildren(); it.hasNext(); ) {
XMPNode currChild = it.next();
if (currChild.getOptions().isCompositeProperty()) {
it.remove();
continue;
}
if (!currChild.getOptions().getHasLanguage()) {
String childValue = currChild.getValue();
if (childValue == null || childValue.length() == 0) {
it.remove();
continue;
}
XMPNode repairLang = new XMPNode("xml:lang", "x-repair", null);
currChild.addQualifier(repairLang);
}
}
}
private static void moveExplicitAliases(XMPNode tree, ParseOptions options) throws XMPException {
if (!tree.getHasAliases())
return;
tree.setHasAliases(false);
boolean strictAliasing = options.getStrictAliasing();
for (XMPNode currSchema : (Iterable<XMPNode>)tree.getUnmodifiableChildren()) {
if (!currSchema.getHasAliases())
continue;
for (Iterator<XMPNode> propertyIt = currSchema.iterateChildren(); propertyIt.hasNext(); ) {
XMPNode currProp = propertyIt.next();
if (!currProp.isAlias())
continue;
currProp.setAlias(false);
XMPAliasInfo info = XMPMetaFactory.getSchemaRegistry()
.findAlias(currProp.getName());
if (info != null) {
XMPNode baseSchema = XMPNodeUtils.findSchemaNode(tree,
info.getNamespace(), null, true);
baseSchema.setImplicit(false);
XMPNode baseNode = XMPNodeUtils.findChildNode(baseSchema,
info.getPrefix() + info.getPropName(), false);
if (baseNode == null) {
if (info.getAliasForm().isSimple()) {
String qname = info.getPrefix() + info.getPropName();
currProp.setName(qname);
baseSchema.addChild(currProp);
propertyIt.remove();
continue;
}
baseNode = new XMPNode(info.getPrefix() + info.getPropName(), info.getAliasForm().toPropertyOptions());
baseSchema.addChild(baseNode);
transplantArrayItemAlias(propertyIt, currProp, baseNode);
continue;
}
if (info.getAliasForm().isSimple()) {
if (strictAliasing)
compareAliasedSubtrees(currProp, baseNode, true);
propertyIt.remove();
continue;
}
XMPNode itemNode = null;
if (info.getAliasForm().isArrayAltText()) {
int xdIndex = XMPNodeUtils.lookupLanguageItem(baseNode, "x-default");
if (xdIndex != -1)
itemNode = baseNode.getChild(xdIndex);
} else if (baseNode.hasChildren()) {
itemNode = baseNode.getChild(1);
}
if (itemNode == null) {
transplantArrayItemAlias(propertyIt, currProp, baseNode);
continue;
}
if (strictAliasing)
compareAliasedSubtrees(currProp, itemNode, true);
propertyIt.remove();
}
}
currSchema.setHasAliases(false);
}
}
private static void transplantArrayItemAlias(Iterator propertyIt, XMPNode childNode, XMPNode baseArray) throws XMPException {
if (baseArray.getOptions().isArrayAltText()) {
if (childNode.getOptions().getHasLanguage())
throw new XMPException("Alias to x-default already has a language qualifier", 203);
XMPNode langQual = new XMPNode("xml:lang", "x-default", null);
childNode.addQualifier(langQual);
}
propertyIt.remove();
childNode.setName("[]");
baseArray.addChild(childNode);
}
private static void fixGPSTimeStamp(XMPNode exifSchema) throws XMPException {
XMPNode gpsDateTime = XMPNodeUtils.findChildNode(exifSchema, "exif:GPSTimeStamp", false);
if (gpsDateTime == null)
return;
try {
XMPDateTime binGPSStamp = XMPUtils.convertToDate(gpsDateTime.getValue());
if (binGPSStamp.getYear() != 0 ||
binGPSStamp.getMonth() != 0 ||
binGPSStamp.getDay() != 0)
return;
XMPNode otherDate = XMPNodeUtils.findChildNode(exifSchema, "exif:DateTimeOriginal", false);
if (otherDate == null)
otherDate = XMPNodeUtils.findChildNode(exifSchema, "exif:DateTimeDigitized", false);
XMPDateTime binOtherDate = XMPUtils.convertToDate(otherDate.getValue());
Calendar cal = binGPSStamp.getCalendar();
cal.set(1, binOtherDate.getYear());
cal.set(2, binOtherDate.getMonth());
cal.set(5, binOtherDate.getDay());
binGPSStamp = new XMPDateTimeImpl(cal);
gpsDateTime.setValue(XMPUtils.convertFromDate(binGPSStamp));
} catch (XMPException e) {
return;
}
}
private static void deleteEmptySchemas(XMPNode tree) {
for (Iterator<XMPNode> it = tree.iterateChildren(); it.hasNext(); ) {
XMPNode schema = it.next();
if (!schema.hasChildren())
it.remove();
}
}
private static void compareAliasedSubtrees(XMPNode aliasNode, XMPNode baseNode, boolean outerCall) throws XMPException {
if (!aliasNode.getValue().equals(baseNode.getValue()) ||
aliasNode.getChildrenLength() != baseNode.getChildrenLength())
throw new XMPException("Mismatch between alias and base nodes", 203);
if (!outerCall && (
!aliasNode.getName().equals(baseNode.getName()) ||
!aliasNode.getOptions().equals(baseNode.getOptions()) ||
aliasNode.getQualifierLength() != baseNode.getQualifierLength()))
throw new XMPException("Mismatch between alias and base nodes", 203);
Iterator<XMPNode> iterator1 = aliasNode.iterateChildren();
Iterator<XMPNode> iterator2 = baseNode.iterateChildren();
while (iterator1.hasNext() && iterator2.hasNext()) {
XMPNode aliasChild = iterator1.next();
XMPNode baseChild = iterator2.next();
compareAliasedSubtrees(aliasChild, baseChild, false);
}
Iterator<XMPNode> an = aliasNode.iterateQualifier();
Iterator<XMPNode> bn = baseNode.iterateQualifier();
while (an.hasNext() && bn.hasNext()) {
XMPNode aliasQual = an.next();
XMPNode baseQual = bn.next();
compareAliasedSubtrees(aliasQual, baseQual, false);
}
}
private static void migrateAudioCopyright(XMPMeta xmp, XMPNode dmCopyright) {
try {
XMPNode dcSchema = XMPNodeUtils.findSchemaNode(((XMPMetaImpl)xmp)
.getRoot(), "http://purl.org/dc/elements/1.1/", true);
String dmValue = dmCopyright.getValue();
String doubleLF = "\n\n";
XMPNode dcRightsArray = XMPNodeUtils.findChildNode(dcSchema, "dc:rights", false);
if (dcRightsArray == null || !dcRightsArray.hasChildren()) {
dmValue = doubleLF + dmValue;
xmp.setLocalizedText("http://purl.org/dc/elements/1.1/", "rights", "", "x-default", dmValue, null);
} else {
int xdIndex = XMPNodeUtils.lookupLanguageItem(dcRightsArray, "x-default");
if (xdIndex < 0) {
String firstValue = dcRightsArray.getChild(1).getValue();
xmp.setLocalizedText("http://purl.org/dc/elements/1.1/", "rights", "", "x-default", firstValue, null);
xdIndex = XMPNodeUtils.lookupLanguageItem(dcRightsArray, "x-default");
}
XMPNode defaultNode = dcRightsArray.getChild(xdIndex);
String defaultValue = defaultNode.getValue();
int lfPos = defaultValue.indexOf(doubleLF);
if (lfPos < 0) {
if (!dmValue.equals(defaultValue))
defaultNode.setValue(defaultValue + doubleLF + dmValue);
} else if (!defaultValue.substring(lfPos + 2).equals(dmValue)) {
defaultNode.setValue(defaultValue.substring(0, lfPos + 2) + dmValue);
}
}
dmCopyright.getParent().removeChild(dmCopyright);
} catch (XMPException e) {}
}
private static void initDCArrays() {
dcArrayForms = new HashMap();
PropertyOptions bagForm = new PropertyOptions();
bagForm.setArray(true);
dcArrayForms.put("dc:contributor", bagForm);
dcArrayForms.put("dc:language", bagForm);
dcArrayForms.put("dc:publisher", bagForm);
dcArrayForms.put("dc:relation", bagForm);
dcArrayForms.put("dc:subject", bagForm);
dcArrayForms.put("dc:type", bagForm);
PropertyOptions seqForm = new PropertyOptions();
seqForm.setArray(true);
seqForm.setArrayOrdered(true);
dcArrayForms.put("dc:creator", seqForm);
dcArrayForms.put("dc:date", seqForm);
PropertyOptions altTextForm = new PropertyOptions();
altTextForm.setArray(true);
altTextForm.setArrayOrdered(true);
altTextForm.setArrayAlternate(true);
altTextForm.setArrayAltText(true);
dcArrayForms.put("dc:description", altTextForm);
dcArrayForms.put("dc:rights", altTextForm);
dcArrayForms.put("dc:title", altTextForm);
}
}

View file

@ -0,0 +1,233 @@
package com.itextpdf.xmp.impl;
import com.itextpdf.xmp.XMPConst;
import com.itextpdf.xmp.XMPException;
import com.itextpdf.xmp.XMPSchemaRegistry;
import com.itextpdf.xmp.options.AliasOptions;
import com.itextpdf.xmp.properties.XMPAliasInfo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;
public final class XMPSchemaRegistryImpl implements XMPSchemaRegistry, XMPConst {
private Map namespaceToPrefixMap = new HashMap();
private Map prefixToNamespaceMap = new HashMap();
private Map aliasMap = new HashMap();
private Pattern p = Pattern.compile("[/*?\\[\\]]");
public XMPSchemaRegistryImpl() {
try {
registerStandardNamespaces();
registerStandardAliases();
} catch (XMPException e) {
throw new RuntimeException("The XMPSchemaRegistry cannot be initialized!");
}
}
public synchronized String registerNamespace(String namespaceURI, String suggestedPrefix) throws XMPException {
ParameterAsserts.assertSchemaNS(namespaceURI);
ParameterAsserts.assertPrefix(suggestedPrefix);
if (suggestedPrefix.charAt(suggestedPrefix.length() - 1) != ':')
suggestedPrefix = suggestedPrefix + ':';
if (!Utils.isXMLNameNS(suggestedPrefix.substring(0,
suggestedPrefix.length() - 1)))
throw new XMPException("The prefix is a bad XML name", 201);
String registeredPrefix = (String)this.namespaceToPrefixMap.get(namespaceURI);
String registeredNS = (String)this.prefixToNamespaceMap.get(suggestedPrefix);
if (registeredPrefix != null)
return registeredPrefix;
if (registeredNS != null) {
String generatedPrefix = suggestedPrefix;
for (int i = 1; this.prefixToNamespaceMap.containsKey(generatedPrefix); i++)
generatedPrefix = suggestedPrefix.substring(0, suggestedPrefix.length() - 1) + "_" + i + "_:";
suggestedPrefix = generatedPrefix;
}
this.prefixToNamespaceMap.put(suggestedPrefix, namespaceURI);
this.namespaceToPrefixMap.put(namespaceURI, suggestedPrefix);
return suggestedPrefix;
}
public synchronized void deleteNamespace(String namespaceURI) {
String prefixToDelete = getNamespacePrefix(namespaceURI);
if (prefixToDelete != null) {
this.namespaceToPrefixMap.remove(namespaceURI);
this.prefixToNamespaceMap.remove(prefixToDelete);
}
}
public synchronized String getNamespacePrefix(String namespaceURI) {
return (String)this.namespaceToPrefixMap.get(namespaceURI);
}
public synchronized String getNamespaceURI(String namespacePrefix) {
if (namespacePrefix != null && !namespacePrefix.endsWith(":"))
namespacePrefix = namespacePrefix + ":";
return (String)this.prefixToNamespaceMap.get(namespacePrefix);
}
public synchronized Map getNamespaces() {
return Collections.unmodifiableMap(new TreeMap(this.namespaceToPrefixMap));
}
public synchronized Map getPrefixes() {
return Collections.unmodifiableMap(new TreeMap(this.prefixToNamespaceMap));
}
private void registerStandardNamespaces() throws XMPException {
registerNamespace("http://www.w3.org/XML/1998/namespace", "xml");
registerNamespace("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "rdf");
registerNamespace("http://purl.org/dc/elements/1.1/", "dc");
registerNamespace("http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/", "Iptc4xmpCore");
registerNamespace("http://iptc.org/std/Iptc4xmpExt/2008-02-29/", "Iptc4xmpExt");
registerNamespace("http://ns.adobe.com/DICOM/", "DICOM");
registerNamespace("http://ns.useplus.org/ldf/xmp/1.0/", "plus");
registerNamespace("adobe:ns:meta/", "x");
registerNamespace("http://ns.adobe.com/iX/1.0/", "iX");
registerNamespace("http://ns.adobe.com/xap/1.0/", "xmp");
registerNamespace("http://ns.adobe.com/xap/1.0/rights/", "xmpRights");
registerNamespace("http://ns.adobe.com/xap/1.0/mm/", "xmpMM");
registerNamespace("http://ns.adobe.com/xap/1.0/bj/", "xmpBJ");
registerNamespace("http://ns.adobe.com/xmp/note/", "xmpNote");
registerNamespace("http://ns.adobe.com/pdf/1.3/", "pdf");
registerNamespace("http://ns.adobe.com/pdfx/1.3/", "pdfx");
registerNamespace("http://www.npes.org/pdfx/ns/id/", "pdfxid");
registerNamespace("http://www.aiim.org/pdfa/ns/schema#", "pdfaSchema");
registerNamespace("http://www.aiim.org/pdfa/ns/property#", "pdfaProperty");
registerNamespace("http://www.aiim.org/pdfa/ns/type#", "pdfaType");
registerNamespace("http://www.aiim.org/pdfa/ns/field#", "pdfaField");
registerNamespace("http://www.aiim.org/pdfa/ns/id/", "pdfaid");
registerNamespace("http://www.aiim.org/pdfua/ns/id/", "pdfuaid");
registerNamespace("http://www.aiim.org/pdfa/ns/extension/", "pdfaExtension");
registerNamespace("http://ns.adobe.com/photoshop/1.0/", "photoshop");
registerNamespace("http://ns.adobe.com/album/1.0/", "album");
registerNamespace("http://ns.adobe.com/exif/1.0/", "exif");
registerNamespace("http://cipa.jp/exif/1.0/", "exifEX");
registerNamespace("http://ns.adobe.com/exif/1.0/aux/", "aux");
registerNamespace("http://ns.adobe.com/tiff/1.0/", "tiff");
registerNamespace("http://ns.adobe.com/png/1.0/", "png");
registerNamespace("http://ns.adobe.com/jpeg/1.0/", "jpeg");
registerNamespace("http://ns.adobe.com/jp2k/1.0/", "jp2k");
registerNamespace("http://ns.adobe.com/camera-raw-settings/1.0/", "crs");
registerNamespace("http://ns.adobe.com/StockPhoto/1.0/", "bmsp");
registerNamespace("http://ns.adobe.com/creatorAtom/1.0/", "creatorAtom");
registerNamespace("http://ns.adobe.com/asf/1.0/", "asf");
registerNamespace("http://ns.adobe.com/xmp/wav/1.0/", "wav");
registerNamespace("http://ns.adobe.com/bwf/bext/1.0/", "bext");
registerNamespace("http://ns.adobe.com/riff/info/", "riffinfo");
registerNamespace("http://ns.adobe.com/xmp/1.0/Script/", "xmpScript");
registerNamespace("http://ns.adobe.com/TransformXMP/", "txmp");
registerNamespace("http://ns.adobe.com/swf/1.0/", "swf");
registerNamespace("http://ns.adobe.com/xmp/1.0/DynamicMedia/", "xmpDM");
registerNamespace("http://ns.adobe.com/xmp/transient/1.0/", "xmpx");
registerNamespace("http://ns.adobe.com/xap/1.0/t/", "xmpT");
registerNamespace("http://ns.adobe.com/xap/1.0/t/pg/", "xmpTPg");
registerNamespace("http://ns.adobe.com/xap/1.0/g/", "xmpG");
registerNamespace("http://ns.adobe.com/xap/1.0/g/img/", "xmpGImg");
registerNamespace("http://ns.adobe.com/xap/1.0/sType/Font#", "stFnt");
registerNamespace("http://ns.adobe.com/xap/1.0/sType/Dimensions#", "stDim");
registerNamespace("http://ns.adobe.com/xap/1.0/sType/ResourceEvent#", "stEvt");
registerNamespace("http://ns.adobe.com/xap/1.0/sType/ResourceRef#", "stRef");
registerNamespace("http://ns.adobe.com/xap/1.0/sType/Version#", "stVer");
registerNamespace("http://ns.adobe.com/xap/1.0/sType/Job#", "stJob");
registerNamespace("http://ns.adobe.com/xap/1.0/sType/ManifestItem#", "stMfs");
registerNamespace("http://ns.adobe.com/xmp/Identifier/qual/1.0/", "xmpidq");
}
public synchronized XMPAliasInfo resolveAlias(String aliasNS, String aliasProp) {
String aliasPrefix = getNamespacePrefix(aliasNS);
if (aliasPrefix == null)
return null;
return (XMPAliasInfo)this.aliasMap.get(aliasPrefix + aliasProp);
}
public synchronized XMPAliasInfo findAlias(String qname) {
return (XMPAliasInfo)this.aliasMap.get(qname);
}
public synchronized XMPAliasInfo[] findAliases(String aliasNS) {
String prefix = getNamespacePrefix(aliasNS);
List<XMPAliasInfo> result = new ArrayList();
if (prefix != null)
for (String qname : (Iterable<String>)this.aliasMap.keySet()) {
if (qname.startsWith(prefix))
result.add(findAlias(qname));
}
return result.<XMPAliasInfo>toArray(new XMPAliasInfo[result.size()]);
}
synchronized void registerAlias(String aliasNS, String aliasProp, String actualNS, String actualProp, AliasOptions aliasForm) throws XMPException {
assert false : "Decompilation failed at line #367 -> offsets [0]";
assert false : "Decompilation failed at line #368 -> offsets [4]";
assert false : "Decompilation failed at line #369 -> offsets [8]";
assert false : "Decompilation failed at line #370 -> offsets [12]";
assert false : "Decompilation failed at line #373 -> offsets [17]";
assert false : "Decompilation failed at line #374 -> offsets [32]";
assert false : "Decompilation failed at line #375 -> offsets [28, 35]";
assert false : "Decompilation failed at line #378 -> offsets [53]";
assert false : "Decompilation failed at line #380 -> offsets [82]";
assert false : "Decompilation failed at line #385 -> offsets [94]";
assert false : "Decompilation failed at line #386 -> offsets [101]";
assert false : "Decompilation failed at line #387 -> offsets [108]";
assert false : "Decompilation failed at line #389 -> offsets [113]";
assert false : "Decompilation failed at line #391 -> offsets [125]";
assert false : "Decompilation failed at line #393 -> offsets [130]";
assert false : "Decompilation failed at line #397 -> offsets [142]";
assert false : "Decompilation failed at line #400 -> offsets [163]";
assert false : "Decompilation failed at line #402 -> offsets [177]";
assert false : "Decompilation failed at line #404 -> offsets [188]";
assert false : "Decompilation failed at line #406 -> offsets [220]";
assert false : "Decompilation failed at line #411 -> offsets [231]";
assert false : "Decompilation failed at line #452 -> offsets [248]";
assert false : "Decompilation failed at line #453 -> offsets [262]";
}
public synchronized Map getAliases() {
return Collections.unmodifiableMap(new TreeMap(this.aliasMap));
}
private void registerStandardAliases() throws XMPException {
AliasOptions aliasToArrayOrdered = new AliasOptions().setArrayOrdered(true);
AliasOptions aliasToArrayAltText = new AliasOptions().setArrayAltText(true);
registerAlias("http://ns.adobe.com/xap/1.0/", "Author", "http://purl.org/dc/elements/1.1/", "creator", aliasToArrayOrdered);
registerAlias("http://ns.adobe.com/xap/1.0/", "Authors", "http://purl.org/dc/elements/1.1/", "creator", null);
registerAlias("http://ns.adobe.com/xap/1.0/", "Description", "http://purl.org/dc/elements/1.1/", "description", null);
registerAlias("http://ns.adobe.com/xap/1.0/", "Format", "http://purl.org/dc/elements/1.1/", "format", null);
registerAlias("http://ns.adobe.com/xap/1.0/", "Keywords", "http://purl.org/dc/elements/1.1/", "subject", null);
registerAlias("http://ns.adobe.com/xap/1.0/", "Locale", "http://purl.org/dc/elements/1.1/", "language", null);
registerAlias("http://ns.adobe.com/xap/1.0/", "Title", "http://purl.org/dc/elements/1.1/", "title", null);
registerAlias("http://ns.adobe.com/xap/1.0/rights/", "Copyright", "http://purl.org/dc/elements/1.1/", "rights", null);
registerAlias("http://ns.adobe.com/pdf/1.3/", "Author", "http://purl.org/dc/elements/1.1/", "creator", aliasToArrayOrdered);
registerAlias("http://ns.adobe.com/pdf/1.3/", "BaseURL", "http://ns.adobe.com/xap/1.0/", "BaseURL", null);
registerAlias("http://ns.adobe.com/pdf/1.3/", "CreationDate", "http://ns.adobe.com/xap/1.0/", "CreateDate", null);
registerAlias("http://ns.adobe.com/pdf/1.3/", "Creator", "http://ns.adobe.com/xap/1.0/", "CreatorTool", null);
registerAlias("http://ns.adobe.com/pdf/1.3/", "ModDate", "http://ns.adobe.com/xap/1.0/", "ModifyDate", null);
registerAlias("http://ns.adobe.com/pdf/1.3/", "Subject", "http://purl.org/dc/elements/1.1/", "description", aliasToArrayAltText);
registerAlias("http://ns.adobe.com/pdf/1.3/", "Title", "http://purl.org/dc/elements/1.1/", "title", aliasToArrayAltText);
registerAlias("http://ns.adobe.com/photoshop/1.0/", "Author", "http://purl.org/dc/elements/1.1/", "creator", aliasToArrayOrdered);
registerAlias("http://ns.adobe.com/photoshop/1.0/", "Caption", "http://purl.org/dc/elements/1.1/", "description", aliasToArrayAltText);
registerAlias("http://ns.adobe.com/photoshop/1.0/", "Copyright", "http://purl.org/dc/elements/1.1/", "rights", aliasToArrayAltText);
registerAlias("http://ns.adobe.com/photoshop/1.0/", "Keywords", "http://purl.org/dc/elements/1.1/", "subject", null);
registerAlias("http://ns.adobe.com/photoshop/1.0/", "Marked", "http://ns.adobe.com/xap/1.0/rights/", "Marked", null);
registerAlias("http://ns.adobe.com/photoshop/1.0/", "Title", "http://purl.org/dc/elements/1.1/", "title", aliasToArrayAltText);
registerAlias("http://ns.adobe.com/photoshop/1.0/", "WebStatement", "http://ns.adobe.com/xap/1.0/rights/", "WebStatement", null);
registerAlias("http://ns.adobe.com/tiff/1.0/", "Artist", "http://purl.org/dc/elements/1.1/", "creator", aliasToArrayOrdered);
registerAlias("http://ns.adobe.com/tiff/1.0/", "Copyright", "http://purl.org/dc/elements/1.1/", "rights", null);
registerAlias("http://ns.adobe.com/tiff/1.0/", "DateTime", "http://ns.adobe.com/xap/1.0/", "ModifyDate", null);
registerAlias("http://ns.adobe.com/tiff/1.0/", "ImageDescription", "http://purl.org/dc/elements/1.1/", "description", null);
registerAlias("http://ns.adobe.com/tiff/1.0/", "Software", "http://ns.adobe.com/xap/1.0/", "CreatorTool", null);
registerAlias("http://ns.adobe.com/png/1.0/", "Author", "http://purl.org/dc/elements/1.1/", "creator", aliasToArrayOrdered);
registerAlias("http://ns.adobe.com/png/1.0/", "Copyright", "http://purl.org/dc/elements/1.1/", "rights", aliasToArrayAltText);
registerAlias("http://ns.adobe.com/png/1.0/", "CreationTime", "http://ns.adobe.com/xap/1.0/", "CreateDate", null);
registerAlias("http://ns.adobe.com/png/1.0/", "Description", "http://purl.org/dc/elements/1.1/", "description", aliasToArrayAltText);
registerAlias("http://ns.adobe.com/png/1.0/", "ModificationTime", "http://ns.adobe.com/xap/1.0/", "ModifyDate", null);
registerAlias("http://ns.adobe.com/png/1.0/", "Software", "http://ns.adobe.com/xap/1.0/", "CreatorTool", null);
registerAlias("http://ns.adobe.com/png/1.0/", "Title", "http://purl.org/dc/elements/1.1/", "title", aliasToArrayAltText);
}
}

View file

@ -0,0 +1,34 @@
package com.itextpdf.xmp.impl;
import com.itextpdf.xmp.XMPException;
import com.itextpdf.xmp.options.SerializeOptions;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
public class XMPSerializerHelper {
public static void serialize(XMPMetaImpl xmp, OutputStream out, SerializeOptions options) throws XMPException {
options = (options != null) ? options : new SerializeOptions();
if (options.getSort())
xmp.sort();
new XMPSerializerRDF().serialize(xmp, out, options);
}
public static String serializeToString(XMPMetaImpl xmp, SerializeOptions options) throws XMPException {
options = (options != null) ? options : new SerializeOptions();
options.setEncodeUTF16BE(true);
ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
serialize(xmp, out, options);
try {
return out.toString(options.getEncoding());
} catch (UnsupportedEncodingException e) {
return out.toString();
}
}
public static byte[] serializeToBuffer(XMPMetaImpl xmp, SerializeOptions options) throws XMPException {
ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
serialize(xmp, out, options);
return out.toByteArray();
}
}

View file

@ -0,0 +1,649 @@
package com.itextpdf.xmp.impl;
import com.itextpdf.xmp.XMPException;
import com.itextpdf.xmp.XMPMeta;
import com.itextpdf.xmp.XMPMetaFactory;
import com.itextpdf.xmp.options.SerializeOptions;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class XMPSerializerRDF {
private static final int DEFAULT_PAD = 2048;
private static final String PACKET_HEADER = "<?xpacket begin=\"\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>";
private static final String PACKET_TRAILER = "<?xpacket end=\"";
private static final String PACKET_TRAILER2 = "\"?>";
private static final String RDF_XMPMETA_START = "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"";
private static final String RDF_XMPMETA_END = "</x:xmpmeta>";
private static final String RDF_RDF_START = "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">";
private static final String RDF_RDF_END = "</rdf:RDF>";
private static final String RDF_SCHEMA_START = "<rdf:Description rdf:about=";
private static final String RDF_SCHEMA_END = "</rdf:Description>";
private static final String RDF_STRUCT_START = "<rdf:Description";
private static final String RDF_STRUCT_END = "</rdf:Description>";
private static final String RDF_EMPTY_STRUCT = "<rdf:Description/>";
static final Set RDF_ATTR_QUALIFIER = new HashSet(Arrays.<String>asList("xml:lang", "rdf:resource", "rdf:ID", "rdf:bagID", "rdf:nodeID"));
private XMPMetaImpl xmp;
private CountOutputStream outputStream;
private OutputStreamWriter writer;
private SerializeOptions options;
private int unicodeSize = 1;
private int padding;
public void serialize(XMPMeta xmp, OutputStream out, SerializeOptions options) throws XMPException {
try {
this.outputStream = new CountOutputStream(out);
this.writer = new OutputStreamWriter(this.outputStream, options.getEncoding());
this.xmp = (XMPMetaImpl)xmp;
this.options = options;
this.padding = options.getPadding();
this.writer = new OutputStreamWriter(this.outputStream, options.getEncoding());
checkOptionsConsistence();
String tailStr = serializeAsRDF();
this.writer.flush();
addPadding(tailStr.length());
write(tailStr);
this.writer.flush();
this.outputStream.close();
} catch (IOException e) {
throw new XMPException("Error writing to the OutputStream", 0);
}
}
private void addPadding(int tailLength) throws XMPException, IOException {
if (this.options.getExactPacketLength()) {
int minSize = this.outputStream.getBytesWritten() + tailLength * this.unicodeSize;
if (minSize > this.padding)
throw new XMPException("Can't fit into specified packet size", 107);
this.padding -= minSize;
}
this.padding /= this.unicodeSize;
int newlineLen = this.options.getNewline().length();
if (this.padding >= newlineLen) {
this.padding -= newlineLen;
while (this.padding >= 100 + newlineLen) {
writeChars(100, ' ');
writeNewline();
this.padding -= 100 + newlineLen;
}
writeChars(this.padding, ' ');
writeNewline();
} else {
writeChars(this.padding, ' ');
}
}
protected void checkOptionsConsistence() throws XMPException {
if (this.options.getEncodeUTF16BE() | this.options.getEncodeUTF16LE())
this.unicodeSize = 2;
if (this.options.getExactPacketLength()) {
if (this.options.getOmitPacketWrapper() | this.options.getIncludeThumbnailPad())
throw new XMPException("Inconsistent options for exact size serialize", 103);
if ((this.options.getPadding() & this.unicodeSize - 1) != 0)
throw new XMPException("Exact size must be a multiple of the Unicode element", 103);
} else if (this.options.getReadOnlyPacket()) {
if (this.options.getOmitPacketWrapper() | this.options.getIncludeThumbnailPad())
throw new XMPException("Inconsistent options for read-only packet", 103);
this.padding = 0;
} else if (this.options.getOmitPacketWrapper()) {
if (this.options.getIncludeThumbnailPad())
throw new XMPException("Inconsistent options for non-packet serialize", 103);
this.padding = 0;
} else {
if (this.padding == 0)
this.padding = 2048 * this.unicodeSize;
if (this.options.getIncludeThumbnailPad())
if (!this.xmp.doesPropertyExist("http://ns.adobe.com/xap/1.0/", "Thumbnails"))
this.padding += 10000 * this.unicodeSize;
}
}
private String serializeAsRDF() throws IOException, XMPException {
int level = 0;
if (!this.options.getOmitPacketWrapper()) {
writeIndent(level);
write("<?xpacket begin=\"\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>");
writeNewline();
}
if (!this.options.getOmitXmpMetaElement()) {
writeIndent(level);
write("<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"");
if (!this.options.getOmitVersionAttribute())
write(XMPMetaFactory.getVersionInfo().getMessage());
write("\">");
writeNewline();
level++;
}
writeIndent(level);
write("<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">");
writeNewline();
if (this.options.getUseCanonicalFormat()) {
serializeCanonicalRDFSchemas(level);
} else {
serializeCompactRDFSchemas(level);
}
writeIndent(level);
write("</rdf:RDF>");
writeNewline();
if (!this.options.getOmitXmpMetaElement()) {
level--;
writeIndent(level);
write("</x:xmpmeta>");
writeNewline();
}
String tailStr = "";
if (!this.options.getOmitPacketWrapper()) {
for (int i = this.options.getBaseIndent(); i > 0; i--)
tailStr = tailStr + this.options.getIndent();
tailStr = tailStr + "<?xpacket end=\"";
tailStr = tailStr + (this.options.getReadOnlyPacket() ? 114 : 119);
tailStr = tailStr + "\"?>";
}
return tailStr;
}
private void serializeCanonicalRDFSchemas(int level) throws IOException, XMPException {
if (this.xmp.getRoot().getChildrenLength() > 0) {
startOuterRDFDescription(this.xmp.getRoot(), level);
for (Iterator<XMPNode> it = this.xmp.getRoot().iterateChildren(); it.hasNext(); ) {
XMPNode currSchema = it.next();
serializeCanonicalRDFSchema(currSchema, level);
}
endOuterRDFDescription(level);
} else {
writeIndent(level + 1);
write("<rdf:Description rdf:about=");
writeTreeName();
write("/>");
writeNewline();
}
}
private void writeTreeName() throws IOException {
write(34);
String name = this.xmp.getRoot().getName();
if (name != null)
appendNodeValue(name, true);
write(34);
}
private void serializeCompactRDFSchemas(int level) throws IOException, XMPException {
writeIndent(level + 1);
write("<rdf:Description rdf:about=");
writeTreeName();
Set<String> usedPrefixes = new HashSet();
usedPrefixes.add("xml");
usedPrefixes.add("rdf");
for (Iterator<XMPNode> it = this.xmp.getRoot().iterateChildren(); it.hasNext(); ) {
XMPNode schema = it.next();
declareUsedNamespaces(schema, usedPrefixes, level + 3);
}
boolean allAreAttrs = true;
for (Iterator<XMPNode> iterator2 = this.xmp.getRoot().iterateChildren(); iterator2.hasNext(); ) {
XMPNode schema = iterator2.next();
allAreAttrs &= serializeCompactRDFAttrProps(schema, level + 2);
}
if (!allAreAttrs) {
write(62);
writeNewline();
} else {
write("/>");
writeNewline();
return;
}
for (Iterator<XMPNode> iterator1 = this.xmp.getRoot().iterateChildren(); iterator1.hasNext(); ) {
XMPNode schema = iterator1.next();
serializeCompactRDFElementProps(schema, level + 2);
}
writeIndent(level + 1);
write("</rdf:Description>");
writeNewline();
}
private boolean serializeCompactRDFAttrProps(XMPNode parentNode, int indent) throws IOException {
boolean allAreAttrs = true;
for (Iterator<XMPNode> it = parentNode.iterateChildren(); it.hasNext(); ) {
XMPNode prop = it.next();
if (canBeRDFAttrProp(prop)) {
writeNewline();
writeIndent(indent);
write(prop.getName());
write("=\"");
appendNodeValue(prop.getValue(), true);
write(34);
continue;
}
allAreAttrs = false;
}
return allAreAttrs;
}
private void serializeCompactRDFElementProps(XMPNode parentNode, int indent) throws IOException, XMPException {
for (Iterator<XMPNode> it = parentNode.iterateChildren(); it.hasNext(); ) {
XMPNode node = it.next();
if (canBeRDFAttrProp(node))
continue;
boolean emitEndTag = true;
boolean indentEndTag = true;
String elemName = node.getName();
if ("[]".equals(elemName))
elemName = "rdf:li";
writeIndent(indent);
write(60);
write(elemName);
boolean hasGeneralQualifiers = false;
boolean hasRDFResourceQual = false;
for (Iterator<XMPNode> iq = node.iterateQualifier(); iq.hasNext(); ) {
XMPNode qualifier = iq.next();
if (!RDF_ATTR_QUALIFIER.contains(qualifier.getName())) {
hasGeneralQualifiers = true;
continue;
}
hasRDFResourceQual = "rdf:resource".equals(qualifier.getName());
write(32);
write(qualifier.getName());
write("=\"");
appendNodeValue(qualifier.getValue(), true);
write(34);
}
if (hasGeneralQualifiers) {
serializeCompactRDFGeneralQualifier(indent, node);
} else if (!node.getOptions().isCompositeProperty()) {
Object[] result = serializeCompactRDFSimpleProp(node);
emitEndTag = (Boolean)result[0];
indentEndTag = (Boolean)result[1];
} else if (node.getOptions().isArray()) {
serializeCompactRDFArrayProp(node, indent);
} else {
emitEndTag = serializeCompactRDFStructProp(node, indent, hasRDFResourceQual);
}
if (emitEndTag) {
if (indentEndTag)
writeIndent(indent);
write("</");
write(elemName);
write(62);
writeNewline();
}
}
}
private Object[] serializeCompactRDFSimpleProp(XMPNode node) throws IOException {
Boolean emitEndTag = Boolean.TRUE;
Boolean indentEndTag = Boolean.TRUE;
if (node.getOptions().isURI()) {
write(" rdf:resource=\"");
appendNodeValue(node.getValue(), true);
write("\"/>");
writeNewline();
emitEndTag = Boolean.FALSE;
} else if (node.getValue() == null || node.getValue().length() == 0) {
write("/>");
writeNewline();
emitEndTag = Boolean.FALSE;
} else {
write(62);
appendNodeValue(node.getValue(), false);
indentEndTag = Boolean.FALSE;
}
return new Object[] { emitEndTag, indentEndTag };
}
private void serializeCompactRDFArrayProp(XMPNode node, int indent) throws IOException, XMPException {
write(62);
writeNewline();
emitRDFArrayTag(node, true, indent + 1);
if (node.getOptions().isArrayAltText())
XMPNodeUtils.normalizeLangArray(node);
serializeCompactRDFElementProps(node, indent + 2);
emitRDFArrayTag(node, false, indent + 1);
}
private boolean serializeCompactRDFStructProp(XMPNode node, int indent, boolean hasRDFResourceQual) throws XMPException, IOException {
boolean hasAttrFields = false;
boolean hasElemFields = false;
boolean emitEndTag = true;
for (Iterator<XMPNode> ic = node.iterateChildren(); ic.hasNext(); ) {
XMPNode field = ic.next();
if (canBeRDFAttrProp(field)) {
hasAttrFields = true;
} else {
hasElemFields = true;
}
if (hasAttrFields && hasElemFields)
break;
}
if (hasRDFResourceQual && hasElemFields)
throw new XMPException("Can't mix rdf:resource qualifier and element fields", 202);
if (!node.hasChildren()) {
write(" rdf:parseType=\"Resource\"/>");
writeNewline();
emitEndTag = false;
} else if (!hasElemFields) {
serializeCompactRDFAttrProps(node, indent + 1);
write("/>");
writeNewline();
emitEndTag = false;
} else if (!hasAttrFields) {
write(" rdf:parseType=\"Resource\">");
writeNewline();
serializeCompactRDFElementProps(node, indent + 1);
} else {
write(62);
writeNewline();
writeIndent(indent + 1);
write("<rdf:Description");
serializeCompactRDFAttrProps(node, indent + 2);
write(">");
writeNewline();
serializeCompactRDFElementProps(node, indent + 1);
writeIndent(indent + 1);
write("</rdf:Description>");
writeNewline();
}
return emitEndTag;
}
private void serializeCompactRDFGeneralQualifier(int indent, XMPNode node) throws IOException, XMPException {
write(" rdf:parseType=\"Resource\">");
writeNewline();
serializeCanonicalRDFProperty(node, false, true, indent + 1);
for (Iterator<XMPNode> iq = node.iterateQualifier(); iq.hasNext(); ) {
XMPNode qualifier = iq.next();
serializeCanonicalRDFProperty(qualifier, false, false, indent + 1);
}
}
private void serializeCanonicalRDFSchema(XMPNode schemaNode, int level) throws IOException, XMPException {
for (Iterator<XMPNode> it = schemaNode.iterateChildren(); it.hasNext(); ) {
XMPNode propNode = it.next();
serializeCanonicalRDFProperty(propNode, this.options.getUseCanonicalFormat(), false, level + 2);
}
}
private void declareUsedNamespaces(XMPNode node, Set usedPrefixes, int indent) throws IOException {
if (node.getOptions().isSchemaNode()) {
String prefix = node.getValue().substring(0, node.getValue().length() - 1);
declareNamespace(prefix, node.getName(), usedPrefixes, indent);
} else if (node.getOptions().isStruct()) {
for (Iterator<XMPNode> iterator = node.iterateChildren(); iterator.hasNext(); ) {
XMPNode field = iterator.next();
declareNamespace(field.getName(), null, usedPrefixes, indent);
}
}
for (Iterator<XMPNode> iterator1 = node.iterateChildren(); iterator1.hasNext(); ) {
XMPNode child = iterator1.next();
declareUsedNamespaces(child, usedPrefixes, indent);
}
for (Iterator<XMPNode> it = node.iterateQualifier(); it.hasNext(); ) {
XMPNode qualifier = it.next();
declareNamespace(qualifier.getName(), null, usedPrefixes, indent);
declareUsedNamespaces(qualifier, usedPrefixes, indent);
}
}
private void declareNamespace(String prefix, String namespace, Set usedPrefixes, int indent) throws IOException {
if (namespace == null) {
QName qname = new QName(prefix);
if (qname.hasPrefix()) {
prefix = qname.getPrefix();
namespace = XMPMetaFactory.getSchemaRegistry().getNamespaceURI(prefix + ":");
declareNamespace(prefix, namespace, usedPrefixes, indent);
} else {
return;
}
}
if (!usedPrefixes.contains(prefix)) {
writeNewline();
writeIndent(indent);
write("xmlns:");
write(prefix);
write("=\"");
write(namespace);
write(34);
usedPrefixes.add(prefix);
}
}
private void startOuterRDFDescription(XMPNode schemaNode, int level) throws IOException {
writeIndent(level + 1);
write("<rdf:Description rdf:about=");
writeTreeName();
Set<String> usedPrefixes = new HashSet();
usedPrefixes.add("xml");
usedPrefixes.add("rdf");
declareUsedNamespaces(schemaNode, usedPrefixes, level + 3);
write(62);
writeNewline();
}
private void endOuterRDFDescription(int level) throws IOException {
writeIndent(level + 1);
write("</rdf:Description>");
writeNewline();
}
private void serializeCanonicalRDFProperty(XMPNode node, boolean useCanonicalRDF, boolean emitAsRDFValue, int indent) throws IOException, XMPException {
boolean emitEndTag = true;
boolean indentEndTag = true;
String elemName = node.getName();
if (emitAsRDFValue) {
elemName = "rdf:value";
} else if ("[]".equals(elemName)) {
elemName = "rdf:li";
}
writeIndent(indent);
write(60);
write(elemName);
boolean hasGeneralQualifiers = false;
boolean hasRDFResourceQual = false;
for (Iterator<XMPNode> it = node.iterateQualifier(); it.hasNext(); ) {
XMPNode qualifier = it.next();
if (!RDF_ATTR_QUALIFIER.contains(qualifier.getName())) {
hasGeneralQualifiers = true;
continue;
}
hasRDFResourceQual = "rdf:resource".equals(qualifier.getName());
if (!emitAsRDFValue) {
write(32);
write(qualifier.getName());
write("=\"");
appendNodeValue(qualifier.getValue(), true);
write(34);
}
}
if (hasGeneralQualifiers && !emitAsRDFValue) {
if (hasRDFResourceQual)
throw new XMPException("Can't mix rdf:resource and general qualifiers", 202);
if (useCanonicalRDF) {
write(">");
writeNewline();
indent++;
writeIndent(indent);
write("<rdf:Description");
write(">");
} else {
write(" rdf:parseType=\"Resource\">");
}
writeNewline();
serializeCanonicalRDFProperty(node, useCanonicalRDF, true, indent + 1);
for (Iterator<XMPNode> iterator = node.iterateQualifier(); iterator.hasNext(); ) {
XMPNode qualifier = iterator.next();
if (!RDF_ATTR_QUALIFIER.contains(qualifier.getName()))
serializeCanonicalRDFProperty(qualifier, useCanonicalRDF, false, indent + 1);
}
if (useCanonicalRDF) {
writeIndent(indent);
write("</rdf:Description>");
writeNewline();
indent--;
}
} else if (!node.getOptions().isCompositeProperty()) {
if (node.getOptions().isURI()) {
write(" rdf:resource=\"");
appendNodeValue(node.getValue(), true);
write("\"/>");
writeNewline();
emitEndTag = false;
} else if (node.getValue() == null || "".equals(node.getValue())) {
write("/>");
writeNewline();
emitEndTag = false;
} else {
write(62);
appendNodeValue(node.getValue(), false);
indentEndTag = false;
}
} else if (node.getOptions().isArray()) {
write(62);
writeNewline();
emitRDFArrayTag(node, true, indent + 1);
if (node.getOptions().isArrayAltText())
XMPNodeUtils.normalizeLangArray(node);
for (Iterator<XMPNode> iterator = node.iterateChildren(); iterator.hasNext(); ) {
XMPNode child = iterator.next();
serializeCanonicalRDFProperty(child, useCanonicalRDF, false, indent + 2);
}
emitRDFArrayTag(node, false, indent + 1);
} else if (!hasRDFResourceQual) {
if (!node.hasChildren()) {
if (useCanonicalRDF) {
write(">");
writeNewline();
writeIndent(indent + 1);
write("<rdf:Description/>");
} else {
write(" rdf:parseType=\"Resource\"/>");
emitEndTag = false;
}
writeNewline();
} else {
if (useCanonicalRDF) {
write(">");
writeNewline();
indent++;
writeIndent(indent);
write("<rdf:Description");
write(">");
} else {
write(" rdf:parseType=\"Resource\">");
}
writeNewline();
for (Iterator<XMPNode> iterator = node.iterateChildren(); iterator.hasNext(); ) {
XMPNode child = iterator.next();
serializeCanonicalRDFProperty(child, useCanonicalRDF, false, indent + 1);
}
if (useCanonicalRDF) {
writeIndent(indent);
write("</rdf:Description>");
writeNewline();
indent--;
}
}
} else {
for (Iterator<XMPNode> iterator = node.iterateChildren(); iterator.hasNext(); ) {
XMPNode child = iterator.next();
if (!canBeRDFAttrProp(child))
throw new XMPException("Can't mix rdf:resource and complex fields", 202);
writeNewline();
writeIndent(indent + 1);
write(32);
write(child.getName());
write("=\"");
appendNodeValue(child.getValue(), true);
write(34);
}
write("/>");
writeNewline();
emitEndTag = false;
}
if (emitEndTag) {
if (indentEndTag)
writeIndent(indent);
write("</");
write(elemName);
write(62);
writeNewline();
}
}
private void emitRDFArrayTag(XMPNode arrayNode, boolean isStartTag, int indent) throws IOException {
if (isStartTag || arrayNode.hasChildren()) {
writeIndent(indent);
write(isStartTag ? "<rdf:" : "</rdf:");
if (arrayNode.getOptions().isArrayAlternate()) {
write("Alt");
} else if (arrayNode.getOptions().isArrayOrdered()) {
write("Seq");
} else {
write("Bag");
}
if (isStartTag && !arrayNode.hasChildren()) {
write("/>");
} else {
write(">");
}
writeNewline();
}
}
private void appendNodeValue(String value, boolean forAttribute) throws IOException {
if (value == null)
value = "";
write(Utils.escapeXML(value, forAttribute, true));
}
private boolean canBeRDFAttrProp(XMPNode node) {
return (
!node.hasQualifier() &&
!node.getOptions().isURI() &&
!node.getOptions().isCompositeProperty() &&
!node.getOptions().containsOneOf(1073741824) &&
!"[]".equals(node.getName()));
}
private void writeIndent(int times) throws IOException {
for (int i = this.options.getBaseIndent() + times; i > 0; i--)
this.writer.write(this.options.getIndent());
}
private void write(int c) throws IOException {
this.writer.write(c);
}
private void write(String str) throws IOException {
this.writer.write(str);
}
private void writeChars(int number, char c) throws IOException {
for (; number > 0; number--)
this.writer.write(c);
}
private void writeNewline() throws IOException {
this.writer.write(this.options.getNewline());
}
}

View file

@ -0,0 +1,506 @@
package com.itextpdf.xmp.impl;
import com.itextpdf.xmp.XMPConst;
import com.itextpdf.xmp.XMPException;
import com.itextpdf.xmp.XMPMeta;
import com.itextpdf.xmp.XMPMetaFactory;
import com.itextpdf.xmp.impl.xpath.XMPPath;
import com.itextpdf.xmp.impl.xpath.XMPPathParser;
import com.itextpdf.xmp.options.PropertyOptions;
import com.itextpdf.xmp.properties.XMPAliasInfo;
import java.util.Iterator;
public class XMPUtilsImpl implements XMPConst {
private static final int UCK_NORMAL = 0;
private static final int UCK_SPACE = 1;
private static final int UCK_COMMA = 2;
private static final int UCK_SEMICOLON = 3;
private static final int UCK_QUOTE = 4;
private static final int UCK_CONTROL = 5;
private static final String SPACES = "  〿";
private static final String COMMAS = ",,、﹐﹑、،՝";
private static final String SEMICOLA = ";;﹔؛;";
private static final String QUOTES = "\"«»〝〞〟―‹›";
private static final String CONTROLS = "";
public static String catenateArrayItems(XMPMeta xmp, String schemaNS, String arrayName, String separator, String quotes, boolean allowCommas) throws XMPException {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertArrayName(arrayName);
ParameterAsserts.assertImplementation(xmp);
if (separator == null || separator.length() == 0)
separator = "; ";
if (quotes == null || quotes.length() == 0)
quotes = "\"";
XMPMetaImpl xmpImpl = (XMPMetaImpl)xmp;
XMPNode arrayNode = null;
XMPNode currItem = null;
XMPPath arrayPath = XMPPathParser.expandXPath(schemaNS, arrayName);
arrayNode = XMPNodeUtils.findNode(xmpImpl.getRoot(), arrayPath, false, null);
if (arrayNode == null)
return "";
if (!arrayNode.getOptions().isArray() || arrayNode.getOptions().isArrayAlternate())
throw new XMPException("Named property must be non-alternate array", 4);
checkSeparator(separator);
char openQuote = quotes.charAt(0);
char closeQuote = checkQuotes(quotes, openQuote);
StringBuffer catinatedString = new StringBuffer();
for (Iterator<XMPNode> it = arrayNode.iterateChildren(); it.hasNext(); ) {
currItem = it.next();
if (currItem.getOptions().isCompositeProperty())
throw new XMPException("Array items must be simple", 4);
String str = applyQuotes(currItem.getValue(), openQuote, closeQuote, allowCommas);
catinatedString.append(str);
if (it.hasNext())
catinatedString.append(separator);
}
return catinatedString.toString();
}
public static void separateArrayItems(XMPMeta xmp, String schemaNS, String arrayName, String catedStr, PropertyOptions arrayOptions, boolean preserveCommas) throws XMPException {
ParameterAsserts.assertSchemaNS(schemaNS);
ParameterAsserts.assertArrayName(arrayName);
if (catedStr == null)
throw new XMPException("Parameter must not be null", 4);
ParameterAsserts.assertImplementation(xmp);
XMPMetaImpl xmpImpl = (XMPMetaImpl)xmp;
XMPNode arrayNode = separateFindCreateArray(schemaNS, arrayName, arrayOptions, xmpImpl);
int nextKind = 0, charKind = 0;
char ch = '\000', nextChar = '\000';
int itemEnd = 0;
int endPos = catedStr.length();
while (itemEnd < endPos) {
String itemValue;
int itemStart;
for (itemStart = itemEnd; itemStart < endPos; itemStart++) {
ch = catedStr.charAt(itemStart);
charKind = classifyCharacter(ch);
if (charKind == 0 || charKind == 4)
break;
}
if (itemStart >= endPos)
break;
if (charKind != 4) {
for (itemEnd = itemStart; itemEnd < endPos; itemEnd++) {
ch = catedStr.charAt(itemEnd);
charKind = classifyCharacter(ch);
if (charKind == 0 || charKind == 4 || (charKind == 2 && preserveCommas))
continue;
if (charKind != 1)
break;
if (itemEnd + 1 < endPos) {
ch = catedStr.charAt(itemEnd + 1);
nextKind = classifyCharacter(ch);
if (nextKind == 0 || nextKind == 4 || (nextKind == 2 && preserveCommas))
continue;
break;
}
break;
}
itemValue = catedStr.substring(itemStart, itemEnd);
} else {
char openQuote = ch;
char closeQuote = getClosingQuote(openQuote);
itemStart++;
itemValue = "";
for (int i = itemStart; i < endPos; i++) {
ch = catedStr.charAt(i);
charKind = classifyCharacter(ch);
if (charKind != 4 || !isSurroundingQuote(ch, openQuote, closeQuote)) {
itemValue = itemValue + ch;
} else {
if (i + 1 < endPos) {
nextChar = catedStr.charAt(i + 1);
nextKind = classifyCharacter(nextChar);
} else {
nextKind = 3;
nextChar = ';';
}
if (ch == nextChar) {
itemValue = itemValue + ch;
i++;
} else if (!isClosingingQuote(ch, openQuote, closeQuote)) {
itemValue = itemValue + ch;
} else {
i++;
break;
}
}
}
}
int foundIndex = -1;
for (int oldChild = 1; oldChild <= arrayNode.getChildrenLength(); oldChild++) {
if (itemValue.equals(arrayNode.getChild(oldChild).getValue())) {
foundIndex = oldChild;
break;
}
}
XMPNode newItem = null;
if (foundIndex < 0) {
newItem = new XMPNode("[]", itemValue, null);
arrayNode.addChild(newItem);
}
}
}
private static XMPNode separateFindCreateArray(String schemaNS, String arrayName, PropertyOptions arrayOptions, XMPMetaImpl xmp) throws XMPException {
arrayOptions = XMPNodeUtils.verifySetOptions(arrayOptions, null);
if (!arrayOptions.isOnlyArrayOptions())
throw new XMPException("Options can only provide array form", 103);
XMPPath arrayPath = XMPPathParser.expandXPath(schemaNS, arrayName);
XMPNode arrayNode = XMPNodeUtils.findNode(xmp.getRoot(), arrayPath, false, null);
if (arrayNode != null) {
PropertyOptions arrayForm = arrayNode.getOptions();
if (!arrayForm.isArray() || arrayForm.isArrayAlternate())
throw new XMPException("Named property must be non-alternate array", 102);
if (arrayOptions.equalArrayTypes(arrayForm))
throw new XMPException("Mismatch of specified and existing array form", 102);
} else {
arrayNode = XMPNodeUtils.findNode(xmp.getRoot(), arrayPath, true,
arrayOptions.setArray(true));
if (arrayNode == null)
throw new XMPException("Failed to create named array", 102);
}
return arrayNode;
}
public static void removeProperties(XMPMeta xmp, String schemaNS, String propName, boolean doAllProperties, boolean includeAliases) throws XMPException {
ParameterAsserts.assertImplementation(xmp);
XMPMetaImpl xmpImpl = (XMPMetaImpl)xmp;
if (propName != null && propName.length() > 0) {
if (schemaNS == null || schemaNS.length() == 0)
throw new XMPException("Property name requires schema namespace", 4);
XMPPath expPath = XMPPathParser.expandXPath(schemaNS, propName);
XMPNode propNode = XMPNodeUtils.findNode(xmpImpl.getRoot(), expPath, false, null);
if (propNode != null)
if (doAllProperties ||
!Utils.isInternalProperty(expPath.getSegment(0)
.getName(), expPath.getSegment(1).getName())) {
XMPNode parent = propNode.getParent();
parent.removeChild(propNode);
if (parent.getOptions().isSchemaNode() && !parent.hasChildren())
parent.getParent().removeChild(parent);
}
} else if (schemaNS != null && schemaNS.length() > 0) {
XMPNode schemaNode = XMPNodeUtils.findSchemaNode(xmpImpl.getRoot(), schemaNS, false);
if (schemaNode != null)
if (removeSchemaChildren(schemaNode, doAllProperties))
xmpImpl.getRoot().removeChild(schemaNode);
if (includeAliases) {
XMPAliasInfo[] aliases = XMPMetaFactory.getSchemaRegistry().findAliases(schemaNS);
for (int i = 0; i < aliases.length; i++) {
XMPAliasInfo info = aliases[i];
XMPPath path = XMPPathParser.expandXPath(info.getNamespace(),
info.getPropName());
XMPNode actualProp = XMPNodeUtils.findNode(xmpImpl.getRoot(), path, false, null);
if (actualProp != null) {
XMPNode parent = actualProp.getParent();
parent.removeChild(actualProp);
}
}
}
} else {
for (Iterator<XMPNode> it = xmpImpl.getRoot().iterateChildren(); it.hasNext(); ) {
XMPNode schema = it.next();
if (removeSchemaChildren(schema, doAllProperties))
it.remove();
}
}
}
public static void appendProperties(XMPMeta source, XMPMeta destination, boolean doAllProperties, boolean replaceOldValues, boolean deleteEmptyValues) throws XMPException {
ParameterAsserts.assertImplementation(source);
ParameterAsserts.assertImplementation(destination);
XMPMetaImpl src = (XMPMetaImpl)source;
XMPMetaImpl dest = (XMPMetaImpl)destination;
for (Iterator<XMPNode> it = src.getRoot().iterateChildren(); it.hasNext(); ) {
XMPNode sourceSchema = it.next();
XMPNode destSchema = XMPNodeUtils.findSchemaNode(dest.getRoot(),
sourceSchema.getName(), false);
boolean createdSchema = false;
if (destSchema == null) {
destSchema = new XMPNode(sourceSchema.getName(), sourceSchema.getValue(), new PropertyOptions().setSchemaNode(true));
dest.getRoot().addChild(destSchema);
createdSchema = true;
}
for (Iterator<XMPNode> ic = sourceSchema.iterateChildren(); ic.hasNext(); ) {
XMPNode sourceProp = ic.next();
if (doAllProperties ||
!Utils.isInternalProperty(sourceSchema.getName(), sourceProp.getName()))
appendSubtree(dest, sourceProp, destSchema, replaceOldValues, deleteEmptyValues);
}
if (!destSchema.hasChildren() && (createdSchema || deleteEmptyValues))
dest.getRoot().removeChild(destSchema);
}
}
private static boolean removeSchemaChildren(XMPNode schemaNode, boolean doAllProperties) {
for (Iterator<XMPNode> it = schemaNode.iterateChildren(); it.hasNext(); ) {
XMPNode currProp = it.next();
if (doAllProperties ||
!Utils.isInternalProperty(schemaNode.getName(), currProp.getName()))
it.remove();
}
return !schemaNode.hasChildren();
}
private static void appendSubtree(XMPMetaImpl destXMP, XMPNode sourceNode, XMPNode destParent, boolean replaceOldValues, boolean deleteEmptyValues) throws XMPException {
XMPNode destNode = XMPNodeUtils.findChildNode(destParent, sourceNode.getName(), false);
boolean valueIsEmpty = false;
if (deleteEmptyValues)
valueIsEmpty = sourceNode.getOptions().isSimple() ? ((sourceNode.getValue() == null || sourceNode.getValue().length() == 0)) : (!sourceNode.hasChildren());
if (deleteEmptyValues && valueIsEmpty) {
if (destNode != null)
destParent.removeChild(destNode);
} else if (destNode == null) {
destParent.addChild((XMPNode)sourceNode.clone());
} else if (replaceOldValues) {
destXMP.setNode(destNode, sourceNode.getValue(), sourceNode.getOptions(), true);
destParent.removeChild(destNode);
destNode = (XMPNode)sourceNode.clone();
destParent.addChild(destNode);
} else {
PropertyOptions sourceForm = sourceNode.getOptions();
PropertyOptions destForm = destNode.getOptions();
if (sourceForm != destForm)
return;
if (sourceForm.isStruct()) {
for (Iterator<XMPNode> it = sourceNode.iterateChildren(); it.hasNext(); ) {
XMPNode sourceField = it.next();
appendSubtree(destXMP, sourceField, destNode, replaceOldValues, deleteEmptyValues);
if (deleteEmptyValues && !destNode.hasChildren())
destParent.removeChild(destNode);
}
} else if (sourceForm.isArrayAltText()) {
for (Iterator<XMPNode> it = sourceNode.iterateChildren(); it.hasNext(); ) {
XMPNode sourceItem = it.next();
if (!sourceItem.hasQualifier() ||
!"xml:lang".equals(sourceItem.getQualifier(1).getName()))
continue;
int destIndex = XMPNodeUtils.lookupLanguageItem(destNode,
sourceItem.getQualifier(1).getValue());
if (deleteEmptyValues && (
sourceItem.getValue() == null ||
sourceItem.getValue().length() == 0)) {
if (destIndex != -1) {
destNode.removeChild(destIndex);
if (!destNode.hasChildren())
destParent.removeChild(destNode);
}
continue;
}
if (destIndex == -1) {
if (!"x-default".equals(sourceItem.getQualifier(1).getValue()) ||
!destNode.hasChildren()) {
sourceItem.cloneSubtree(destNode);
continue;
}
XMPNode destItem = new XMPNode(
sourceItem.getName(),
sourceItem.getValue(),
sourceItem.getOptions());
sourceItem.cloneSubtree(destItem);
destNode.addChild(1, destItem);
}
}
} else if (sourceForm.isArray()) {
for (Iterator<XMPNode> is = sourceNode.iterateChildren(); is.hasNext(); ) {
XMPNode sourceItem = is.next();
boolean match = false;
for (Iterator<XMPNode> id = destNode.iterateChildren(); id.hasNext(); ) {
XMPNode destItem = id.next();
if (itemValuesMatch(sourceItem, destItem))
match = true;
}
if (!match) {
destNode = (XMPNode)sourceItem.clone();
destParent.addChild(destNode);
}
}
}
}
}
private static boolean itemValuesMatch(XMPNode leftNode, XMPNode rightNode) throws XMPException {
PropertyOptions leftForm = leftNode.getOptions();
PropertyOptions rightForm = rightNode.getOptions();
if (leftForm.equals(rightForm))
return false;
if (leftForm.getOptions() == 0) {
if (!leftNode.getValue().equals(rightNode.getValue()))
return false;
if (leftNode.getOptions().getHasLanguage() != rightNode.getOptions().getHasLanguage())
return false;
if (leftNode.getOptions().getHasLanguage() &&
!leftNode.getQualifier(1).getValue().equals(
rightNode.getQualifier(1).getValue()))
return false;
} else if (leftForm.isStruct()) {
if (leftNode.getChildrenLength() != rightNode.getChildrenLength())
return false;
for (Iterator<XMPNode> it = leftNode.iterateChildren(); it.hasNext(); ) {
XMPNode leftField = it.next();
XMPNode rightField = XMPNodeUtils.findChildNode(rightNode, leftField.getName(), false);
if (rightField == null || !itemValuesMatch(leftField, rightField))
return false;
}
} else {
assert leftForm.isArray();
for (Iterator<XMPNode> il = leftNode.iterateChildren(); il.hasNext(); ) {
XMPNode leftItem = il.next();
boolean match = false;
for (Iterator<XMPNode> ir = rightNode.iterateChildren(); ir.hasNext(); ) {
XMPNode rightItem = ir.next();
if (itemValuesMatch(leftItem, rightItem)) {
match = true;
break;
}
}
if (!match)
return false;
}
}
return true;
}
private static void checkSeparator(String separator) throws XMPException {
boolean haveSemicolon = false;
for (int i = 0; i < separator.length(); i++) {
int charKind = classifyCharacter(separator.charAt(i));
if (charKind == 3) {
if (haveSemicolon)
throw new XMPException("Separator can have only one semicolon", 4);
haveSemicolon = true;
} else if (charKind != 1) {
throw new XMPException("Separator can have only spaces and one semicolon", 4);
}
}
if (!haveSemicolon)
throw new XMPException("Separator must have one semicolon", 4);
}
private static char checkQuotes(String quotes, char openQuote) throws XMPException {
char closeQuote;
int charKind = classifyCharacter(openQuote);
if (charKind != 4)
throw new XMPException("Invalid quoting character", 4);
if (quotes.length() == 1) {
closeQuote = openQuote;
} else {
closeQuote = quotes.charAt(1);
charKind = classifyCharacter(closeQuote);
if (charKind != 4)
throw new XMPException("Invalid quoting character", 4);
}
if (closeQuote != getClosingQuote(openQuote))
throw new XMPException("Mismatched quote pair", 4);
return closeQuote;
}
private static int classifyCharacter(char ch) {
if ("  〿".indexOf(ch) >= 0 || (' ' <= ch && ch <= ''))
return 1;
if (",,、﹐﹑、،՝".indexOf(ch) >= 0)
return 2;
if (";;﹔؛;".indexOf(ch) >= 0)
return 3;
if ("\"«»〝〞〟―‹›".indexOf(ch) >= 0 || ('〈' <= ch && ch <= '』') || ('' <= ch && ch <= '‟'))
return 4;
if (ch < ' ' || "".indexOf(ch) >= 0)
return 5;
return 0;
}
private static char getClosingQuote(char openQuote) {
switch (openQuote) {
case '"':
return '"';
case '«':
return '»';
case '»':
return '«';
case '―':
return '―';
case '':
return '';
case '':
return '';
case '“':
return '”';
case '„':
return '‟';
case '':
return '';
case '':
return '';
case '〈':
return '〉';
case '《':
return '》';
case '「':
return '」';
case '『':
return '』';
case '〝':
return '〟';
}
return '\000';
}
private static String applyQuotes(String item, char openQuote, char closeQuote, boolean allowCommas) {
if (item == null)
item = "";
boolean prevSpace = false;
int i;
for (i = 0; i < item.length(); i++) {
char ch = item.charAt(i);
int charKind = classifyCharacter(ch);
if (i == 0 && charKind == 4)
break;
if (charKind == 1) {
if (prevSpace)
break;
prevSpace = true;
} else {
prevSpace = false;
if (charKind == 3 || charKind == 5 || (charKind == 2 && !allowCommas))
break;
}
}
if (i < item.length()) {
StringBuffer newItem = new StringBuffer(item.length() + 2);
int splitPoint;
for (splitPoint = 0; splitPoint <= i; splitPoint++) {
if (classifyCharacter(item.charAt(i)) == 4)
break;
}
newItem.append(openQuote).append(item.substring(0, splitPoint));
for (int charOffset = splitPoint; charOffset < item.length(); charOffset++) {
newItem.append(item.charAt(charOffset));
if (classifyCharacter(item.charAt(charOffset)) == 4 &&
isSurroundingQuote(item.charAt(charOffset), openQuote, closeQuote))
newItem.append(item.charAt(charOffset));
}
newItem.append(closeQuote);
item = newItem.toString();
}
return item;
}
private static boolean isSurroundingQuote(char ch, char openQuote, char closeQuote) {
return (ch == openQuote || isClosingingQuote(ch, openQuote, closeQuote));
}
private static boolean isClosingingQuote(char ch, char openQuote, char closeQuote) {
return (ch == closeQuote || (openQuote == '〝' && ch == '〞') || ch == '〟');
}
}

View file

@ -0,0 +1,13 @@
package com.itextpdf.xmp.impl.xpath;
class PathPosition {
public String path = null;
int nameStart = 0;
int nameEnd = 0;
int stepBegin = 0;
int stepEnd = 0;
}

View file

@ -0,0 +1,53 @@
package com.itextpdf.xmp.impl.xpath;
import java.util.ArrayList;
import java.util.List;
public class XMPPath {
public static final int STRUCT_FIELD_STEP = 1;
public static final int QUALIFIER_STEP = 2;
public static final int ARRAY_INDEX_STEP = 3;
public static final int ARRAY_LAST_STEP = 4;
public static final int QUAL_SELECTOR_STEP = 5;
public static final int FIELD_SELECTOR_STEP = 6;
public static final int SCHEMA_NODE = -2147483648;
public static final int STEP_SCHEMA = 0;
public static final int STEP_ROOT_PROP = 1;
private List segments = new ArrayList(5);
public void add(XMPPathSegment segment) {
this.segments.add(segment);
}
public XMPPathSegment getSegment(int index) {
return this.segments.get(index);
}
public int size() {
return this.segments.size();
}
public String toString() {
StringBuffer result = new StringBuffer();
int index = 1;
while (index < size()) {
result.append(getSegment(index));
if (index < size() - 1) {
int kind = getSegment(index + 1).getKind();
if (kind == 1 || kind == 2)
result.append('/');
}
index++;
}
return result.toString();
}
}

View file

@ -0,0 +1,202 @@
package com.itextpdf.xmp.impl.xpath;
import com.itextpdf.xmp.XMPException;
import com.itextpdf.xmp.XMPMetaFactory;
import com.itextpdf.xmp.impl.Utils;
import com.itextpdf.xmp.properties.XMPAliasInfo;
public final class XMPPathParser {
public static XMPPath expandXPath(String schemaNS, String path) throws XMPException {
if (schemaNS == null || path == null)
throw new XMPException("Parameter must not be null", 4);
XMPPath expandedXPath = new XMPPath();
PathPosition pos = new PathPosition();
pos.path = path;
parseRootNode(schemaNS, pos, expandedXPath);
while (pos.stepEnd < path.length()) {
XMPPathSegment segment;
pos.stepBegin = pos.stepEnd;
skipPathDelimiter(path, pos);
pos.stepEnd = pos.stepBegin;
if (path.charAt(pos.stepBegin) != '[') {
segment = parseStructSegment(pos);
} else {
segment = parseIndexSegment(pos);
}
if (segment.getKind() == 1) {
if (segment.getName().charAt(0) == '@') {
segment.setName("?" + segment.getName().substring(1));
if (!"?xml:lang".equals(segment.getName()))
throw new XMPException("Only xml:lang allowed with '@'", 102);
}
if (segment.getName().charAt(0) == '?') {
pos.nameStart++;
segment.setKind(2);
}
verifyQualName(pos.path.substring(pos.nameStart, pos.nameEnd));
} else if (segment.getKind() == 6) {
if (segment.getName().charAt(1) == '@') {
segment.setName("[?" + segment.getName().substring(2));
if (!segment.getName().startsWith("[?xml:lang="))
throw new XMPException("Only xml:lang allowed with '@'", 102);
}
if (segment.getName().charAt(1) == '?') {
pos.nameStart++;
segment.setKind(5);
verifyQualName(pos.path.substring(pos.nameStart, pos.nameEnd));
}
}
expandedXPath.add(segment);
}
return expandedXPath;
}
private static void skipPathDelimiter(String path, PathPosition pos) throws XMPException {
if (path.charAt(pos.stepBegin) == '/') {
pos.stepBegin++;
if (pos.stepBegin >= path.length())
throw new XMPException("Empty XMPPath segment", 102);
}
if (path.charAt(pos.stepBegin) == '*') {
pos.stepBegin++;
if (pos.stepBegin >= path.length() || path.charAt(pos.stepBegin) != '[')
throw new XMPException("Missing '[' after '*'", 102);
}
}
private static XMPPathSegment parseStructSegment(PathPosition pos) throws XMPException {
pos.nameStart = pos.stepBegin;
while (pos.stepEnd < pos.path.length() && "/[*".indexOf(pos.path.charAt(pos.stepEnd)) < 0)
pos.stepEnd++;
pos.nameEnd = pos.stepEnd;
if (pos.stepEnd == pos.stepBegin)
throw new XMPException("Empty XMPPath segment", 102);
XMPPathSegment segment = new XMPPathSegment(pos.path.substring(pos.stepBegin, pos.stepEnd), 1);
return segment;
}
private static XMPPathSegment parseIndexSegment(PathPosition pos) throws XMPException {
XMPPathSegment segment;
pos.stepEnd++;
if ('0' <= pos.path.charAt(pos.stepEnd) && pos.path.charAt(pos.stepEnd) <= '9') {
while (pos.stepEnd < pos.path.length() && '0' <= pos.path.charAt(pos.stepEnd) &&
pos.path.charAt(pos.stepEnd) <= '9')
pos.stepEnd++;
segment = new XMPPathSegment(null, 3);
} else {
while (pos.stepEnd < pos.path.length() && pos.path.charAt(pos.stepEnd) != ']' &&
pos.path.charAt(pos.stepEnd) != '=')
pos.stepEnd++;
if (pos.stepEnd >= pos.path.length())
throw new XMPException("Missing ']' or '=' for array index", 102);
if (pos.path.charAt(pos.stepEnd) == ']') {
if (!"[last()".equals(pos.path.substring(pos.stepBegin, pos.stepEnd)))
throw new XMPException("Invalid non-numeric array index", 102);
segment = new XMPPathSegment(null, 4);
} else {
pos.nameStart = pos.stepBegin + 1;
pos.nameEnd = pos.stepEnd;
pos.stepEnd++;
char quote = pos.path.charAt(pos.stepEnd);
if (quote != '\'' && quote != '"')
throw new XMPException("Invalid quote in array selector", 102);
pos.stepEnd++;
while (pos.stepEnd < pos.path.length()) {
if (pos.path.charAt(pos.stepEnd) == quote) {
if (pos.stepEnd + 1 >= pos.path.length() ||
pos.path.charAt(pos.stepEnd + 1) != quote)
break;
pos.stepEnd++;
}
pos.stepEnd++;
}
if (pos.stepEnd >= pos.path.length())
throw new XMPException("No terminating quote for array selector", 102);
pos.stepEnd++;
segment = new XMPPathSegment(null, 6);
}
}
if (pos.stepEnd >= pos.path.length() || pos.path.charAt(pos.stepEnd) != ']')
throw new XMPException("Missing ']' for array index", 102);
pos.stepEnd++;
segment.setName(pos.path.substring(pos.stepBegin, pos.stepEnd));
return segment;
}
private static void parseRootNode(String schemaNS, PathPosition pos, XMPPath expandedXPath) throws XMPException {
while (pos.stepEnd < pos.path.length() && "/[*".indexOf(pos.path.charAt(pos.stepEnd)) < 0)
pos.stepEnd++;
if (pos.stepEnd == pos.stepBegin)
throw new XMPException("Empty initial XMPPath step", 102);
String rootProp = verifyXPathRoot(schemaNS, pos.path.substring(pos.stepBegin, pos.stepEnd));
XMPAliasInfo aliasInfo = XMPMetaFactory.getSchemaRegistry().findAlias(rootProp);
if (aliasInfo == null) {
expandedXPath.add(new XMPPathSegment(schemaNS, Integer.MIN_VALUE));
XMPPathSegment rootStep = new XMPPathSegment(rootProp, 1);
expandedXPath.add(rootStep);
} else {
expandedXPath.add(new XMPPathSegment(aliasInfo.getNamespace(), Integer.MIN_VALUE));
XMPPathSegment rootStep = new XMPPathSegment(verifyXPathRoot(aliasInfo.getNamespace(),
aliasInfo.getPropName()), 1);
rootStep.setAlias(true);
rootStep.setAliasForm(aliasInfo.getAliasForm().getOptions());
expandedXPath.add(rootStep);
if (aliasInfo.getAliasForm().isArrayAltText()) {
XMPPathSegment qualSelectorStep = new XMPPathSegment("[?xml:lang='x-default']", 5);
qualSelectorStep.setAlias(true);
qualSelectorStep.setAliasForm(aliasInfo.getAliasForm().getOptions());
expandedXPath.add(qualSelectorStep);
} else if (aliasInfo.getAliasForm().isArray()) {
XMPPathSegment indexStep = new XMPPathSegment("[1]", 3);
indexStep.setAlias(true);
indexStep.setAliasForm(aliasInfo.getAliasForm().getOptions());
expandedXPath.add(indexStep);
}
}
}
private static void verifyQualName(String qualName) throws XMPException {
int colonPos = qualName.indexOf(':');
if (colonPos > 0) {
String prefix = qualName.substring(0, colonPos);
if (Utils.isXMLNameNS(prefix)) {
String regURI = XMPMetaFactory.getSchemaRegistry().getNamespaceURI(prefix);
if (regURI != null)
return;
throw new XMPException("Unknown namespace prefix for qualified name", 102);
}
}
throw new XMPException("Ill-formed qualified name", 102);
}
private static void verifySimpleXMLName(String name) throws XMPException {
if (!Utils.isXMLName(name))
throw new XMPException("Bad XML name", 102);
}
private static String verifyXPathRoot(String schemaNS, String rootProp) throws XMPException {
if (schemaNS == null || schemaNS.length() == 0)
throw new XMPException("Schema namespace URI is required", 101);
if (rootProp.charAt(0) == '?' || rootProp.charAt(0) == '@')
throw new XMPException("Top level name must not be a qualifier", 102);
if (rootProp.indexOf('/') >= 0 || rootProp.indexOf('[') >= 0)
throw new XMPException("Top level name must be simple", 102);
String prefix = XMPMetaFactory.getSchemaRegistry().getNamespacePrefix(schemaNS);
if (prefix == null)
throw new XMPException("Unregistered schema namespace URI", 101);
int colonPos = rootProp.indexOf(':');
if (colonPos < 0) {
verifySimpleXMLName(rootProp);
return prefix + rootProp;
}
verifySimpleXMLName(rootProp.substring(0, colonPos));
verifySimpleXMLName(rootProp.substring(colonPos));
prefix = rootProp.substring(0, colonPos + 1);
String regPrefix = XMPMetaFactory.getSchemaRegistry().getNamespacePrefix(schemaNS);
if (regPrefix == null)
throw new XMPException("Unknown schema namespace prefix", 101);
if (!prefix.equals(regPrefix))
throw new XMPException("Schema namespace URI and prefix mismatch", 101);
return rootProp;
}
}

View file

@ -0,0 +1,66 @@
package com.itextpdf.xmp.impl.xpath;
public class XMPPathSegment {
private String name;
private int kind;
private boolean alias;
private int aliasForm;
public XMPPathSegment(String name) {
this.name = name;
}
public XMPPathSegment(String name, int kind) {
this.name = name;
this.kind = kind;
}
public int getKind() {
return this.kind;
}
public void setKind(int kind) {
this.kind = kind;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public void setAlias(boolean alias) {
this.alias = alias;
}
public boolean isAlias() {
return this.alias;
}
public int getAliasForm() {
return this.aliasForm;
}
public void setAliasForm(int aliasForm) {
this.aliasForm = aliasForm;
}
public String toString() {
switch (this.kind) {
case 1:
case 2:
case 3:
case 4:
return this.name;
case 5:
case 6:
return this.name;
}
return this.name;
}
}

View file

@ -0,0 +1,85 @@
package com.itextpdf.xmp.options;
import com.itextpdf.xmp.XMPException;
public final class AliasOptions extends Options {
public static final int PROP_DIRECT = 0;
public static final int PROP_ARRAY = 512;
public static final int PROP_ARRAY_ORDERED = 1024;
public static final int PROP_ARRAY_ALTERNATE = 2048;
public static final int PROP_ARRAY_ALT_TEXT = 4096;
public AliasOptions() {}
public AliasOptions(int options) throws XMPException {
super(options);
}
public boolean isSimple() {
return (getOptions() == 0);
}
public boolean isArray() {
return getOption(512);
}
public AliasOptions setArray(boolean value) {
setOption(512, value);
return this;
}
public boolean isArrayOrdered() {
return getOption(1024);
}
public AliasOptions setArrayOrdered(boolean value) {
setOption(1536, value);
return this;
}
public boolean isArrayAlternate() {
return getOption(2048);
}
public AliasOptions setArrayAlternate(boolean value) {
setOption(3584, value);
return this;
}
public boolean isArrayAltText() {
return getOption(4096);
}
public AliasOptions setArrayAltText(boolean value) {
setOption(7680, value);
return this;
}
public PropertyOptions toPropertyOptions() throws XMPException {
return new PropertyOptions(getOptions());
}
protected String defineOptionName(int option) {
switch (option) {
case 0:
return "PROP_DIRECT";
case 512:
return "ARRAY";
case 1024:
return "ARRAY_ORDERED";
case 2048:
return "ARRAY_ALTERNATE";
case 4096:
return "ARRAY_ALT_TEXT";
}
return null;
}
protected int getValidOptions() {
return 7680;
}
}

View file

@ -0,0 +1,65 @@
package com.itextpdf.xmp.options;
public final class IteratorOptions extends Options {
public static final int JUST_CHILDREN = 256;
public static final int JUST_LEAFNODES = 512;
public static final int JUST_LEAFNAME = 1024;
public static final int OMIT_QUALIFIERS = 4096;
public boolean isJustChildren() {
return getOption(256);
}
public boolean isJustLeafname() {
return getOption(1024);
}
public boolean isJustLeafnodes() {
return getOption(512);
}
public boolean isOmitQualifiers() {
return getOption(4096);
}
public IteratorOptions setJustChildren(boolean value) {
setOption(256, value);
return this;
}
public IteratorOptions setJustLeafname(boolean value) {
setOption(1024, value);
return this;
}
public IteratorOptions setJustLeafnodes(boolean value) {
setOption(512, value);
return this;
}
public IteratorOptions setOmitQualifiers(boolean value) {
setOption(4096, value);
return this;
}
protected String defineOptionName(int option) {
switch (option) {
case 256:
return "JUST_CHILDREN";
case 512:
return "JUST_LEAFNODES";
case 1024:
return "JUST_LEAFNAME";
case 4096:
return "OMIT_QUALIFIERS";
}
return null;
}
protected int getValidOptions() {
return 5888;
}
}

View file

@ -0,0 +1,117 @@
package com.itextpdf.xmp.options;
import com.itextpdf.xmp.XMPException;
import java.util.HashMap;
import java.util.Map;
public abstract class Options {
private int options = 0;
private Map optionNames = null;
public Options() {}
public Options(int options) throws XMPException {
assertOptionsValid(options);
setOptions(options);
}
public void clear() {
this.options = 0;
}
public boolean isExactly(int optionBits) {
return (getOptions() == optionBits);
}
public boolean containsAllOptions(int optionBits) {
return ((getOptions() & optionBits) == optionBits);
}
public boolean containsOneOf(int optionBits) {
return ((getOptions() & optionBits) != 0);
}
protected boolean getOption(int optionBit) {
return ((this.options & optionBit) != 0);
}
public void setOption(int optionBits, boolean value) {
this.options = value ? (this.options | optionBits) : (this.options & (optionBits ^ 0xFFFFFFFF));
}
public int getOptions() {
return this.options;
}
public void setOptions(int options) throws XMPException {
assertOptionsValid(options);
this.options = options;
}
public boolean equals(Object obj) {
return (getOptions() == ((Options)obj).getOptions());
}
public int hashCode() {
return getOptions();
}
public String getOptionsString() {
if (this.options != 0) {
StringBuffer sb = new StringBuffer();
int theBits = this.options;
while (theBits != 0) {
int oneLessBit = theBits & theBits - 1;
int singleBit = theBits ^ oneLessBit;
String bitName = getOptionName(singleBit);
sb.append(bitName);
if (oneLessBit != 0)
sb.append(" | ");
theBits = oneLessBit;
}
return sb.toString();
}
return "<none>";
}
public String toString() {
return "0x" + Integer.toHexString(this.options);
}
protected abstract int getValidOptions();
protected abstract String defineOptionName(int paramInt);
protected void assertConsistency(int options) throws XMPException {}
private void assertOptionsValid(int options) throws XMPException {
int invalidOptions = options & (getValidOptions() ^ 0xFFFFFFFF);
if (invalidOptions == 0) {
assertConsistency(options);
} else {
throw new XMPException("The option bit(s) 0x" + Integer.toHexString(invalidOptions) + " are invalid!", 103);
}
}
private String getOptionName(int option) {
Map<Integer, String> optionsNames = procureOptionNames();
Integer key = new Integer(option);
String result = (String)optionsNames.get(key);
if (result == null) {
result = defineOptionName(option);
if (result != null) {
optionsNames.put(key, result);
} else {
result = "<option name not defined>";
}
}
return result;
}
private Map procureOptionNames() {
if (this.optionNames == null)
this.optionNames = new HashMap();
return this.optionNames;
}
}

View file

@ -0,0 +1,82 @@
package com.itextpdf.xmp.options;
public final class ParseOptions extends Options {
public static final int REQUIRE_XMP_META = 1;
public static final int STRICT_ALIASING = 4;
public static final int FIX_CONTROL_CHARS = 8;
public static final int ACCEPT_LATIN_1 = 16;
public static final int OMIT_NORMALIZATION = 32;
public ParseOptions() {
setOption(24, true);
}
public boolean getRequireXMPMeta() {
return getOption(1);
}
public ParseOptions setRequireXMPMeta(boolean value) {
setOption(1, value);
return this;
}
public boolean getStrictAliasing() {
return getOption(4);
}
public ParseOptions setStrictAliasing(boolean value) {
setOption(4, value);
return this;
}
public boolean getFixControlChars() {
return getOption(8);
}
public ParseOptions setFixControlChars(boolean value) {
setOption(8, value);
return this;
}
public boolean getAcceptLatin1() {
return getOption(16);
}
public ParseOptions setOmitNormalization(boolean value) {
setOption(32, value);
return this;
}
public boolean getOmitNormalization() {
return getOption(32);
}
public ParseOptions setAcceptLatin1(boolean value) {
setOption(16, value);
return this;
}
protected String defineOptionName(int option) {
switch (option) {
case 1:
return "REQUIRE_XMP_META";
case 4:
return "STRICT_ALIASING";
case 8:
return "FIX_CONTROL_CHARS";
case 16:
return "ACCEPT_LATIN_1";
case 32:
return "OMIT_NORMALIZATION";
}
return null;
}
protected int getValidOptions() {
return 61;
}
}

View file

@ -0,0 +1,202 @@
package com.itextpdf.xmp.options;
import com.itextpdf.xmp.XMPException;
public final class PropertyOptions extends Options {
public static final int NO_OPTIONS = 0;
public static final int URI = 2;
public static final int HAS_QUALIFIERS = 16;
public static final int QUALIFIER = 32;
public static final int HAS_LANGUAGE = 64;
public static final int HAS_TYPE = 128;
public static final int STRUCT = 256;
public static final int ARRAY = 512;
public static final int ARRAY_ORDERED = 1024;
public static final int ARRAY_ALTERNATE = 2048;
public static final int ARRAY_ALT_TEXT = 4096;
public static final int SCHEMA_NODE = -2147483648;
public static final int DELETE_EXISTING = 536870912;
public static final int SEPARATE_NODE = 1073741824;
public PropertyOptions() {}
public PropertyOptions(int options) throws XMPException {
super(options);
}
public boolean isURI() {
return getOption(2);
}
public PropertyOptions setURI(boolean value) {
setOption(2, value);
return this;
}
public boolean getHasQualifiers() {
return getOption(16);
}
public PropertyOptions setHasQualifiers(boolean value) {
setOption(16, value);
return this;
}
public boolean isQualifier() {
return getOption(32);
}
public PropertyOptions setQualifier(boolean value) {
setOption(32, value);
return this;
}
public boolean getHasLanguage() {
return getOption(64);
}
public PropertyOptions setHasLanguage(boolean value) {
setOption(64, value);
return this;
}
public boolean getHasType() {
return getOption(128);
}
public PropertyOptions setHasType(boolean value) {
setOption(128, value);
return this;
}
public boolean isStruct() {
return getOption(256);
}
public PropertyOptions setStruct(boolean value) {
setOption(256, value);
return this;
}
public boolean isArray() {
return getOption(512);
}
public PropertyOptions setArray(boolean value) {
setOption(512, value);
return this;
}
public boolean isArrayOrdered() {
return getOption(1024);
}
public PropertyOptions setArrayOrdered(boolean value) {
setOption(1024, value);
return this;
}
public boolean isArrayAlternate() {
return getOption(2048);
}
public PropertyOptions setArrayAlternate(boolean value) {
setOption(2048, value);
return this;
}
public boolean isArrayAltText() {
return getOption(4096);
}
public PropertyOptions setArrayAltText(boolean value) {
setOption(4096, value);
return this;
}
public boolean isSchemaNode() {
return getOption(Integer.MIN_VALUE);
}
public PropertyOptions setSchemaNode(boolean value) {
setOption(Integer.MIN_VALUE, value);
return this;
}
public boolean isCompositeProperty() {
return ((getOptions() & 0x300) > 0);
}
public boolean isSimple() {
return ((getOptions() & 0x300) == 0);
}
public boolean equalArrayTypes(PropertyOptions options) {
return (
isArray() == options.isArray() &&
isArrayOrdered() == options.isArrayOrdered() &&
isArrayAlternate() == options.isArrayAlternate() &&
isArrayAltText() == options.isArrayAltText());
}
public void mergeWith(PropertyOptions options) throws XMPException {
if (options != null)
setOptions(getOptions() | options.getOptions());
}
public boolean isOnlyArrayOptions() {
return ((getOptions() & 0xFFFFE1FF) == 0);
}
protected int getValidOptions() {
return -1073733646;
}
protected String defineOptionName(int option) {
switch (option) {
case 2:
return "URI";
case 16:
return "HAS_QUALIFIER";
case 32:
return "QUALIFIER";
case 64:
return "HAS_LANGUAGE";
case 128:
return "HAS_TYPE";
case 256:
return "STRUCT";
case 512:
return "ARRAY";
case 1024:
return "ARRAY_ORDERED";
case 2048:
return "ARRAY_ALTERNATE";
case 4096:
return "ARRAY_ALT_TEXT";
case -2147483648:
return "SCHEMA_NODE";
}
return null;
}
public void assertConsistency(int options) throws XMPException {
if ((options & 0x100) > 0 && (options & 0x200) > 0)
throw new XMPException("IsStruct and IsArray options are mutually exclusive", 103);
if ((options & 0x2) > 0 && (options & 0x300) > 0)
throw new XMPException("Structs and arrays can't have \"value\" options", 103);
}
}

View file

@ -0,0 +1,226 @@
package com.itextpdf.xmp.options;
import com.itextpdf.xmp.XMPException;
public final class SerializeOptions extends Options {
public static final int OMIT_PACKET_WRAPPER = 16;
public static final int READONLY_PACKET = 32;
public static final int USE_COMPACT_FORMAT = 64;
public static final int USE_CANONICAL_FORMAT = 128;
public static final int INCLUDE_THUMBNAIL_PAD = 256;
public static final int EXACT_PACKET_LENGTH = 512;
public static final int OMIT_XMPMETA_ELEMENT = 4096;
public static final int SORT = 8192;
private static final int LITTLEENDIAN_BIT = 1;
private static final int UTF16_BIT = 2;
public static final int ENCODE_UTF8 = 0;
public static final int ENCODE_UTF16BE = 2;
public static final int ENCODE_UTF16LE = 3;
private static final int ENCODING_MASK = 3;
private int padding = 2048;
private String newline = "\n";
private String indent = " ";
private int baseIndent = 0;
private boolean omitVersionAttribute = false;
public SerializeOptions() {}
public SerializeOptions(int options) throws XMPException {
super(options);
}
public boolean getOmitPacketWrapper() {
return getOption(16);
}
public SerializeOptions setOmitPacketWrapper(boolean value) {
setOption(16, value);
return this;
}
public boolean getOmitXmpMetaElement() {
return getOption(4096);
}
public SerializeOptions setOmitXmpMetaElement(boolean value) {
setOption(4096, value);
return this;
}
public boolean getReadOnlyPacket() {
return getOption(32);
}
public SerializeOptions setReadOnlyPacket(boolean value) {
setOption(32, value);
return this;
}
public boolean getUseCompactFormat() {
return getOption(64);
}
public SerializeOptions setUseCompactFormat(boolean value) {
setOption(64, value);
return this;
}
public boolean getUseCanonicalFormat() {
return getOption(128);
}
public SerializeOptions setUseCanonicalFormat(boolean value) {
setOption(128, value);
return this;
}
public boolean getIncludeThumbnailPad() {
return getOption(256);
}
public SerializeOptions setIncludeThumbnailPad(boolean value) {
setOption(256, value);
return this;
}
public boolean getExactPacketLength() {
return getOption(512);
}
public SerializeOptions setExactPacketLength(boolean value) {
setOption(512, value);
return this;
}
public boolean getSort() {
return getOption(8192);
}
public SerializeOptions setSort(boolean value) {
setOption(8192, value);
return this;
}
public boolean getEncodeUTF16BE() {
return ((getOptions() & 0x3) == 2);
}
public SerializeOptions setEncodeUTF16BE(boolean value) {
setOption(3, false);
setOption(2, value);
return this;
}
public boolean getEncodeUTF16LE() {
return ((getOptions() & 0x3) == 3);
}
public SerializeOptions setEncodeUTF16LE(boolean value) {
setOption(3, false);
setOption(3, value);
return this;
}
public int getBaseIndent() {
return this.baseIndent;
}
public SerializeOptions setBaseIndent(int baseIndent) {
this.baseIndent = baseIndent;
return this;
}
public String getIndent() {
return this.indent;
}
public SerializeOptions setIndent(String indent) {
this.indent = indent;
return this;
}
public String getNewline() {
return this.newline;
}
public SerializeOptions setNewline(String newline) {
this.newline = newline;
return this;
}
public int getPadding() {
return this.padding;
}
public SerializeOptions setPadding(int padding) {
this.padding = padding;
return this;
}
public boolean getOmitVersionAttribute() {
return this.omitVersionAttribute;
}
public String getEncoding() {
if (getEncodeUTF16BE())
return "UTF-16BE";
if (getEncodeUTF16LE())
return "UTF-16LE";
return "UTF-8";
}
public Object clone() throws CloneNotSupportedException {
try {
SerializeOptions clone = new SerializeOptions(getOptions());
clone.setBaseIndent(this.baseIndent);
clone.setIndent(this.indent);
clone.setNewline(this.newline);
clone.setPadding(this.padding);
return clone;
} catch (XMPException e) {
return null;
}
}
protected String defineOptionName(int option) {
switch (option) {
case 16:
return "OMIT_PACKET_WRAPPER";
case 32:
return "READONLY_PACKET";
case 64:
return "USE_COMPACT_FORMAT";
case 256:
return "INCLUDE_THUMBNAIL_PAD";
case 512:
return "EXACT_PACKET_LENGTH";
case 4096:
return "OMIT_XMPMETA_ELEMENT";
case 8192:
return "NORMALIZED";
}
return null;
}
protected int getValidOptions() {
return 13168;
}
}

View file

@ -0,0 +1,13 @@
package com.itextpdf.xmp.properties;
import com.itextpdf.xmp.options.AliasOptions;
public interface XMPAliasInfo {
String getNamespace();
String getPrefix();
String getPropName();
AliasOptions getAliasForm();
}

View file

@ -0,0 +1,11 @@
package com.itextpdf.xmp.properties;
import com.itextpdf.xmp.options.PropertyOptions;
public interface XMPProperty {
String getValue();
PropertyOptions getOptions();
String getLanguage();
}

View file

@ -0,0 +1,13 @@
package com.itextpdf.xmp.properties;
import com.itextpdf.xmp.options.PropertyOptions;
public interface XMPPropertyInfo extends XMPProperty {
String getNamespace();
String getPath();
String getValue();
PropertyOptions getOptions();
}