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(