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

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pdfbox-jbig2.git


The following commit(s) were added to refs/heads/master by this push:
     new 749e09b  PDFBOX-6162: handle shared symbol context properly
749e09b is described below

commit 749e09b774b85076bb9edd42583ded5a63f1d20f
Author: Maruan Sahyoun <[email protected]>
AuthorDate: Fri Apr 24 11:25:05 2026 +0200

    PDFBOX-6162: handle shared symbol context properly
---
 .../pdfbox/jbig2/segments/SymbolDictionary.java    | 84 ++++++++++++++++------
 .../org/apache/pdfbox/jbig2/SerenityTests.java     |  3 +-
 2 files changed, 64 insertions(+), 23 deletions(-)

diff --git 
a/src/main/java/org/apache/pdfbox/jbig2/segments/SymbolDictionary.java 
b/src/main/java/org/apache/pdfbox/jbig2/segments/SymbolDictionary.java
index 5cddb11..48f2eab 100644
--- a/src/main/java/org/apache/pdfbox/jbig2/segments/SymbolDictionary.java
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/SymbolDictionary.java
@@ -107,6 +107,8 @@ public class SymbolDictionary implements Dictionary
     protected CX cxIAID;
     private int sbSymCodeLen;
 
+    SymbolDictionary lastSymbolDictionary;
+
     public SymbolDictionary()
     {
     }
@@ -129,7 +131,6 @@ public class SymbolDictionary implements Dictionary
         setInSyms();
 
         boolean isContextAdopted = false;
-        SymbolDictionary lastSymbolDictionary = null;
 
         SegmentHeader[] rtSegments = segmentHeader.getRtSegments();
 
@@ -145,8 +146,6 @@ public class SymbolDictionary implements Dictionary
 
                     if (isCodingContextUsed && 
lastSymbolDictionary.isCodingContextRetained)
                     {
-                        /* 7.4.2.2 3) */
-                        adoptRetainedCodingContexts(lastSymbolDictionary);
                         isContextAdopted = true;
                     }
                     break;
@@ -296,7 +295,7 @@ public class SymbolDictionary implements Dictionary
     private void adoptRetainedCodingContexts(final SymbolDictionary sd) throws 
InvalidHeaderValueException
     {
         validateContextValues(sd);
-        this.cx = sd.cx;
+        this.cx = sd.cx.copy();
     }
     
     /**
@@ -376,6 +375,29 @@ public class SymbolDictionary implements Dictionary
         }
     }
 
+    private void ensureBitmapCxInitialized() throws 
InvalidHeaderValueException, IOException
+    {
+        if (cx != null)
+        {
+            return;
+        }
+
+        if (isCodingContextUsed)
+        {
+            if (lastSymbolDictionary == null)
+            {
+                throw new InvalidHeaderValueException(
+                    "Coding context reuse requested but no previous dictionary 
available");
+            }
+
+            adoptRetainedCodingContexts(lastSymbolDictionary);
+        }
+        else
+        {
+            resetBitmapCodingStatistics();
+        }
+    }
+
     /**
      * 6.5.5 Decoding the symbol dictionary
      * 
@@ -387,19 +409,23 @@ public class SymbolDictionary implements Dictionary
     {
         if (null == exportSymbols)
         {
+            ensureBitmapCxInitialized();
 
             if (useRefinementAggregation)
                 sbSymCodeLen = getSbSymCodeLen();
 
-            if (!isHuffmanEncoded)
-            {
-                if (!isCodingContextUsed)
-                {
-                    resetBitmapCodingStatistics();
-                } 
+            if (!isHuffmanEncoded) {
                 resetIntegerCoderStatistics();
             }
 
+            // decodes all referred segments including lastSymbolDictionary
+            setSymbolsArray();
+
+            // Now safe: lastSymbolDictionary was decoded by setSymbolsArray 
above
+            if (!isHuffmanEncoded && isCodingContextUsed) {
+                adoptRetainedCodingContexts(lastSymbolDictionary);
+            }
+
             /* 6.5.5 1) */
             newSymbols = new Bitmap[amountOfNewSymbols];
 
@@ -410,8 +436,6 @@ public class SymbolDictionary implements Dictionary
                 newSymbolsWidths = new int[amountOfNewSymbols];
             }
 
-            setSymbolsArray();
-
             /* 6.5.5 3) */
             int heightClassHeight = 0;
             amountOfDecodedSymbols = 0;
@@ -715,16 +739,37 @@ public class SymbolDictionary implements Dictionary
         }
     }
 
+    /**
+     * Decodes a new symbol using the provided parameters.
+     *
+     * @param symWidth The width of the symbol.
+     * @param hcHeight The height of the symbol.
+     * @param ibo The input bitmap object.
+     * @param rdx The x-offset for refinement.
+     * @param rdy The y-offset for refinement.
+     * @throws IllegalStateException if {@code cx} or {@code 
arithmeticDecoder} is not initialized.
+     * @throws IOException if an I/O error occurs during decoding.
+     * @throws InvalidHeaderValueException if an invalid header value is 
encountered.
+     * @throws IntegerMaxValueException if an integer value exceeds its 
maximum allowed value
+     * 
+     */
     private void decodeNewSymbols(final int symWidth, final int hcHeight, 
final Bitmap ibo,
             final int rdx, final int rdy)
             throws IOException, InvalidHeaderValueException, 
IntegerMaxValueException
     {
-        if (arithmeticDecoder == null) {
-            arithmeticDecoder = new ArithmeticDecoder(subInputStream);
+        // cx (bitmap coding context) must already be initialized via 
ensureBitmapCxInitialized()
+        // in getDictionary(). It is required by 
GenericRefinementRegionDecodingProcedure.decode
+        // and provides the arithmetic decoder statistics for bitmap decoding.
+        if (cx == null)
+        {
+            throw new IllegalStateException("CX not initialized (bug in 
initialization order)");
         }
 
-        if (cx == null) {
-            cx = new CX(65536, 1);
+        // arithmeticDecoder must already be initialized for the current 
bitstream context.
+        // It is required by GenericRefinementRegionDecodingProcedure.decode 
and is
+        // normally set during segment decoding (e.g., refinement or 
integer-coded paths).
+        if (arithmeticDecoder == null) {
+            throw new IllegalStateException("ArithmeticDecoder not 
initialized");
         }
 
         // Parameters as shown in Table 18, page 36
@@ -897,12 +942,9 @@ public class SymbolDictionary implements Dictionary
         {
             long exRunLength;
 
-            if (isHuffmanEncoded)
-            {
+            if (isHuffmanEncoded) {
                 exRunLength = 
StandardTables.getTable(1).decode(subInputStream);
-            }
-            else
-            {
+            } else {
                 exRunLength = iDecoder.decode(cxIAEX);
             }
 
diff --git a/src/test/java/org/apache/pdfbox/jbig2/SerenityTests.java 
b/src/test/java/org/apache/pdfbox/jbig2/SerenityTests.java
index 279d579..4a709b9 100644
--- a/src/test/java/org/apache/pdfbox/jbig2/SerenityTests.java
+++ b/src/test/java/org/apache/pdfbox/jbig2/SerenityTests.java
@@ -60,8 +60,7 @@ public class SerenityTests
         {
             String name = file.getName();
             // Files that are not properly decoded yet
-            if (name.equals("bitmap-refine-template1-tpgron.jbig2") ||
-                name.equals("bitmap-symbol-context-reuse.jbig2"))
+            if (name.equals("bitmap-refine-template1-tpgron.jbig2"))
             {
                 continue;
             }

Reply via email to