Skip to content

Commit da35998

Browse files
authored
TIKA-4775 - saxify xmp (#2946)
1 parent 63e22d0 commit da35998

49 files changed

Lines changed: 2661 additions & 1592 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ Release 4.0.0-beta-1 - 6/29/2026
2828
false, so an out-of-the-box tika-grpc server no longer accepts per-request
2929
configuration or runtime store mutations (TIKA-4764).
3030

31+
* Unified XMP extraction across containers; adds HEIF/HEIC and WebP XMP,
32+
including Samsung/Google Motion Photo (TIKA-4775).
33+
3134
OTHER CHANGES
3235

3336
* Release artifacts are now channel-specific. Maven Central gets slim

tika-bundles/tika-bundle-standard/pom.xml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,6 @@
113113
pdfbox|
114114
pdfbox-tools|
115115
fontbox|
116-
jempbox|
117-
xmpbox|
118116
bcjmail-jdk18on|
119117
bcprov-jdk18on|
120118
bcpkix-jdk18on|
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.tika.metadata;
18+
19+
/**
20+
* Properties from the Google Photos XMP namespaces used by Motion Photos and the legacy
21+
* MicroVideo format. See
22+
* <a href="https://developer.android.com/media/platform/motion-photo-format">the Motion Photo
23+
* format</a>.
24+
* <p>
25+
* Files use the {@code Camera} (current) or {@code GCamera} (legacy) prefix, but the namespace
26+
* URI is the same; the image XMP handler maps by URI, so these keys are stable regardless of
27+
* prefix.
28+
*/
29+
public interface Google {
30+
31+
String CAMERA_NS = "http://ns.google.com/photos/1.0/camera/";
32+
33+
String CONTAINER_NS = "http://ns.google.com/photos/1.0/container/";
34+
35+
String ITEM_NS = "http://ns.google.com/photos/1.0/container/item/";
36+
37+
Property MOTION_PHOTO = Property.externalText("Camera:MotionPhoto");
38+
39+
Property MOTION_PHOTO_VERSION = Property.externalText("Camera:MotionPhotoVersion");
40+
41+
Property MOTION_PHOTO_PRESENTATION_TIMESTAMP_US =
42+
Property.externalText("Camera:MotionPhotoPresentationTimestampUs");
43+
44+
Property MICRO_VIDEO = Property.externalText("Camera:MicroVideo");
45+
46+
Property MICRO_VIDEO_VERSION = Property.externalText("Camera:MicroVideoVersion");
47+
48+
Property MICRO_VIDEO_OFFSET = Property.externalText("Camera:MicroVideoOffset");
49+
50+
Property MICRO_VIDEO_PRESENTATION_TIMESTAMP_US =
51+
Property.externalText("Camera:MicroVideoPresentationTimestampUs");
52+
}

tika-core/src/main/java/org/apache/tika/metadata/PDF.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ public interface PDF {
3838
*/
3939
Property EOF_OFFSETS = Property.externalRealSeq(PDF_PREFIX + "eofOffsets");
4040

41+
/** Trapped flag from the XMP {@code pdf:} schema; docinfo counterpart {@link #DOC_INFO_TRAPPED}. */
42+
Property TRAPPED = Property.internalText(PDF_PREFIX + "Trapped");
43+
4144
/**
4245
* Prefix to be used for properties that record what was stored
4346
* in the docinfo section (as opposed to XMP)

tika-core/src/main/java/org/apache/tika/metadata/Photoshop.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ public interface Photoshop {
4747
PREFIX_PHOTOSHOP + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + "ColorMode",
4848
_COLOR_MODE_CHOICES_INDEXED);
4949

50+
Property ICC_PROFILE = Property.internalText(
51+
PREFIX_PHOTOSHOP + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + "ICCProfile");
52+
5053
Property CAPTION_WRITER = Property.internalText(
5154
PREFIX_PHOTOSHOP + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + "CaptionWriter");
5255

tika-core/src/main/java/org/apache/tika/metadata/TIFF.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,16 @@ public interface TIFF {
126126
Property ORIGINAL_DATE = Property.internalDate("exif:DateTimeOriginal");
127127

128128
Property EXIF_PAGE_COUNT = Property.externalInteger("exif:PageCount");
129+
130+
/** Camera-body serial number. */
131+
Property SERIAL_NUMBER = Property.internalText("aux:SerialNumber");
132+
133+
/** Human-readable lens description, e.g. "70-200mm f/2.8". */
134+
Property LENS = Property.internalText("aux:Lens");
135+
136+
/** Lens spec: min/max focal length and aperture. */
137+
Property LENS_INFO = Property.internalText("aux:LensInfo");
138+
139+
/** Numeric lens identifier. */
140+
Property LENS_ID = Property.internalText("aux:LensID");
129141
}

tika-core/src/main/java/org/apache/tika/metadata/XMPMM.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ public interface XMPMM {
9292
*/
9393
Property HISTORY_SOFTWARE_AGENT = Property.externalTextBag(PREFIX_ + "History:SoftwareAgent");
9494

95+
/** Part of the resource changed by the action (XMPMM history section). */
96+
Property HISTORY_CHANGED = Property.externalTextBag(PREFIX_ + "History:Changed");
97+
98+
/** Additional parameters describing the action (XMPMM history section). */
99+
Property HISTORY_PARAMETERS = Property.externalTextBag(PREFIX_ + "History:Parameters");
100+
95101
/**
96102
* Document id for the document that this document
97103
* was derived from
@@ -104,4 +110,12 @@ public interface XMPMM {
104110
*/
105111
Property DERIVED_FROM_INSTANCEID = Property.externalText(PREFIX_ + "DerivedFrom:InstanceID");
106112

113+
/** Original document id of the source this document was derived from. */
114+
Property DERIVED_FROM_ORIGINAL_DOCUMENTID =
115+
Property.externalText(PREFIX_ + "DerivedFrom:OriginalDocumentID");
116+
117+
/** Rendition class of the source this document was derived from. */
118+
Property DERIVED_FROM_RENDITION_CLASS =
119+
Property.externalText(PREFIX_ + "DerivedFrom:RenditionClass");
120+
107121
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.tika.metadata;
18+
19+
/**
20+
* Metadata keys for values that derive strictly from the XMP {@code tiff:} / {@code exif:}
21+
* schemas, mirroring {@link TIFF}. Tika folds these into the canonical {@link TIFF} keys, where
22+
* the same value can also arrive from binary EXIF; these parallel keys preserve provenance so a
23+
* consumer can distinguish an XMP-sourced value from a binary one.
24+
* <p>
25+
* Same pattern as {@link XMPDC}. Deliberately not implemented by {@link Metadata}; reference it
26+
* directly.
27+
*/
28+
public interface XMPTIFF {
29+
30+
String PREFIX_TIFF = "xmp" + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + "tiff";
31+
String PREFIX_EXIF = "xmp" + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + "exif";
32+
33+
Property EQUIPMENT_MAKE = Property.internalText(
34+
PREFIX_TIFF + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + "Make");
35+
36+
Property EQUIPMENT_MODEL = Property.internalText(
37+
PREFIX_TIFF + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + "Model");
38+
39+
Property SOFTWARE = Property.internalText(
40+
PREFIX_TIFF + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + "Software");
41+
42+
Property IMAGE_WIDTH = Property.internalInteger(
43+
PREFIX_TIFF + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + "ImageWidth");
44+
45+
Property IMAGE_LENGTH = Property.internalInteger(
46+
PREFIX_TIFF + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + "ImageLength");
47+
48+
Property BITS_PER_SAMPLE = Property.internalIntegerSequence(
49+
PREFIX_TIFF + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + "BitsPerSample");
50+
51+
Property SAMPLES_PER_PIXEL = Property.internalInteger(
52+
PREFIX_TIFF + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + "SamplesPerPixel");
53+
54+
Property ORIENTATION = Property.internalText(
55+
PREFIX_TIFF + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + "Orientation");
56+
57+
Property ORIGINAL_DATE = Property.internalDate(
58+
PREFIX_EXIF + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + "DateTimeOriginal");
59+
60+
Property ISO_SPEED_RATINGS = Property.internalIntegerSequence(
61+
PREFIX_EXIF + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + "IsoSpeedRatings");
62+
63+
/** Valid image width; folded into canonical {@link TIFF#IMAGE_WIDTH}. */
64+
Property PIXEL_X_DIMENSION = Property.internalInteger(
65+
PREFIX_EXIF + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + "PixelXDimension");
66+
67+
/** Valid image height; folded into canonical {@link TIFF#IMAGE_LENGTH}. */
68+
Property PIXEL_Y_DIMENSION = Property.internalInteger(
69+
PREFIX_EXIF + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + "PixelYDimension");
70+
}

tika-parent/pom.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,6 @@
381381
<jbig2.version>3.0.5</jbig2.version>
382382
<jcommander.version>1.82</jcommander.version>
383383
<jdom2.version>2.0.6.1</jdom2.version>
384-
<jempbox.version>1.8.17</jempbox.version>
385384
<jetty.version>12.1.11</jetty.version>
386385
<jetty.http2.version>12.1.11</jetty.http2.version>
387386
<jhighlight.version>2.0.0</jhighlight.version>

tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/BPGParser.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ void extractMetadata(InputStream stream, ContentHandler contentHandler, Metadata
165165
metadataExtractor.parseRawExif(stream, extensionLength, true);
166166
break;
167167
case EXTENSION_TAG_XMP:
168-
handleXMP(stream, extensionLength, metadataExtractor);
168+
handleXMP(stream, extensionLength, metadata, parseContext);
169169
break;
170170
default:
171171
IOUtils.skipFully(stream, extensionLength);
@@ -186,7 +186,8 @@ public int getMaxRecordLength() {
186186
return this.maxRecordLength;
187187
}
188188

189-
protected void handleXMP(InputStream stream, int xmpLength, ImageMetadataExtractor extractor)
189+
protected void handleXMP(InputStream stream, int xmpLength, Metadata metadata,
190+
ParseContext context)
190191
throws IOException, TikaException, SAXException {
191192
if (xmpLength < 0) {
192193
throw new TikaException("xmp length must be >= 0");
@@ -199,6 +200,6 @@ protected void handleXMP(InputStream stream, int xmpLength, ImageMetadataExtract
199200
}
200201
byte[] xmp = new byte[xmpLength];
201202
IOUtils.readFully(stream, xmp);
202-
extractor.parseRawXMP(xmp);
203+
ImageXmp.extractRaw(xmp, metadata, context);
203204
}
204205
}

0 commit comments

Comments
 (0)