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 205414aec2 TIKA-4995: IptcAnpaParser no longer carries the wire format 
across parses (#3408)
205414aec2 is described below

commit 205414aec2c6a97e27cf657fbf565cb656ac8d1a
Author: Tim Allison <[email protected]>
AuthorDate: Fri Oct 23 11:53:09 2026 -0400

    TIKA-4995: IptcAnpaParser no longer carries the wire format across parses 
(#3408)
    
    scanFormat started from the previous parse's FORMAT, so an unmarked wire
      parsed after a Reuters/AP/NYT/Bloomberg one inherited that agency's format
      rules and publisher. The format is now a per-parse local.
    
    Co-authored-by: Claude Opus 5.5 <[email protected]>
---
 CHANGES.txt                                        |  5 ++
 .../apache/tika/parser/iptc/IptcAnpaParser.java    | 85 ++++++++++------------
 .../tika/parser/iptc/IptcAnpaParserTest.java       | 58 +++++++++++++++
 3 files changed, 103 insertions(+), 45 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 71cc4dac73..265f33c8ce 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -192,6 +192,11 @@ Release 4.2.0 - 10/22/2026
      tk:chunks as JSON values rather than strings; new Property.ValueType.JSON
      (TIKA-4969).
 
+   * Parsers no longer carry one document's state into the next: IDML page 
counts no
+     longer accumulate, an ENVI header's multi-line field no longer absorbs 
the previous
+     field or document (#3406), and an IPTC/ANPA wire no longer inherits the 
previous
+     wire's agency format and publisher (TIKA-4995).
+
   OTHER CHANGES
 
    * tika-app -J writes the metadata list when the container throws, with the 
exception
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/main/java/org/apache/tika/parser/iptc/IptcAnpaParser.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/main/java/org/apache/tika/parser/iptc/IptcAnpaParser.java
index f2ec2b0fae..060f166fa4 100644
--- 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/main/java/org/apache/tika/parser/iptc/IptcAnpaParser.java
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/main/java/org/apache/tika/parser/iptc/IptcAnpaParser.java
@@ -79,21 +79,20 @@ public class IptcAnpaParser implements Parser {
     private final static char SR = 0x92;    // single-quote right
     private final static char DL = 0x93;    // double-quote left
     private final static char DR = 0x94;    // double-quote right
-    private int FMT_ANPA_1312 = 0x00;   // "NAA 89-3 (ANPA 1312)"
-    private int FMT_ANPA_UPI = 0x01;   // "United Press International ANPA 
1312 variant"
-    private int FMT_ANPA_UPI_DL = 0x02;   // "United Press International 
Down-Load Message"
-    private int FMT_IPTC_7901 = 0x03;   // "IPTC7901 Recommended Message 
Format"
-    private int FMT_IPTC_PHOTO = 0x04;   // "IPTC-NAA Digital Newsphoto 
Parameter Record"
-    private int FMT_IPTC_CHAR = 0x05;
+    private static final int FMT_ANPA_1312 = 0x00;   // "NAA 89-3 (ANPA 1312)"
+    private static final int FMT_ANPA_UPI = 0x01;   // "United Press 
International ANPA 1312 variant"
+    private static final int FMT_ANPA_UPI_DL = 0x02;   // "United Press 
International Down-Load Message"
+    private static final int FMT_IPTC_7901 = 0x03;   // "IPTC7901 Recommended 
Message Format"
+    private static final int FMT_IPTC_PHOTO = 0x04;   // "IPTC-NAA Digital 
Newsphoto Parameter Record"
+    private static final int FMT_IPTC_CHAR = 0x05;
             // "IPTC Unstructured Character Oriented File Format (UCOFF)"
-    private int FMT_NITF = 0x06;   // "News Industry Text Format (NITF)"
-    private int FMT_NITF_TT = 0x07;   // "Tidningarnas Telegrambyra NITF 
version (TTNITF DTD)"
-    private int FMT_NITF_RB = 0x08;   // "Ritzaus Bureau NITF version (RBNITF 
DTD)"
-    private int FMT_IPTC_AP = 0x09;   // "Associated Press news wire format"
-    private int FMT_IPTC_BLM = 0x0A;   // "Bloomberg News news wire format"
-    private int FMT_IPTC_NYT = 0x0B;   // "New York Times news wire format"
-    private int FMT_IPTC_RTR = 0x0C;   // "Reuters news wire format"
-    private int FORMAT = FMT_ANPA_1312;    // assume the default format to be 
ANPA-1312
+    private static final int FMT_NITF = 0x06;   // "News Industry Text Format 
(NITF)"
+    private static final int FMT_NITF_TT = 0x07;   // "Tidningarnas 
Telegrambyra NITF version (TTNITF DTD)"
+    private static final int FMT_NITF_RB = 0x08;   // "Ritzaus Bureau NITF 
version (RBNITF DTD)"
+    private static final int FMT_IPTC_AP = 0x09;   // "Associated Press news 
wire format"
+    private static final int FMT_IPTC_BLM = 0x0A;   // "Bloomberg News news 
wire format"
+    private static final int FMT_IPTC_NYT = 0x0B;   // "New York Times news 
wire format"
+    private static final int FMT_IPTC_RTR = 0x0C;   // "Reuters news wire 
format"
 
     public Set<MediaType> getSupportedTypes(ParseContext context) {
         return SUPPORTED_TYPES;
@@ -102,8 +101,9 @@ public class IptcAnpaParser implements Parser {
     public void parse(TikaInputStream tis, ContentHandler handler, Metadata 
metadata,
                       ParseContext context) throws IOException, SAXException, 
TikaException {
 
-        HashMap<String, String> properties = this.loadProperties(tis);
-        this.setMetadata(metadata, properties);
+        int format = scanFormat(tis);
+        HashMap<String, String> properties = loadProperties(tis, format);
+        setMetadata(metadata, properties, format);
 
         XHTMLContentHandler xhtml = new XHTMLContentHandler(handler, metadata, 
context);
         xhtml.startDocument();
@@ -129,29 +129,27 @@ public class IptcAnpaParser implements Parser {
     /**
      * scan the news messsage and store the metadata and data into a map
      */
-    private HashMap<String, String> loadProperties(InputStream is) {
+    private HashMap<String, String> loadProperties(InputStream is, int format) 
{
 
         HashMap<String, String> properties = new HashMap<>();
 
-        FORMAT = this.scanFormat(is);
-
         byte[] residual = this.getSection(is, "residual");
 
         byte[] header = this.getSection(is, "header");
         parseHeader(header, properties);
 
         byte[] body = this.getSection(is, "body");
-        parseBody(body, properties);
+        parseBody(body, properties, format);
 
         byte[] footer = this.getSection(is, "footer");
-        parseFooter(footer, properties);
+        parseFooter(footer, properties, format);
 
         return (properties);
     }
 
 
     private int scanFormat(InputStream is) {
-        int format = this.FORMAT;
+        int format = FMT_ANPA_1312;
         int maxsize = 524288;     //  512K
 
         byte[] buf = new byte[maxsize];
@@ -168,16 +166,16 @@ public class IptcAnpaParser implements Parser {
             // explicit codes to identify the wire source in the message itself
 
             if (message.contains("ap-wf")) {
-                format = this.FMT_IPTC_AP;
+                format = FMT_IPTC_AP;
             }
             if (message.contains("reuters")) {
-                format = this.FMT_IPTC_RTR;
+                format = FMT_IPTC_RTR;
             }
             if (message.contains("new york times")) {
-                format = this.FMT_IPTC_NYT;
+                format = FMT_IPTC_NYT;
             }
             if (message.contains("bloomberg news")) {
-                format = this.FMT_IPTC_BLM;
+                format = FMT_IPTC_BLM;
             }
         } catch (IOException eio) {
             // we are in an unstable state
@@ -194,22 +192,17 @@ public class IptcAnpaParser implements Parser {
     }
 
 
-    private void setFormat(int format) {
-        this.FORMAT = format;
-    }
-
-
-    private String getFormatName() {
+    private String getFormatName(int format) {
 
         String name = "";
 
-        if (FORMAT == this.FMT_IPTC_AP) {
+        if (format == FMT_IPTC_AP) {
             name = "Associated Press";
-        } else if (FORMAT == this.FMT_IPTC_BLM) {
+        } else if (format == FMT_IPTC_BLM) {
             name = "Bloomberg";
-        } else if (FORMAT == this.FMT_IPTC_NYT) {
+        } else if (format == FMT_IPTC_NYT) {
             name = "New York Times";
-        } else if (FORMAT == this.FMT_IPTC_RTR) {
+        } else if (format == FMT_IPTC_RTR) {
             name = "Reuters";
         }
 
@@ -460,7 +453,7 @@ public class IptcAnpaParser implements Parser {
         return added;
     }
 
-    private boolean parseBody(byte[] value, HashMap<String, String> 
properties) {
+    private boolean parseBody(byte[] value, HashMap<String, String> 
properties, int format) {
         boolean added = false;
 
         StringBuilder bdy_heading = new StringBuilder();
@@ -504,7 +497,7 @@ public class IptcAnpaParser implements Parser {
 
                     // for reuters, the heading does not start with the ^, so 
we push one back
                     // into the stream
-                    if (FORMAT == this.FMT_IPTC_RTR) {
+                    if (format == FMT_IPTC_RTR) {
                         if (val_next != CT) {
                             // for any non-whitespace, we need to go back an 
additional step to
                             // non destroy the data
@@ -563,7 +556,7 @@ public class IptcAnpaParser implements Parser {
 
                     // for bloomberg, the title does not start with the ^, so 
we push one back
                     // into the stream
-                    if (FORMAT == this.FMT_IPTC_BLM) {
+                    if (format == FMT_IPTC_BLM) {
                         if (val_next == TB) {
                             value[--read] = CT;
                             continue;
@@ -572,7 +565,7 @@ public class IptcAnpaParser implements Parser {
 
                     // for reuters, the title does not start with the ^, so we 
push one back into
                     // the stream
-                    if (FORMAT == this.FMT_IPTC_RTR) {
+                    if (format == FMT_IPTC_RTR) {
                         if (val_next != CT) {
                             // for any non-whitespace, we need to go back an 
additional step to
                             // non destroy the data
@@ -657,7 +650,7 @@ public class IptcAnpaParser implements Parser {
                         longline =
                                 ((tmp_line.toString().contains("=")) && 
(!longline.equals(longkey)) ? longkey :
                                         "");
-                    } else if (FORMAT == this.FMT_IPTC_BLM) {
+                    } else if (format == FMT_IPTC_BLM) {
                         String byline = "   by ";
                         if 
(tmp_line.toString().toLowerCase(Locale.ROOT).contains(byline)) {
                             longkey = "bdy_author";
@@ -755,7 +748,8 @@ public class IptcAnpaParser implements Parser {
     }
 
 
-    private boolean parseFooter(byte[] value, HashMap<String, String> 
properties) {
+    private boolean parseFooter(byte[] value, HashMap<String, String> 
properties,
+                                int format) {
         boolean added = false;
 
         StringBuilder ftr_source = new StringBuilder();
@@ -803,7 +797,7 @@ public class IptcAnpaParser implements Parser {
                     // standard ap format
                     String format_in = "MM-dd-yy HHmmzzz";
 
-                    if (FORMAT == this.FMT_IPTC_RTR) {
+                    if (format == FMT_IPTC_RTR) {
                         // standard reuters format
                         format_in = "HH:mm MM-dd-yy";
                     }
@@ -835,7 +829,8 @@ public class IptcAnpaParser implements Parser {
     }
 
 
-    private void setMetadata(Metadata metadata, HashMap<String, String> 
properties) {
+    private void setMetadata(Metadata metadata, HashMap<String, String> 
properties,
+                             int format) {
 
         // every property that gets set must be non-null, or it will cause NPE
         // in other consuming applications, like Lucene
@@ -847,7 +842,7 @@ public class IptcAnpaParser implements Parser {
         metadata.set(TikaCoreProperties.MODIFIED, 
clean(properties.get("modified")));
         metadata.set(TikaCoreProperties.SOURCE, 
clean(properties.get("source")));
 //      metadata.set(TikaCoreProperties.PUBLISHER,     
clean(properties.get("publisher")));
-        metadata.set(TikaCoreProperties.PUBLISHER, 
clean(this.getFormatName()));
+        metadata.set(TikaCoreProperties.PUBLISHER, 
clean(getFormatName(format)));
 
 /*
         metadata.set(TikaCoreProperties.DATE, 
font.getHeader().getCreated().getTime());
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/test/java/org/apache/tika/parser/iptc/IptcAnpaParserTest.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/test/java/org/apache/tika/parser/iptc/IptcAnpaParserTest.java
new file mode 100644
index 0000000000..e49f7cfc3d
--- /dev/null
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/test/java/org/apache/tika/parser/iptc/IptcAnpaParserTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.iptc;
+
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import org.apache.tika.TikaTest;
+import org.apache.tika.io.TikaInputStream;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.metadata.TikaCoreProperties;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.sax.BodyContentHandler;
+
+public class IptcAnpaParserTest extends TikaTest {
+
+    private static final String REUTERS = message("Markets rallied, Reuters 
reported.");
+    private static final String UNMARKED = message("Markets rallied on 
Tuesday.");
+
+    @Test
+    public void testFormatIsNotCarriedAcrossParses() throws Exception {
+        String expected = parse(new IptcAnpaParser(), 
UNMARKED).get(TikaCoreProperties.PUBLISHER);
+
+        IptcAnpaParser parser = new IptcAnpaParser();
+        assertEquals("Reuters", parse(parser, 
REUTERS).get(TikaCoreProperties.PUBLISHER));
+        assertEquals(expected, parse(parser, 
UNMARKED).get(TikaCoreProperties.PUBLISHER));
+    }
+
+    private static Metadata parse(IptcAnpaParser parser, String message) 
throws Exception {
+        Metadata metadata = new Metadata();
+        try (TikaInputStream tis = 
TikaInputStream.get(message.getBytes(US_ASCII))) {
+            parser.parse(tis, new BodyContentHandler(), metadata, new 
ParseContext());
+        }
+        return metadata;
+    }
+
+    // SYN SYN SOH header STX body ETX footer EOT
+    private static String message(String body) {
+        return "\u0016\u0016\u0001a0001 \u0013\u0011BC-Markets 10-21 
0000\u0002\n" + body +
+                "\n\u0003 21-10-26 0000GMT\u0004";
+    }
+}

Reply via email to