📄 xmpnormalizer.java
字号:
// =================================================================================================// ADOBE SYSTEMS INCORPORATED// Copyright 2006-2007 Adobe Systems Incorporated// All Rights Reserved//// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms// of the Adobe license agreement accompanying it.// =================================================================================================package com.adobe.xmp.impl;import java.util.Calendar;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import com.adobe.xmp.XMPConst;import com.adobe.xmp.XMPDateTime;import com.adobe.xmp.XMPError;import com.adobe.xmp.XMPException;import com.adobe.xmp.XMPMeta;import com.adobe.xmp.XMPMetaFactory;import com.adobe.xmp.XMPUtils;import com.adobe.xmp.impl.xpath.XMPPath;import com.adobe.xmp.impl.xpath.XMPPathParser;import com.adobe.xmp.options.ParseOptions;import com.adobe.xmp.options.PropertyOptions;import com.adobe.xmp.properties.XMPAliasInfo;/** * @since Aug 18, 2006 */public class XMPNormalizer{ /** caches the correct dc-property array forms */ private static Map dcArrayForms; /** init char tables */ static { initDCArrays(); } /** * Hidden constructor */ private XMPNormalizer() { // EMPTY } /** * Normalizes a raw parsed XMPMeta-Object * @param xmp the raw metadata object * @param options the parsing options * @return Returns the normalized metadata object * @throws XMPException Collects all severe processing errors. */ static XMPMeta process(XMPMetaImpl xmp, ParseOptions options) throws XMPException { XMPNode tree = xmp.getRoot(); touchUpDataModel(xmp); moveExplicitAliases(tree, options); tweakOldXMP(tree); deleteEmptySchemas(tree); return xmp; } /** * Tweak old XMP: Move an instance ID from rdf:about to the * <em>xmpMM:InstanceID</em> property. An old instance ID usually looks * like "uuid:bac965c4-9d87-11d9-9a30-000d936b79c4", plus InDesign * 3.0 wrote them like "bac965c4-9d87-11d9-9a30-000d936b79c4". If * the name looks like a UUID simply move it to <em>xmpMM:InstanceID</em>, * don't worry about any existing <em>xmpMM:InstanceID</em>. Both will * only be present when a newer file with the <em>xmpMM:InstanceID</em> * property is updated by an old app that uses <em>rdf:about</em>. * * @param tree the root of the metadata tree * @throws XMPException Thrown if tweaking fails. */ private static void tweakOldXMP(XMPNode tree) throws XMPException { if (tree.getName() != null && tree.getName().length() >= Utils.UUID_LENGTH) { String nameStr = tree.getName().toLowerCase(); if (nameStr.startsWith("uuid:")) { nameStr = nameStr.substring(5); } if (Utils.checkUUIDFormat(nameStr)) { // move UUID to xmpMM:InstanceID and remove it from the root node XMPPath path = XMPPathParser.expandXPath(XMPConst.NS_XMP_MM, "InstanceID"); XMPNode idNode = XMPNodeUtils.findNode (tree, path, true, null); if (idNode != null) { idNode.setOptions(null); // Clobber any existing xmpMM:InstanceID. idNode.setValue("uuid:" + nameStr); idNode.removeChildren(); idNode.removeQualifiers(); tree.setName(null); } else { throw new XMPException("Failure creating xmpMM:InstanceID", XMPError.INTERNALFAILURE); } } } } /** * Visit all schemas to do general fixes and handle special cases. * * @param xmp the metadata object implementation * @throws XMPException Thrown if the normalisation fails. */ private static void touchUpDataModel(XMPMetaImpl xmp) throws XMPException { // make sure the DC schema is existing, because it might be needed within the normalization // if not touched it will be removed by removeEmptySchemas XMPNodeUtils.findSchemaNode(xmp.getRoot(), XMPConst.NS_DC, true); // Do the special case fixes within each schema. for (Iterator it = xmp.getRoot().iterateChildren(); it.hasNext();) { XMPNode currSchema = (XMPNode) it.next(); if (XMPConst.NS_DC.equals(currSchema.getName())) { normalizeDCArrays(currSchema); } else if (XMPConst.NS_EXIF.equals(currSchema.getName())) { // Do a special case fix for exif:GPSTimeStamp. fixGPSTimeStamp(currSchema); XMPNode arrayNode = XMPNodeUtils.findChildNode(currSchema, "exif:UserComment", false); if (arrayNode != null) { repairAltText(arrayNode); } } else if (XMPConst.NS_DM.equals(currSchema.getName())) { // Do a special case migration of xmpDM:copyright to // dc:rights['x-default']. XMPNode dmCopyright = XMPNodeUtils.findChildNode(currSchema, "xmpDM:copyright", false); if (dmCopyright != null) { migrateAudioCopyright(xmp, dmCopyright); } } else if (XMPConst.NS_XMP_RIGHTS.equals(currSchema.getName())) { XMPNode arrayNode = XMPNodeUtils.findChildNode(currSchema, "xapRights:UsageTerms", false); if (arrayNode != null) { repairAltText(arrayNode); } } } } /** * Undo the denormalization performed by the XMP used in Acrobat 5.<br> * If a Dublin Core array had only one item, it was serialized as a simple * property. <br> * The <code>xml:lang</code> attribute was dropped from an * <code>alt-text</code> item if the language was <code>x-default</code>. * * @param dcSchema the DC schema node * @throws XMPException Thrown if normalization fails */ 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) { continue; } else if (currProp.getOptions().isSimple()) { // create a new array and add the current property as child, // if it was formerly simple XMPNode newArray = new XMPNode(currProp.getName(), arrayForm); currProp.setName(XMPConst.ARRAY_ITEM_NAME); newArray.addChild(currProp); dcSchema.replaceChild(i, newArray); // fix language alternatives if (arrayForm.isArrayAltText() && !currProp.getOptions().getHasLanguage()) { XMPNode newLang = new XMPNode(XMPConst.XML_LANG, XMPConst.X_DEFAULT, null); currProp.addQualifier(newLang); } } else { // clear array options and add corrected array form if it has been an array before currProp.getOptions().setOption( PropertyOptions.ARRAY | PropertyOptions.ARRAY_ORDERED | PropertyOptions.ARRAY_ALTERNATE | PropertyOptions.ARRAY_ALT_TEXT, false); currProp.getOptions().mergeWith(arrayForm); if (arrayForm.isArrayAltText()) { // applying for "dc:description", "dc:rights", "dc:title" repairAltText(currProp); } } } } /** * Make sure that the array is well-formed AltText. Each item must be simple * and have an "xml:lang" qualifier. If repairs are needed, keep simple * non-empty items by adding the "xml:lang" with value "x-repair". * @param arrayNode the property node of the array to repair. * @throws XMPException Forwards unexpected exceptions. */ private static void repairAltText(XMPNode arrayNode) throws XMPException { if (arrayNode == null || !arrayNode.getOptions().isArray()) { // Already OK or not even an array. return; } // fix options arrayNode.getOptions().setArrayOrdered(true).setArrayAlternate(true).setArrayAltText(true); for (Iterator it = arrayNode.iterateChildren(); it.hasNext();) { XMPNode currChild = (XMPNode) it.next(); if (currChild.getOptions().isCompositeProperty()) { // Delete non-simple children. it.remove(); } else if (!currChild.getOptions().getHasLanguage()) { String childValue = currChild.getValue(); if (childValue == null || childValue.length() == 0) { // Delete empty valued children that have no xml:lang. it.remove(); } else { // Add an xml:lang qualifier with the value "x-repair". XMPNode repairLang = new XMPNode(XMPConst.XML_LANG, "x-repair", null); currChild.addQualifier(repairLang); } } } } /** * Visit all of the top level nodes looking for aliases. If there is * no base, transplant the alias subtree. If there is a base and strict * aliasing is on, make sure the alias and base subtrees match. * * @param tree the root of the metadata tree * @param options th parsing options * @throws XMPException Forwards XMP errors */ private static void moveExplicitAliases(XMPNode tree, ParseOptions options) throws XMPException { if (!tree.getHasAliases()) { return; } tree.setHasAliases(false); boolean strictAliasing = options.getStrictAliasing(); for (Iterator schemaIt = tree.getUnmodifiableChildren().iterator(); schemaIt.hasNext();) { XMPNode currSchema = (XMPNode) schemaIt.next(); if (!currSchema.getHasAliases()) { continue; } for (Iterator propertyIt = currSchema.iterateChildren(); propertyIt.hasNext();) { XMPNode currProp = (XMPNode) propertyIt.next(); if (!currProp.isAlias()) { continue; } currProp.setAlias(false); // Find the base path, look for the base schema and root node. XMPAliasInfo info = XMPMetaFactory.getSchemaRegistry() .findAlias(currProp.getName()); if (info != null) { // find or create schema 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()) { // A top-to-top alias, transplant the property. // change the alias property name to the base name String qname = info.getPrefix() + info.getPropName(); currProp.setName(qname); baseSchema.addChild(currProp); // remove the alias property propertyIt.remove(); } else { // An alias to an array item, // create the array and transplant the property. baseNode = new XMPNode(info.getPrefix() + info.getPropName(), info
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -