first commit
This commit is contained in:
commit
4d332ef662
27586 changed files with 3281783 additions and 0 deletions
|
|
@ -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";
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.itextpdf.xmp;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
public interface XMPIterator extends Iterator {
|
||||
void skipSubtree();
|
||||
|
||||
void skipSiblings();
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.itextpdf.xmp;
|
||||
|
||||
public interface XMPVersionInfo {
|
||||
int getMajor();
|
||||
|
||||
int getMinor();
|
||||
|
||||
int getMicro();
|
||||
|
||||
int getBuild();
|
||||
|
||||
boolean isDebug();
|
||||
|
||||
String getMessage();
|
||||
}
|
||||
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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 };
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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("<");
|
||||
break;
|
||||
case '>':
|
||||
buffer.append(">");
|
||||
break;
|
||||
case '&':
|
||||
buffer.append("&");
|
||||
break;
|
||||
case '"':
|
||||
buffer.append(forAttribute ? """ : "\"");
|
||||
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 == '·');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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 == '〟');
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.itextpdf.xmp.properties;
|
||||
|
||||
import com.itextpdf.xmp.options.PropertyOptions;
|
||||
|
||||
public interface XMPProperty {
|
||||
String getValue();
|
||||
|
||||
PropertyOptions getOptions();
|
||||
|
||||
String getLanguage();
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue