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 da67b78787 TIKA-5035: Mark the FictionBook cover as THUMBNAIL (#3527)
da67b78787 is described below

commit da67b78787a205245324813ae36a6efcc0679769
Author: Dominik Schmidt <[email protected]>
AuthorDate: Thu Nov 5 17:30:44 2026 +0100

    TIKA-5035: Mark the FictionBook cover as THUMBNAIL (#3527)
    
    * TIKA-5035: mark the FictionBook cover as THUMBNAIL
    
    * TIKA-5035: list FictionBook among the thumbnail sources
---
 CHANGES.txt                                        |  3 +++
 .../ROOT/pages/using-tika/server/index.adoc        | 12 +++++------
 .../tika/parser/xml/FictionBookParserTest.java     | 21 +++++++++++++++++-
 .../apache/tika/parser/xml/FictionBookParser.java  | 25 ++++++++++++++++++++--
 4 files changed, 52 insertions(+), 9 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index ced529cb23..7766a1945d 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -72,6 +72,9 @@ Release 4.3.0 - ???
 
   EXTRACTION AND FORMATS
 
+   * FictionBook marks the cover image named in <coverpage> as a THUMBNAIL 
embedded
+     resource, so the thumbnail preset serves it (TIKA-5035).
+     
    * MP4Parser extracts text subtitle tracks (tx3g/mov_text, QuickTime text) as
      embedded text/vtt documents named track-<id>.vtt, synthesized from the 
samples
      and parsed by SubtitleParser, with the track language as dc:language (ISO 
639-2;
diff --git a/docs/modules/ROOT/pages/using-tika/server/index.adoc 
b/docs/modules/ROOT/pages/using-tika/server/index.adoc
index 8bdec8870e..03bb991fa8 100644
--- a/docs/modules/ROOT/pages/using-tika/server/index.adoc
+++ b/docs/modules/ROOT/pages/using-tika/server/index.adoc
@@ -200,12 +200,12 @@ renders, so the presets do not make them. Both name the 
standard parsers, so the
 `tika-parsers-standard-package` on the classpath.
 
 `/unpack/preset/thumbnail` — "just give me the thumbnail". The zip holds at 
most one raster
-image per request and nothing else: the stored thumbnail (Office docProps, 
audio cover art,
-raw camera previews, epub, Mobipocket, PSD, iWork, the cover of a cbz, the 
preview of Krita and
-other zip-based formats), the rasterized EMF/WMF thumbnail, or the PDF page; 
`204` when the
-document has none. An image file is its own thumbnail: it has no embedded 
documents, so it
-answers `204` too. Text is read from the first PDF page only, no OCR or 
inference runs, and
-embedded documents are parsed two levels deep at most, so the parse is cheap.
+image per request and nothing else: the stored thumbnail (Office docProps, 
audio cover art, raw
+camera previews, epub, Mobipocket, PSD, FictionBook, iWork, the cover of a 
cbz, the preview of
+Krita and other zip-based formats), the rasterized EMF/WMF thumbnail, or the 
PDF page; `204`
+when the document has none. An image file is its own thumbnail: it has no 
embedded documents,
+so it answers `204` too. Text is read from the first PDF page only, no OCR or 
inference runs,
+and embedded documents are parsed two levels deep at most, so the parse is 
cheap.
 
 `/rmeta/preset/render-thumbnail` — "give me everything, thumbnail included". A 
full parse
 plus the two renders above, so an index gets the thumbnail's type and 
dimensions from the same
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/java/org/apache/tika/parser/xml/FictionBookParserTest.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/java/org/apache/tika/parser/xml/FictionBookParserTest.java
index 1d8f5715c3..2f22445b21 100644
--- 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/java/org/apache/tika/parser/xml/FictionBookParserTest.java
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/java/org/apache/tika/parser/xml/FictionBookParserTest.java
@@ -17,8 +17,10 @@
 package org.apache.tika.parser.xml;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
 
 import java.io.InputStream;
+import java.util.List;
 
 import org.junit.jupiter.api.Test;
 
@@ -26,9 +28,11 @@ import org.apache.tika.TikaTest;
 import org.apache.tika.extractor.ContainerExtractor;
 import org.apache.tika.extractor.ParserContainerExtractor;
 import org.apache.tika.io.TikaInputStream;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.metadata.TikaCoreProperties;
 import org.apache.tika.parser.ParseContext;
 
-public class FictionBookParserTest {
+public class FictionBookParserTest extends TikaTest {
 
     @Test
     public void testEmbedded() throws Exception {
@@ -46,4 +50,19 @@ public class FictionBookParserTest {
             assertEquals(2, handler.filenames.size());
         }
     }
+
+    @Test
+    public void testCoverIsThumbnail() throws Exception {
+        List<Metadata> metadataList = getRecursiveMetadata("test.fb2");
+        assertEquals(3, metadataList.size());
+        for (Metadata m : metadataList.subList(1, metadataList.size())) {
+            String name = m.get(TikaCoreProperties.RESOURCE_NAME_KEY);
+            String type = m.get(TikaCoreProperties.EMBEDDED_RESOURCE_TYPE);
+            if ("cover.jpg".equals(name)) {
+                
assertEquals(TikaCoreProperties.EmbeddedResourceType.THUMBNAIL.toString(), 
type);
+            } else {
+                assertNull(type, name);
+            }
+        }
+    }
 }
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/FictionBookParser.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/FictionBookParser.java
index 7a27ad1f7f..9350a5965e 100644
--- 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/FictionBookParser.java
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/FictionBookParser.java
@@ -59,11 +59,17 @@ public class FictionBookParser extends XMLParser {
         private static final String ELEMENT_BINARY = "binary";
         private static final String ATTRIBUTE_ID = "id";
         private static final String ATTRIBUTE_CONTENT_TYPE = "content-type";
+        private static final String ELEMENT_COVERPAGE = "coverpage";
+        private static final String ELEMENT_IMAGE = "image";
+        private static final String XLINK_NS = "http://www.w3.org/1999/xlink";;
+        private static final String ATTRIBUTE_HREF = "href";
         private final EmbeddedDocumentExtractor partExtractor;
         private final ContentHandler handler;
         private final ParseContext context;
         private final StringBuilder binaryData = new StringBuilder();
         private boolean binaryMode = false;
+        private boolean inCoverpage = false;
+        private String coverId;
         private Metadata metadata;
 
         private BinaryElementsDataHandler(EmbeddedDocumentExtractor 
partExtractor,
@@ -76,19 +82,34 @@ public class FictionBookParser extends XMLParser {
         @Override
         public void startElement(String uri, String localName, String qName, 
Attributes attributes)
                 throws SAXException {
+            if (ELEMENT_COVERPAGE.equals(localName)) {
+                inCoverpage = true;
+            } else if (inCoverpage && coverId == null && 
ELEMENT_IMAGE.equals(localName)) {
+                String href = attributes.getValue(XLINK_NS, ATTRIBUTE_HREF);
+                if (href != null && href.startsWith("#")) {
+                    coverId = href.substring(1);
+                }
+            }
             binaryMode = ELEMENT_BINARY.equals(localName);
             if (binaryMode) {
                 binaryData.setLength(0);
                 metadata = Metadata.newInstance(context);
 
-                metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY,
-                        attributes.getValue(ATTRIBUTE_ID));
+                String id = attributes.getValue(ATTRIBUTE_ID);
+                metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, id);
                 metadata.set(HttpHeaders.CONTENT_TYPE, 
attributes.getValue(ATTRIBUTE_CONTENT_TYPE));
+                if (id != null && id.equals(coverId)) {
+                    metadata.set(TikaCoreProperties.EMBEDDED_RESOURCE_TYPE,
+                            
TikaCoreProperties.EmbeddedResourceType.THUMBNAIL.toString());
+                }
             }
         }
 
         @Override
         public void endElement(String uri, String localName, String qName) 
throws SAXException {
+            if (ELEMENT_COVERPAGE.equals(localName)) {
+                inCoverpage = false;
+            }
             if (binaryMode) {
                 try (TikaInputStream tis = 
TikaInputStream.get(Base64.decodeBase64(binaryData.toString()))) {
                     partExtractor.parseEmbedded(

Reply via email to