This is an automated email from the ASF dual-hosted git repository.

tballison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git


The following commit(s) were added to refs/heads/main by this push:
     new 20c5b9cac6 [TIKA-4824] extract embedded JPEG previews from TIFF-based 
raw images (NEF, ARW, PEF, DNG, CR2) (#3037)
20c5b9cac6 is described below

commit 20c5b9cac6a679d872d6a37f2cc13a99d8c93443
Author: Dominik Schmidt <[email protected]>
AuthorDate: Thu Aug 20 20:12:28 2026 +0200

    [TIKA-4824] extract embedded JPEG previews from TIFF-based raw images (NEF, 
ARW, PEF, DNG, CR2) (#3037)
---
 CHANGES.txt                                        |  13 +
 .../org/apache/tika/mime/tika-mimetypes.xml        |   4 +
 .../java/org/apache/tika/mime/TestMimeTypes.java   |  13 +
 .../src/test/resources/test-documents/testARW.arw  | Bin 0 -> 1769 bytes
 .../src/test/resources/test-documents/testCR2.cr2  | Bin 0 -> 1673 bytes
 .../src/test/resources/test-documents/testDNG.dng  | Bin 0 -> 1820 bytes
 .../resources/test-documents/testDNG_bigtiff.dng   | Bin 0 -> 2111 bytes
 .../src/test/resources/test-documents/testNEF.nef  | Bin 0 -> 1986 bytes
 .../src/test/resources/test-documents/testPEF.pef  | Bin 0 -> 1724 bytes
 .../apache/tika/parser/image/RawTiffParser.java    | 541 +++++++++++++++++++++
 .../tika/parser/image/RawTiffParserTest.java       | 223 +++++++++
 .../tika-config-raw-preview-max-length.json        |  10 +
 .../configs/tika-config-raw-previews-off.json      |  10 +
 .../tika-config-raw-total-preview-budget.json      |  10 +
 .../src/test/resources/test-documents/testARW.arw  | Bin 0 -> 1769 bytes
 .../src/test/resources/test-documents/testCR2.cr2  | Bin 0 -> 1673 bytes
 .../src/test/resources/test-documents/testDNG.dng  | Bin 0 -> 1820 bytes
 .../resources/test-documents/testDNG_bigtiff.dng   | Bin 0 -> 2111 bytes
 .../src/test/resources/test-documents/testNEF.nef  | Bin 0 -> 1986 bytes
 .../test/resources/test-documents/testNEF_dup.nef  | Bin 0 -> 828 bytes
 .../src/test/resources/test-documents/testPEF.pef  | Bin 0 -> 1724 bytes
 21 files changed, 824 insertions(+)

diff --git a/CHANGES.txt b/CHANGES.txt
index 3b26ecb220..5c590593b5 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,16 @@
+Release 4.1.0 - ???
+
+   * RawTiffParser extracts the camera-generated JPEG previews embedded in
+     TIFF-based raw images (Nikon NEF/NRW, Sony ARW/SRF/SR2, Pentax PEF/PTX,
+     Adobe DNG and Canon CR2, including BigTIFF DNG containers) as thumbnail
+     embedded documents. image/x-raw-{nikon,sony,pentax,adobe} are now
+     sub-classes of image/tiff, so a named NEF/ARW/PEF/DNG that used to detect
+     as image/tiff (TiffParser, metadata only) now detects as image/x-raw-* and
+     emits thumbnail-N.jpg attachments in /rmeta and /unpack; CR2 keeps its
+     detection but also gains the attachments. Disable via
+     "raw-tiff-parser": {"extractPreviews": false} (TIKA-4824).
+
+
 Release 4.0.0 - 8/18/2026
 
   This section is the complete delta from 3.x. It includes everything first
diff --git 
a/tika-core/src/main/resources/org/apache/tika/mime/tika-mimetypes.xml 
b/tika-core/src/main/resources/org/apache/tika/mime/tika-mimetypes.xml
index a2f4374f4f..608edcbd72 100644
--- a/tika-core/src/main/resources/org/apache/tika/mime/tika-mimetypes.xml
+++ b/tika-core/src/main/resources/org/apache/tika/mime/tika-mimetypes.xml
@@ -7164,6 +7164,7 @@
   <mime-type type="image/x-raw-adobe">
     <acronym>DNG</acronym>
     <_comment>Adobe Digital Negative</_comment>
+    <sub-class-of type="image/tiff" />
     <glob pattern="*.dng"/>
   </mime-type>
 
@@ -7233,6 +7234,7 @@
 
   <mime-type type="image/x-raw-nikon">
     <_comment>Nikon raw image</_comment>
+    <sub-class-of type="image/tiff" />
     <glob pattern="*.nef"/>
     <glob pattern="*.nrw"/>
   </mime-type>
@@ -7247,12 +7249,14 @@
 
   <mime-type type="image/x-raw-pentax">
     <_comment>Pentax raw image</_comment>
+    <sub-class-of type="image/tiff" />
     <glob pattern="*.ptx"/>
     <glob pattern="*.pef"/>
   </mime-type>
 
   <mime-type type="image/x-raw-sony">
     <_comment>Sony raw image</_comment>
+    <sub-class-of type="image/tiff" />
     <glob pattern="*.arw"/>
     <glob pattern="*.srf"/>
     <glob pattern="*.sr2"/>
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/java/org/apache/tika/mime/TestMimeTypes.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/java/org/apache/tika/mime/TestMimeTypes.java
index b3e220235a..de2222afb6 100644
--- 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/java/org/apache/tika/mime/TestMimeTypes.java
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/java/org/apache/tika/mime/TestMimeTypes.java
@@ -894,6 +894,19 @@ public class TestMimeTypes {
         assertTypeByName("image/x-raw-minolta", "x.mrw");
         assertTypeByName("image/x-raw-nikon", "x.nef");
         assertTypeByName("image/x-raw-nikon", "x.nrw");
+        assertTypeByData("image/tiff", "testNEF.nef");
+        assertTypeByNameAndData("image/x-raw-nikon", "testNEF.nef");
+        assertTypeByData("image/tiff", "testARW.arw");
+        assertTypeByNameAndData("image/x-raw-sony", "testARW.arw");
+        assertTypeByData("image/tiff", "testPEF.pef");
+        assertTypeByNameAndData("image/x-raw-pentax", "testPEF.pef");
+        assertTypeByData("image/tiff", "testDNG.dng");
+        assertTypeByNameAndData("image/x-raw-adobe", "testDNG.dng");
+        //BigTIFF has no image/tiff data magic here (TiffParser can't read 
BigTIFF yet),
+        //so a BigTIFF DNG resolves by name+data via the glob, not by data 
alone
+        assertTypeByNameAndData("image/x-raw-adobe", "testDNG_bigtiff.dng");
+        assertTypeByData("image/x-canon-cr2", "testCR2.cr2");
+        assertTypeByNameAndData("image/x-canon-cr2", "testCR2.cr2");
         assertTypeByName("image/x-raw-olympus", "x.orf");
         assertTypeByName("image/x-raw-pentax", "x.ptx");
         assertTypeByName("image/x-raw-pentax", "x.pef");
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testARW.arw
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testARW.arw
new file mode 100644
index 0000000000..9e917adad7
Binary files /dev/null and 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testARW.arw
 differ
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testCR2.cr2
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testCR2.cr2
new file mode 100644
index 0000000000..c4e299798b
Binary files /dev/null and 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testCR2.cr2
 differ
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testDNG.dng
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testDNG.dng
new file mode 100644
index 0000000000..36b873918d
Binary files /dev/null and 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testDNG.dng
 differ
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testDNG_bigtiff.dng
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testDNG_bigtiff.dng
new file mode 100644
index 0000000000..c223eb0f07
Binary files /dev/null and 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testDNG_bigtiff.dng
 differ
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testNEF.nef
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testNEF.nef
new file mode 100644
index 0000000000..35eb669bca
Binary files /dev/null and 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testNEF.nef
 differ
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testPEF.pef
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testPEF.pef
new file mode 100644
index 0000000000..d4710e3e1a
Binary files /dev/null and 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/resources/test-documents/testPEF.pef
 differ
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/RawTiffParser.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/RawTiffParser.java
new file mode 100644
index 0000000000..a2176519a8
--- /dev/null
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/RawTiffParser.java
@@ -0,0 +1,541 @@
+/*
+ * 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.parser.image;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.io.Serializable;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.input.BoundedInputStream;
+import org.apache.commons.io.input.CloseShieldInputStream;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+import org.apache.tika.annotation.TikaComponent;
+import org.apache.tika.config.ConfigDeserializer;
+import org.apache.tika.config.JsonConfig;
+import org.apache.tika.config.ParseContextConfig;
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.extractor.EmbeddedDocumentExtractor;
+import org.apache.tika.extractor.EmbeddedDocumentUtil;
+import org.apache.tika.io.TikaInputStream;
+import org.apache.tika.metadata.HttpHeaders;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.metadata.TikaCoreProperties;
+import org.apache.tika.mime.MediaType;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.sax.XHTMLContentHandler;
+
+/**
+ * Parser for TIFF-based camera raw images: Nikon NEF/NRW, Sony ARW/SRF/SR2,
+ * Pentax PEF/PTX, Adobe DNG and Canon CR2.
+ * <p>
+ * These formats are TIFF containers: metadata extraction is inherited from
+ * {@link TiffParser}. In addition, this parser extracts the camera-generated
+ * JPEG preview images embedded in the raw file and hands them to the
+ * {@link EmbeddedDocumentExtractor}. Previews are referenced from the IFD
+ * chain or from SubIFDs, either via the JPEGInterchangeFormat/
+ * JPEGInterchangeFormatLength tags or as a single JPEG-compressed strip
+ * (DNG, CR2). Strips holding raw sensor data are also JPEG-encoded in some
+ * formats (lossless JPEG in CR2 and DNG), so strip candidates are only
+ * accepted for displayable images: PhotometricInterpretation RGB or YCbCr,
+ * or 8 bits per sample when PhotometricInterpretation is absent (CR2).
+ * Both classic TIFF and BigTIFF containers (allowed for DNG since spec
+ * version 1.7) are supported for preview extraction; for BigTIFF, EXIF
+ * metadata extraction is skipped until metadata-extractor supports it.
+ */
+@TikaComponent
+public class RawTiffParser extends TiffParser {
+
+    /**
+     * Serial version UID
+     */
+    private static final long serialVersionUID = 5385105345533384662L;
+
+    private static final Set<MediaType> SUPPORTED_TYPES = 
Collections.unmodifiableSet(
+            new HashSet<>(Arrays.asList(
+                    MediaType.image("x-raw-nikon"),
+                    MediaType.image("x-raw-sony"),
+                    MediaType.image("x-raw-pentax"),
+                    MediaType.image("x-raw-adobe"),
+                    MediaType.image("x-canon-cr2"))));
+
+    private static final String JPEG_MIME = "image/jpeg";
+
+    private static final int TAG_BITS_PER_SAMPLE = 0x0102;
+    private static final int TAG_COMPRESSION = 0x0103;
+    private static final int TAG_PHOTOMETRIC_INTERPRETATION = 0x0106;
+    private static final int TAG_STRIP_OFFSETS = 0x0111;
+    private static final int TAG_STRIP_BYTE_COUNTS = 0x0117;
+    private static final int TAG_SUB_IFDS = 0x014A;
+    private static final int TAG_JPEG_INTERCHANGE_FORMAT = 0x0201;
+    private static final int TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = 0x0202;
+
+    private static final int COMPRESSION_OLD_JPEG = 6;
+    private static final int COMPRESSION_JPEG = 7;
+    private static final int PHOTOMETRIC_RGB = 2;
+    private static final int PHOTOMETRIC_YCBCR = 6;
+
+    private static final int MAX_IFDS = 32;
+    private static final int MAX_ENTRIES_PER_IFD = 1024;
+    // at most MAX_IFDS are ever processed; cap the pending queue so a crafted
+    // file packed with SubIFD pointers cannot grow it without bound
+    private static final int MAX_PENDING_IFDS = 1024;
+    //previews are camera-generated JPEGs, tens of MB is already generous
+    private static final long DEFAULT_MAX_PREVIEW_LENGTH_BYTES = 100 * 1024 * 
1024;
+    //a real raw file holds a handful of small previews; bounding the aggregate
+    //stops a crafted file from pointing many IFDs at overlapping large regions
+    //to amplify extraction work well beyond the file's own size
+    private static final long DEFAULT_MAX_TOTAL_PREVIEW_BYTES = 256L * 1024 * 
1024;
+
+    private final RawTiffParserConfig defaultConfig;
+
+    public RawTiffParser() {
+        this(new RawTiffParserConfig());
+    }
+
+    public RawTiffParser(RawTiffParserConfig config) {
+        this.defaultConfig = config;
+    }
+
+    public RawTiffParser(JsonConfig jsonConfig) {
+        this(ConfigDeserializer.buildConfig(jsonConfig, 
RawTiffParserConfig.class));
+    }
+
+    @Override
+    public Set<MediaType> getSupportedTypes(ParseContext context) {
+        return SUPPORTED_TYPES;
+    }
+
+    @Override
+    public void parse(TikaInputStream tis, ContentHandler handler, Metadata 
metadata,
+                      ParseContext context) throws IOException, SAXException, 
TikaException {
+        if (isBigTiff(tis)) {
+            //metadata-extractor cannot read BigTIFF containers yet:
+            //scan for XMP, but skip EXIF metadata extraction
+            tis.getFile();
+            ImageXmp.scanAndExtract(tis, metadata, context);
+        } else {
+            extractMetadata(tis, handler, metadata, context);
+        }
+        RawTiffParserConfig config = getConfig(context);
+        XHTMLContentHandler xhtml = new XHTMLContentHandler(handler, metadata, 
context);
+        xhtml.startDocument();
+        if (config.isExtractPreviews()) {
+            extractPreviews(tis, xhtml, metadata, context, config);
+        }
+        xhtml.endDocument();
+    }
+
+    private RawTiffParserConfig getConfig(ParseContext context) throws 
TikaException, IOException {
+        return ParseContextConfig.getConfig(context, "raw-tiff-parser",
+                RawTiffParserConfig.class, defaultConfig);
+    }
+
+    private void extractPreviews(TikaInputStream tis, XHTMLContentHandler 
xhtml, Metadata metadata,
+                                 ParseContext context, RawTiffParserConfig 
config)
+            throws IOException, SAXException {
+        List<Preview> previews;
+        try (RandomAccessFile raf = new RandomAccessFile(tis.getFile(), "r")) {
+            previews = locateJpegPreviews(raf, config);
+        } catch (TiffStructureException | IOException e) {
+            //a file we cannot walk for previews should not fail the parse;
+            //the TIFF metadata has already been extracted at this point
+            EmbeddedDocumentUtil.recordException(e, metadata);
+            return;
+        }
+        if (previews.isEmpty()) {
+            return;
+        }
+        EmbeddedDocumentExtractor extractor =
+                EmbeddedDocumentUtil.getEmbeddedDocumentExtractor(context);
+        int count = 0;
+        //reuse one channel across previews, positioning to each offset in turn
+        try (FileChannel channel = FileChannel.open(tis.getPath())) {
+            for (Preview preview : previews) {
+                Metadata previewMetadata = Metadata.newInstance(context);
+                previewMetadata.set(TikaCoreProperties.EMBEDDED_RESOURCE_TYPE,
+                        
TikaCoreProperties.EmbeddedResourceType.THUMBNAIL.toString());
+                previewMetadata.set(HttpHeaders.CONTENT_TYPE, JPEG_MIME);
+                EmbeddedDocumentUtil.setGeneratedResourceName(previewMetadata,
+                        EmbeddedDocumentUtil.EmbeddedResourcePrefix.THUMBNAIL, 
count, JPEG_MIME);
+                count++;
+                if (!extractor.shouldParseEmbedded(previewMetadata, context)) {
+                    continue;
+                }
+                //stream the preview region instead of loading it onto the 
heap;
+                //close-shield the shared channel so parseEmbedded cannot 
close it
+                channel.position(preview.offset());
+                BoundedInputStream bounded = BoundedInputStream.builder()
+                        
.setInputStream(CloseShieldInputStream.wrap(Channels.newInputStream(channel)))
+                        .setMaxCount(preview.length())
+                        .get();
+                try (TikaInputStream previewStream = 
TikaInputStream.get(bounded)) {
+                    extractor.parseEmbedded(previewStream, xhtml, 
previewMetadata, context, true);
+                }
+            }
+        }
+    }
+
+    /**
+     * Walks the TIFF IFD chain and any SubIFDs (traversal bounded by
+     * {@link #MAX_IFDS}) and returns the embedded JPEG previews.
+     */
+    private List<Preview> locateJpegPreviews(RandomAccessFile raf, 
RawTiffParserConfig config)
+            throws IOException, TiffStructureException {
+        long fileLength = raf.length();
+        if (fileLength < 8) {
+            throw new TiffStructureException("file too short for a TIFF 
header");
+        }
+        raf.seek(0);
+        int b0 = raf.read();
+        int b1 = raf.read();
+        boolean bigEndian;
+        if (b0 == 'M' && b1 == 'M') {
+            bigEndian = true;
+        } else if (b0 == 'I' && b1 == 'I') {
+            bigEndian = false;
+        } else {
+            throw new TiffStructureException("not a TIFF byte order marker");
+        }
+        int magic = readUInt16(raf, bigEndian);
+        boolean bigTiff;
+        if (magic == 42) {
+            bigTiff = false;
+        } else if (magic == 43) {
+            //BigTIFF: 8-byte offset size, then a constant 0
+            bigTiff = true;
+            if (readUInt16(raf, bigEndian) != 8 || readUInt16(raf, bigEndian) 
!= 0) {
+                throw new TiffStructureException("unsupported BigTIFF header");
+            }
+        } else {
+            throw new TiffStructureException("bad TIFF magic number");
+        }
+        int countSize = bigTiff ? 8 : 2;
+        int entrySize = bigTiff ? 20 : 12;
+        int offsetSize = bigTiff ? 8 : 4;
+
+        List<Preview> previews = new ArrayList<>();
+        //distinct IFDs can reference the same JPEG region; extract each 
region once
+        Set<Long> previewOffsets = new HashSet<>();
+        long totalPreviewBytes = 0;
+        long maxTotalPreviewBytes = config.getMaxTotalPreviewBytes();
+        Set<Long> visited = new HashSet<>();
+        Deque<Long> toVisit = new ArrayDeque<>();
+        toVisit.add(readOffset(raf, bigEndian, bigTiff));
+
+        while (!toVisit.isEmpty() && visited.size() < MAX_IFDS) {
+            long ifdOffset = toVisit.poll();
+            if (ifdOffset <= 0 || !visited.add(ifdOffset)) {
+                continue;
+            }
+            // overflow-safe: ifdOffset is a 64-bit value from the file, so 
avoid
+            // ifdOffset + countSize which could wrap past Long.MAX_VALUE
+            if (ifdOffset > fileLength - countSize) {
+                continue;
+            }
+            raf.seek(ifdOffset);
+            long numEntries = bigTiff ? readUInt64(raf, bigEndian) : 
readUInt16(raf, bigEndian);
+            if (numEntries < 0 || numEntries > MAX_ENTRIES_PER_IFD ||
+                    ifdOffset + countSize + numEntries * entrySize + 
offsetSize > fileLength) {
+                continue;
+            }
+            //read the whole entry table (plus the trailing follower pointer) 
in one go and
+            //parse it from memory, so a crafted IFD cannot force a read 
syscall per byte
+            byte[] table = new byte[(int) (numEntries * entrySize + 
offsetSize)];
+            raf.seek(ifdOffset + countSize);
+            raf.readFully(table);
+            int inlineSize = bigTiff ? 8 : 4;
+            long jpegOffset = -1;
+            long jpegLength = -1;
+            long compression = -1;
+            long photometric = -1;
+            long[] bitsPerSample = new long[0];
+            long[] stripOffsets = new long[0];
+            long[] stripByteCounts = new long[0];
+            for (int i = 0; i < numEntries; i++) {
+                int e = i * entrySize;
+                int tag = getUInt16(table, e, bigEndian);
+                int type = getUInt16(table, e + 2, bigEndian);
+                long valueCount = bigTiff
+                        ? getUInt64(table, e + 4, bigEndian) : 
getUInt32(table, e + 4, bigEndian);
+                int valueField = e + 4 + inlineSize;
+                if (tag == TAG_SUB_IFDS) {
+                    for (long subIfdOffset :
+                            readLongValues(raf, table, valueField, bigEndian, 
bigTiff, type, valueCount)) {
+                        enqueue(toVisit, subIfdOffset);
+                    }
+                } else if (tag == TAG_JPEG_INTERCHANGE_FORMAT && valueCount == 
1) {
+                    long[] v = readLongValues(raf, table, valueField, 
bigEndian, bigTiff, type, valueCount);
+                    jpegOffset = v.length == 1 ? v[0] : -1;
+                } else if (tag == TAG_JPEG_INTERCHANGE_FORMAT_LENGTH && 
valueCount == 1) {
+                    long[] v = readLongValues(raf, table, valueField, 
bigEndian, bigTiff, type, valueCount);
+                    jpegLength = v.length == 1 ? v[0] : -1;
+                } else if (tag == TAG_COMPRESSION && valueCount == 1) {
+                    long[] v = readLongValues(raf, table, valueField, 
bigEndian, bigTiff, type, valueCount);
+                    compression = v.length == 1 ? v[0] : -1;
+                } else if (tag == TAG_PHOTOMETRIC_INTERPRETATION && valueCount 
== 1) {
+                    long[] v = readLongValues(raf, table, valueField, 
bigEndian, bigTiff, type, valueCount);
+                    photometric = v.length == 1 ? v[0] : -1;
+                } else if (tag == TAG_BITS_PER_SAMPLE) {
+                    bitsPerSample = readLongValues(raf, table, valueField, 
bigEndian, bigTiff, type, valueCount);
+                } else if (tag == TAG_STRIP_OFFSETS) {
+                    stripOffsets = readLongValues(raf, table, valueField, 
bigEndian, bigTiff, type, valueCount);
+                } else if (tag == TAG_STRIP_BYTE_COUNTS) {
+                    stripByteCounts = readLongValues(raf, table, valueField, 
bigEndian, bigTiff, type, valueCount);
+                }
+            }
+            int followerField = (int) (numEntries * entrySize);
+            enqueue(toVisit, bigTiff
+                    ? getUInt64(table, followerField, bigEndian) : 
getUInt32(table, followerField, bigEndian));
+
+            if (jpegOffset < 0 && isDisplayableJpegStrip(compression, 
photometric, bitsPerSample,
+                    stripOffsets, stripByteCounts)) {
+                jpegOffset = stripOffsets[0];
+                jpegLength = stripByteCounts[0];
+            }
+            if (jpegOffset > 0 && jpegLength > 4 &&
+                    jpegLength <= config.getMaxPreviewLengthBytes() &&
+                    jpegOffset <= fileLength - jpegLength) {
+                raf.seek(jpegOffset);
+                //require the JPEG SOI marker
+                if (raf.read() == 0xFF && raf.read() == 0xD8 && 
previewOffsets.add(jpegOffset)) {
+                    //bound the aggregate extraction work; overlapping offsets
+                    //could otherwise sum to many times the file's own size
+                    if (totalPreviewBytes > maxTotalPreviewBytes - jpegLength) 
{
+                        break;
+                    }
+                    totalPreviewBytes += jpegLength;
+                    previews.add(new Preview(jpegOffset, jpegLength));
+                }
+            }
+        }
+        return previews;
+    }
+
+    /**
+     * Decides whether an IFD without JPEGInterchangeFormat holds a JPEG
+     * preview as a single strip (DNG, CR2). Raw sensor data can also be
+     * JPEG-encoded (lossless JPEG), so the image must be displayable:
+     * PhotometricInterpretation RGB or YCbCr, or 8 bits per sample when
+     * PhotometricInterpretation is absent (CR2's preview IFD).
+     */
+    private boolean isDisplayableJpegStrip(long compression, long photometric,
+                                           long[] bitsPerSample, long[] 
stripOffsets,
+                                           long[] stripByteCounts) {
+        if (compression != COMPRESSION_OLD_JPEG && compression != 
COMPRESSION_JPEG) {
+            return false;
+        }
+        if (stripOffsets.length != 1 || stripByteCounts.length != 1) {
+            return false;
+        }
+        if (photometric == PHOTOMETRIC_RGB || photometric == 
PHOTOMETRIC_YCBCR) {
+            return true;
+        }
+        if (photometric != -1) {
+            return false;
+        }
+        if (bitsPerSample.length == 0) {
+            return false;
+        }
+        for (long bits : bitsPerSample) {
+            if (bits != 8) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean isBigTiff(TikaInputStream tis) throws IOException {
+        tis.mark(4);
+        try {
+            byte[] header = new byte[4];
+            if (IOUtils.read(tis, header) < 4) {
+                return false;
+            }
+            if (header[0] == 'I' && header[1] == 'I') {
+                return header[2] == 0x2B && header[3] == 0;
+            }
+            if (header[0] == 'M' && header[1] == 'M') {
+                return header[2] == 0 && header[3] == 0x2B;
+            }
+            return false;
+        } finally {
+            tis.reset();
+        }
+    }
+
+    private static void enqueue(Deque<Long> toVisit, long offset) {
+        if (toVisit.size() < MAX_PENDING_IFDS) {
+            toVisit.add(offset);
+        }
+    }
+
+    /**
+     * Reads the values of an integer-typed entry (SHORT, LONG, IFD and, for
+     * BigTIFF, LONG8/IFD8) from the in-memory entry table. Values that do not
+     * fit the inline value field are read once from their offset into a 
buffer;
+     * returns an empty array for other types.
+     */
+    private long[] readLongValues(RandomAccessFile raf, byte[] table, int 
valueField,
+                                  boolean bigEndian, boolean bigTiff, int 
type, long valueCount)
+            throws IOException {
+        int typeSize;
+        if (type == 3) {
+            typeSize = 2;
+        } else if (type == 4 || type == 13) {
+            typeSize = 4;
+        } else if (bigTiff && (type == 16 || type == 18)) {
+            typeSize = 8;
+        } else {
+            return new long[0];
+        }
+        if (valueCount < 1 || valueCount > MAX_ENTRIES_PER_IFD) {
+            return new long[0];
+        }
+        int count = (int) valueCount;
+        long totalBytes = (long) typeSize * count;
+        byte[] src;
+        int off;
+        if (totalBytes <= (bigTiff ? 8 : 4)) {
+            src = table;
+            off = valueField;
+        } else {
+            long valueOffset = bigTiff
+                    ? getUInt64(table, valueField, bigEndian) : 
getUInt32(table, valueField, bigEndian);
+            if (valueOffset < 0 || valueOffset > raf.length() - totalBytes) {
+                return new long[0];
+            }
+            src = new byte[(int) totalBytes];
+            raf.seek(valueOffset);
+            raf.readFully(src);
+            off = 0;
+        }
+        long[] values = new long[count];
+        for (int i = 0; i < count; i++) {
+            int p = off + i * typeSize;
+            if (typeSize == 2) {
+                values[i] = getUInt16(src, p, bigEndian);
+            } else if (typeSize == 4) {
+                values[i] = getUInt32(src, p, bigEndian);
+            } else {
+                values[i] = getUInt64(src, p, bigEndian);
+            }
+        }
+        return values;
+    }
+
+    private int readUInt16(RandomAccessFile raf, boolean bigEndian) throws 
IOException {
+        int a = raf.read();
+        int b = raf.read();
+        if (a < 0 || b < 0) {
+            throw new IOException("unexpected end of file");
+        }
+        return bigEndian ? (a << 8) | b : (b << 8) | a;
+    }
+
+    private long readUInt32(RandomAccessFile raf, boolean bigEndian) throws 
IOException {
+        long high = readUInt16(raf, bigEndian);
+        long low = readUInt16(raf, bigEndian);
+        return bigEndian ? (high << 16) | low : (low << 16) | high;
+    }
+
+    private long readUInt64(RandomAccessFile raf, boolean bigEndian) throws 
IOException {
+        long high = readUInt32(raf, bigEndian);
+        long low = readUInt32(raf, bigEndian);
+        return bigEndian ? (high << 32) | low : (low << 32) | high;
+    }
+
+    private long readOffset(RandomAccessFile raf, boolean bigEndian, boolean 
bigTiff)
+            throws IOException {
+        return bigTiff ? readUInt64(raf, bigEndian) : readUInt32(raf, 
bigEndian);
+    }
+
+    private static int getUInt16(byte[] b, int off, boolean bigEndian) {
+        int a = b[off] & 0xFF;
+        int c = b[off + 1] & 0xFF;
+        return bigEndian ? (a << 8) | c : (c << 8) | a;
+    }
+
+    private static long getUInt32(byte[] b, int off, boolean bigEndian) {
+        long high = getUInt16(b, off, bigEndian);
+        long low = getUInt16(b, off + 2, bigEndian);
+        return bigEndian ? (high << 16) | low : (low << 16) | high;
+    }
+
+    private static long getUInt64(byte[] b, int off, boolean bigEndian) {
+        long high = getUInt32(b, off, bigEndian);
+        long low = getUInt32(b, off + 4, bigEndian);
+        return bigEndian ? (high << 32) | low : (low << 32) | high;
+    }
+
+    private record Preview(long offset, long length) {
+    }
+
+    private static class TiffStructureException extends TikaException {
+        TiffStructureException(String msg) {
+            super(msg);
+        }
+    }
+
+    /**
+     * Configuration class for RawTiffParser.
+     */
+    public static class RawTiffParserConfig implements Serializable {
+
+        private static final long serialVersionUID = 1990316744955315312L;
+
+        private boolean extractPreviews = true;
+        private long maxPreviewLengthBytes = DEFAULT_MAX_PREVIEW_LENGTH_BYTES;
+        private long maxTotalPreviewBytes = DEFAULT_MAX_TOTAL_PREVIEW_BYTES;
+
+        public boolean isExtractPreviews() {
+            return extractPreviews;
+        }
+
+        public void setExtractPreviews(boolean extractPreviews) {
+            this.extractPreviews = extractPreviews;
+        }
+
+        public long getMaxPreviewLengthBytes() {
+            return maxPreviewLengthBytes;
+        }
+
+        public void setMaxPreviewLengthBytes(long maxPreviewLengthBytes) {
+            this.maxPreviewLengthBytes = maxPreviewLengthBytes;
+        }
+
+        public long getMaxTotalPreviewBytes() {
+            return maxTotalPreviewBytes;
+        }
+
+        public void setMaxTotalPreviewBytes(long maxTotalPreviewBytes) {
+            this.maxTotalPreviewBytes = maxTotalPreviewBytes;
+        }
+    }
+}
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/java/org/apache/tika/parser/image/RawTiffParserTest.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/java/org/apache/tika/parser/image/RawTiffParserTest.java
new file mode 100644
index 0000000000..dea3c32464
--- /dev/null
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/java/org/apache/tika/parser/image/RawTiffParserTest.java
@@ -0,0 +1,223 @@
+/*
+ * 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.parser.image;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.apache.tika.TikaTest;
+import org.apache.tika.config.loader.TikaLoader;
+import org.apache.tika.io.TikaInputStream;
+import org.apache.tika.metadata.HttpHeaders;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.metadata.TIFF;
+import org.apache.tika.metadata.TikaCoreProperties;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.parser.Parser;
+
+public class RawTiffParserTest extends TikaTest {
+
+    private List<Metadata> parseByName(String fileName) throws Exception {
+        Metadata metadata = new Metadata();
+        metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, fileName);
+        return getRecursiveMetadata(fileName, metadata);
+    }
+
+    private void assertPreview(Metadata preview, int index, int width, int 
height) {
+        assertEquals("image/jpeg", preview.get(HttpHeaders.CONTENT_TYPE));
+        assertEquals("thumbnail-" + index + ".jpg",
+                preview.get(TikaCoreProperties.RESOURCE_NAME_KEY));
+        
assertEquals(TikaCoreProperties.EmbeddedResourceType.THUMBNAIL.toString(),
+                preview.get(TikaCoreProperties.EMBEDDED_RESOURCE_TYPE));
+        assertEquals(Integer.toString(width), preview.get(TIFF.IMAGE_WIDTH));
+        assertEquals(Integer.toString(height), preview.get(TIFF.IMAGE_LENGTH));
+    }
+
+    @Test
+    public void testNEF() throws Exception {
+        List<Metadata> metadataList = parseByName("testNEF.nef");
+        assertEquals(2, metadataList.size());
+
+        Metadata container = metadataList.get(0);
+        assertEquals("image/x-raw-nikon", 
container.get(HttpHeaders.CONTENT_TYPE));
+        assertEquals("NIKON CORPORATION", container.get(TIFF.EQUIPMENT_MAKE));
+        assertEquals("NIKON D3000", container.get(TIFF.EQUIPMENT_MODEL));
+
+        assertPreview(metadataList.get(1), 0, 64, 48);
+    }
+
+    @Test
+    public void testDuplicatePreviewOffsetsDeduplicated() throws Exception {
+        //IFD0 and IFD1 both point their JPEGInterchangeFormat at the same 
region;
+        //it is extracted once, not per referencing IFD
+        List<Metadata> metadataList = parseByName("testNEF_dup.nef");
+        assertEquals(2, metadataList.size());
+        assertPreview(metadataList.get(1), 0, 64, 48);
+    }
+
+    @Test
+    public void testARW() throws Exception {
+        List<Metadata> metadataList = parseByName("testARW.arw");
+        assertEquals(3, metadataList.size());
+
+        Metadata container = metadataList.get(0);
+        assertEquals("image/x-raw-sony", 
container.get(HttpHeaders.CONTENT_TYPE));
+        assertEquals("SONY", container.get(TIFF.EQUIPMENT_MAKE));
+        assertEquals("NEX-6", container.get(TIFF.EQUIPMENT_MODEL));
+
+        //full-size preview from IFD0, then the thumbnail from IFD1
+        assertPreview(metadataList.get(1), 0, 64, 48);
+        assertPreview(metadataList.get(2), 1, 32, 24);
+    }
+
+    @Test
+    public void testPEF() throws Exception {
+        List<Metadata> metadataList = parseByName("testPEF.pef");
+        assertEquals(3, metadataList.size());
+
+        Metadata container = metadataList.get(0);
+        assertEquals("image/x-raw-pentax", 
container.get(HttpHeaders.CONTENT_TYPE));
+        assertEquals("PENTAX K-7", container.get(TIFF.EQUIPMENT_MODEL));
+
+        //thumbnail from IFD1, then the full-size preview from IFD2
+        assertPreview(metadataList.get(1), 0, 32, 24);
+        assertPreview(metadataList.get(2), 1, 64, 48);
+    }
+
+    @Test
+    public void testDNG() throws Exception {
+        List<Metadata> metadataList = parseByName("testDNG.dng");
+        //the raw strip in SubIFD0 also starts with a JPEG SOI marker
+        //(lossless JPEG) and must not be extracted
+        assertEquals(2, metadataList.size());
+
+        Metadata container = metadataList.get(0);
+        assertEquals("image/x-raw-adobe", 
container.get(HttpHeaders.CONTENT_TYPE));
+        assertEquals("PENTAX K-x", container.get(TIFF.EQUIPMENT_MODEL));
+
+        assertPreview(metadataList.get(1), 0, 64, 48);
+    }
+
+    @Test
+    public void testCR2() throws Exception {
+        List<Metadata> metadataList = parseByName("testCR2.cr2");
+        //the lossless-JPEG raw strip in the last IFD must not be extracted
+        assertEquals(3, metadataList.size());
+
+        Metadata container = metadataList.get(0);
+        assertEquals("image/x-canon-cr2", 
container.get(HttpHeaders.CONTENT_TYPE));
+        assertEquals("Canon EOS 7D", container.get(TIFF.EQUIPMENT_MODEL));
+
+        //full-size preview stored as a strip in IFD0, thumbnail from IFD1
+        assertPreview(metadataList.get(1), 0, 64, 48);
+        assertPreview(metadataList.get(2), 1, 32, 24);
+    }
+
+    @Test
+    public void testParsedByRawTiffParser() throws Exception {
+        List<Metadata> metadataList = parseByName("testNEF.nef");
+        List<String> parsedBy =
+                
Arrays.asList(metadataList.get(0).getValues(TikaCoreProperties.TIKA_PARSED_BY));
+        assertContains(RawTiffParser.class.getName(), parsedBy);
+    }
+
+    @Test
+    public void testBigTiffDNG() throws Exception {
+        //DNG allows BigTIFF containers since spec version 1.7
+        List<Metadata> metadataList = parseByName("testDNG_bigtiff.dng");
+        assertEquals(2, metadataList.size());
+
+        Metadata container = metadataList.get(0);
+        assertEquals("image/x-raw-adobe", 
container.get(HttpHeaders.CONTENT_TYPE));
+        
assertNull(container.get(TikaCoreProperties.TIKA_META_EXCEPTION_WARNING));
+
+        assertPreview(metadataList.get(1), 0, 64, 48);
+    }
+
+    @Test
+    public void testEmptyBigTiffWithoutException() throws Exception {
+        //a BigTIFF file without any previews must parse quietly,
+        //without recording an exception
+        byte[] bigTiff = new byte[]{
+                'M', 'M', 0, 0x2B, 0, 8, 0, 0,
+                0, 0, 0, 0, 0, 0, 0, 16,  //offset of IFD0
+                0, 0, 0, 0, 0, 0, 0, 0,   //IFD0: zero entries
+                0, 0, 0, 0, 0, 0, 0, 0    //no next IFD
+        };
+        Metadata metadata = new Metadata();
+        metadata.set(HttpHeaders.CONTENT_TYPE, "image/x-raw-adobe");
+        try (TikaInputStream tis = TikaInputStream.get(bigTiff)) {
+            new RawTiffParser().parse(tis, new DefaultHandler(), metadata, new 
ParseContext());
+        }
+        
assertNull(metadata.get(TikaCoreProperties.TIKA_META_EXCEPTION_WARNING));
+    }
+
+    @Test
+    public void testMaxPreviewLength() throws Exception {
+        Parser parser = TikaLoader
+                .load(getConfigPath(RawTiffParserTest.class,
+                        "tika-config-raw-preview-max-length.json"))
+                .loadParsers();
+        Metadata metadata = new Metadata();
+        metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, "testNEF.nef");
+        metadata.set(HttpHeaders.CONTENT_TYPE, "image/x-raw-nikon");
+        List<Metadata> metadataList =
+                getRecursiveMetadata("testNEF.nef", parser, metadata, new 
ParseContext(), false);
+
+        //the preview is larger than the configured limit and is skipped
+        assertEquals(1, metadataList.size());
+    }
+
+    @Test
+    public void testMaxTotalPreviewBytes() throws Exception {
+        //testARW has two previews (676 + 644 bytes); a 700-byte total budget
+        //admits only the first, bounding aggregate extraction work
+        Parser parser = TikaLoader
+                .load(getConfigPath(RawTiffParserTest.class,
+                        "tika-config-raw-total-preview-budget.json"))
+                .loadParsers();
+        Metadata metadata = new Metadata();
+        metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, "testARW.arw");
+        metadata.set(HttpHeaders.CONTENT_TYPE, "image/x-raw-sony");
+        List<Metadata> metadataList =
+                getRecursiveMetadata("testARW.arw", parser, metadata, new 
ParseContext(), false);
+
+        assertEquals(2, metadataList.size());
+        assertPreview(metadataList.get(1), 0, 64, 48);
+    }
+
+    @Test
+    public void testExtractPreviewsDisabled() throws Exception {
+        Parser parser = TikaLoader
+                .load(getConfigPath(RawTiffParserTest.class, 
"tika-config-raw-previews-off.json"))
+                .loadParsers();
+        Metadata metadata = new Metadata();
+        metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, "testNEF.nef");
+        metadata.set(HttpHeaders.CONTENT_TYPE, "image/x-raw-nikon");
+        List<Metadata> metadataList =
+                getRecursiveMetadata("testNEF.nef", parser, metadata, new 
ParseContext(), false);
+
+        assertEquals(1, metadataList.size());
+        assertEquals("NIKON CORPORATION", 
metadataList.get(0).get(TIFF.EQUIPMENT_MAKE));
+    }
+}
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/configs/tika-config-raw-preview-max-length.json
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/configs/tika-config-raw-preview-max-length.json
new file mode 100644
index 0000000000..210bf2807e
--- /dev/null
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/configs/tika-config-raw-preview-max-length.json
@@ -0,0 +1,10 @@
+{
+  "parsers": [
+    "default-parser",
+    {
+      "raw-tiff-parser": {
+        "maxPreviewLengthBytes": 100
+      }
+    }
+  ]
+}
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/configs/tika-config-raw-previews-off.json
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/configs/tika-config-raw-previews-off.json
new file mode 100644
index 0000000000..47f2f7ad07
--- /dev/null
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/configs/tika-config-raw-previews-off.json
@@ -0,0 +1,10 @@
+{
+  "parsers": [
+    "default-parser",
+    {
+      "raw-tiff-parser": {
+        "extractPreviews": false
+      }
+    }
+  ]
+}
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/configs/tika-config-raw-total-preview-budget.json
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/configs/tika-config-raw-total-preview-budget.json
new file mode 100644
index 0000000000..69b7617fed
--- /dev/null
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/configs/tika-config-raw-total-preview-budget.json
@@ -0,0 +1,10 @@
+{
+  "parsers": [
+    "default-parser",
+    {
+      "raw-tiff-parser": {
+        "maxTotalPreviewBytes": 700
+      }
+    }
+  ]
+}
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testARW.arw
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testARW.arw
new file mode 100644
index 0000000000..9e917adad7
Binary files /dev/null and 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testARW.arw
 differ
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testCR2.cr2
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testCR2.cr2
new file mode 100644
index 0000000000..c4e299798b
Binary files /dev/null and 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testCR2.cr2
 differ
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testDNG.dng
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testDNG.dng
new file mode 100644
index 0000000000..36b873918d
Binary files /dev/null and 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testDNG.dng
 differ
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testDNG_bigtiff.dng
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testDNG_bigtiff.dng
new file mode 100644
index 0000000000..c223eb0f07
Binary files /dev/null and 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testDNG_bigtiff.dng
 differ
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testNEF.nef
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testNEF.nef
new file mode 100644
index 0000000000..35eb669bca
Binary files /dev/null and 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testNEF.nef
 differ
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testNEF_dup.nef
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testNEF_dup.nef
new file mode 100644
index 0000000000..fa340502e4
Binary files /dev/null and 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testNEF_dup.nef
 differ
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testPEF.pef
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testPEF.pef
new file mode 100644
index 0000000000..d4710e3e1a
Binary files /dev/null and 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/resources/test-documents/testPEF.pef
 differ

Reply via email to