You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tika.apache.org by ta...@apache.org on 2026/08/07 21:09:21 UTC
(tika) branch TIKA-4808-extension created (now df4b9af870)
This is an automated email from the ASF dual-hosted git repository.
tballison pushed a change to branch TIKA-4808-extension
in repository https://gitbox.apache.org/repos/asf/tika.git
at df4b9af870 TIKA-4808 -- fix extension calculations for embedded files
This branch includes the following new commits:
new df4b9af870 TIKA-4808 -- fix extension calculations for embedded files
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(tika) 01/01: TIKA-4808 -- fix extension calculations for embedded files
Posted by ta...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tballison pushed a commit to branch TIKA-4808-extension
in repository https://gitbox.apache.org/repos/asf/tika.git
commit df4b9af87057df8036c63b9bfc187d1a31d8cfe9
Author: tallison <ta...@apache.org>
AuthorDate: Fri Aug 7 17:09:07 2026 -0400
TIKA-4808 -- fix extension calculations for embedded files
---
.../tika/extractor/EmbeddedDocumentUtil.java | 58 +++++-----
.../EmbeddedDocumentUtilExtensionTest.java | 120 +++++++++++++++++++++
2 files changed, 151 insertions(+), 27 deletions(-)
diff --git a/tika-core/src/main/java/org/apache/tika/extractor/EmbeddedDocumentUtil.java b/tika-core/src/main/java/org/apache/tika/extractor/EmbeddedDocumentUtil.java
index 324840ce84..f233d4e0b9 100644
--- a/tika-core/src/main/java/org/apache/tika/extractor/EmbeddedDocumentUtil.java
+++ b/tika-core/src/main/java/org/apache/tika/extractor/EmbeddedDocumentUtil.java
@@ -149,40 +149,24 @@ public class EmbeddedDocumentUtil implements Serializable {
MimeTypes localMimeTypes = getMimeTypes();
MimeType mimeType = null;
- boolean detected = false;
if (mimeString != null) {
- try {
- mimeType = localMimeTypes.forName(mimeString);
- } catch (MimeTypeException e) {
- //swallow
- }
+ mimeType = getRegisteredMimeType(localMimeTypes, mimeString);
}
if (mimeType == null) {
try {
MediaType mediaType = getDetector().detect(is, metadata, context);
- mimeType = localMimeTypes.forName(mediaType.toString());
- detected = true;
is.reset();
- } catch (IOException | MimeTypeException e) {
+ //set or correct the mime type. Record what was detected, not the
+ //registry match, which may have fallen back to the base type.
+ metadata.set(Metadata.CONTENT_TYPE, mediaType.toString());
+ mimeType = getRegisteredMimeType(localMimeTypes, mediaType.toString());
+ } catch (IOException e) {
//swallow
}
}
- if (mimeType != null) {
- if (detected) {
- //set or correct the mime type
- metadata.set(Metadata.CONTENT_TYPE, mimeType.toString());
- }
- return mimeType.getExtension();
- }
- return ".bin";
+ return mimeType == null ? ".bin" : mimeType.getExtension();
}
- /**
- * Looks up the file extension for a given media type string.
- *
- * @param mediaType the media type string (e.g., "image/png")
- * @return the extension including the dot (e.g., ".png"), or empty string if unknown
- */
/**
* Normalizes internal OCR routing media types (e.g., {@code image/ocr-png})
* back to standard media types (e.g., {@code image/png}).
@@ -198,16 +182,36 @@ public class EmbeddedDocumentUtil implements Serializable {
return mediaType;
}
+ /**
+ * Looks up the file extension for a given media type string.
+ *
+ * @param mediaType the media type string (e.g., "image/png"), parameters allowed
+ * @return the extension including the dot (e.g., ".png"), or empty string if unknown
+ */
public static String getExtensionForMediaType(String mediaType) {
if (mediaType == null) {
return "";
}
- mediaType = normalizeMediaType(mediaType);
+ MimeType mimeType =
+ getRegisteredMimeType(MimeTypes.getDefaultMimeTypes(),
+ normalizeMediaType(mediaType));
+ return mimeType == null ? "" : mimeType.getExtension();
+ }
+
+ /**
+ * Not {@link MimeTypes#forName(String)}: that registers a new, glob-less type for
+ * any name it doesn't recognize, so <code>text/plain; charset=UTF-8</code> would
+ * lose its extension and add a registry entry per charset seen. This prefers an
+ * exact parameterized match (<code>application/dita+xml;format=map</code> is real)
+ * and otherwise falls back to the base type.
+ *
+ * @return the registered type, or null if unknown or invalid
+ */
+ private static MimeType getRegisteredMimeType(MimeTypes mimeTypes, String name) {
try {
- MimeType mimeType = MimeTypes.getDefaultMimeTypes().forName(mediaType);
- return mimeType.getExtension();
+ return mimeTypes.getRegisteredMimeType(name);
} catch (MimeTypeException e) {
- return "";
+ return null;
}
}
diff --git a/tika-core/src/test/java/org/apache/tika/extractor/EmbeddedDocumentUtilExtensionTest.java b/tika-core/src/test/java/org/apache/tika/extractor/EmbeddedDocumentUtilExtensionTest.java
new file mode 100644
index 0000000000..4c9449e1ce
--- /dev/null
+++ b/tika-core/src/test/java/org/apache/tika/extractor/EmbeddedDocumentUtilExtensionTest.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tika.extractor;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import org.apache.tika.mime.MediaType;
+import org.apache.tika.mime.MimeTypes;
+
+/**
+ * TIKA-4808 -- a media type carrying parameters must still resolve to the
+ * extension of its base type.
+ */
+public class EmbeddedDocumentUtilExtensionTest {
+
+ @Test
+ public void testParametersDoNotSuppressExtension() {
+ //the corpus regression: Pkcs7Parser refines the coarse family label to the
+ //exact smime-type, which turned /embedded-2.p7s into /embedded-2
+ assertEquals(".p7m",
+ EmbeddedDocumentUtil.getExtensionForMediaType(
+ "application/pkcs7-mime; smime-type=signed-data"));
+ assertEquals(".txt",
+ EmbeddedDocumentUtil.getExtensionForMediaType("text/plain; charset=UTF-8"));
+ assertEquals(".js",
+ EmbeddedDocumentUtil.getExtensionForMediaType(
+ "text/javascript; charset=UTF-8"));
+ assertEquals(".css",
+ EmbeddedDocumentUtil.getExtensionForMediaType(
+ "text/css; charset=ISO-2022-JP"));
+ assertEquals(".html",
+ EmbeddedDocumentUtil.getExtensionForMediaType(
+ "text/html; charset=windows-1252"));
+ }
+
+ @Test
+ public void testUnparameterizedStillWorks() {
+ assertEquals(".p7s",
+ EmbeddedDocumentUtil.getExtensionForMediaType("application/pkcs7-signature"));
+ assertEquals(".png", EmbeddedDocumentUtil.getExtensionForMediaType("image/png"));
+ assertEquals(".txt", EmbeddedDocumentUtil.getExtensionForMediaType("text/plain"));
+ }
+
+ /**
+ * A registered type that genuinely has parameters must win over its base type.
+ */
+ @Test
+ public void testRegisteredParameterizedTypeWinsOverBaseType() {
+ assertEquals(".ditamap",
+ EmbeddedDocumentUtil.getExtensionForMediaType(
+ "application/dita+xml;format=map"));
+ assertEquals(".dita",
+ EmbeddedDocumentUtil.getExtensionForMediaType(
+ "application/dita+xml;format=topic"));
+ }
+
+ @Test
+ public void testUnknownAndNull() {
+ assertEquals("", EmbeddedDocumentUtil.getExtensionForMediaType(null));
+ assertEquals("",
+ EmbeddedDocumentUtil.getExtensionForMediaType("application/tika-bogus-xyz"));
+ }
+
+ @Test
+ public void testOcrRoutingTypeIsNormalized() {
+ assertEquals(".png", EmbeddedDocumentUtil.getExtensionForMediaType("image/ocr-png"));
+ }
+
+ /**
+ * The lookup must not register anything: forName() would add one glob-less entry
+ * per distinct parameter value seen, which on a large crawl grows without bound.
+ */
+ @Test
+ public void testLookupDoesNotPolluteRegistry() {
+ MimeTypes mimeTypes = MimeTypes.getDefaultMimeTypes();
+ int before = mimeTypes.getMediaTypeRegistry().getTypes().size();
+ for (int i = 0; i < 50; i++) {
+ EmbeddedDocumentUtil.getExtensionForMediaType("text/plain; charset=made-up-" + i);
+ }
+ assertEquals(before, mimeTypes.getMediaTypeRegistry().getTypes().size());
+ assertEquals(".txt",
+ EmbeddedDocumentUtil.getExtensionForMediaType("text/plain; charset=made-up-0"));
+ }
+
+ @Test
+ public void testGeneratedResourceNameKeepsExtension() {
+ assertEquals("embedded-2.p7m", EmbeddedDocumentUtil.generateResourceName(
+ EmbeddedDocumentUtil.EmbeddedResourcePrefix.EMBEDDED, 2,
+ "application/pkcs7-mime; smime-type=signed-data"));
+ assertEquals("image-0.png", EmbeddedDocumentUtil.generateResourceName(
+ EmbeddedDocumentUtil.EmbeddedResourcePrefix.IMAGE, 0, "image/png"));
+ }
+
+ /**
+ * Guard the assumption the fix rests on: normalize() deliberately preserves
+ * parameters, which is why forName() misses the registry for parameterized names.
+ */
+ @Test
+ public void testNormalizePreservesParameters() {
+ MediaType withParams = MediaType.parse("text/plain; charset=UTF-8");
+ assertEquals(withParams,
+ MimeTypes.getDefaultMimeTypes().getMediaTypeRegistry().normalize(withParams));
+ }
+}