Author: tilman
Date: Mon Mar 23 05:52:27 2026
New Revision: 1932473

Log:
PDFBOX-6178, PDFBOX-4076: use byte[] instead of String for internal storage in 
COSName, by Maruan Sahyoun (backported 3.0 changes)

Added:
   
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdfparser/TestCOSParser.java
      - copied, changed from r1932462, 
pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdfparser/TestCOSParser.java
Modified:
   pdfbox/branches/2.0/pdfbox/pom.xml
   pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java
   
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/BaseParser.java
   
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/cos/TestCOSName.java

Modified: pdfbox/branches/2.0/pdfbox/pom.xml
==============================================================================
--- pdfbox/branches/2.0/pdfbox/pom.xml  Mon Mar 23 04:20:05 2026        
(r1932472)
+++ pdfbox/branches/2.0/pdfbox/pom.xml  Mon Mar 23 05:52:27 2026        
(r1932473)
@@ -844,6 +844,32 @@
                             
<sha512>60e7b46f11a655083a57f4a627edf75bee477cd1ebfb06fbaefeb8ba16a01be37039cfd51c7c181a3ea368b53d22e327d480a7b0699c0edb2e06b4faaae7790f</sha512>
                         </configuration>
                     </execution>
+                    <execution>
+                        <id>PDFBOX-6178</id>
+                        <phase>generate-test-resources</phase>
+                        <goals>
+                            <goal>wget</goal>
+                        </goals>
+                        <configuration>
+                            
<url>https://issues.apache.org/jira/secure/attachment/13081297/form_empty.pdf</url>
+                            
<outputDirectory>${project.build.directory}/pdfs</outputDirectory>
+                            <outputFileName>PDFBOX-6178.pdf</outputFileName>
+                            
<sha512>d39486af0614bd099167a6adaab833aed41a0ebec7b85b13b382a2fdb6fddbcaaea9ab26ed0a81b72822258c8dd66dd535fa5c76afd1e5a8b1bff7d81e890274</sha512>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>PDFBOX-6178-1</id>
+                        <phase>generate-test-resources</phase>
+                        <goals>
+                            <goal>wget</goal>
+                        </goals>
+                        <configuration>
+                            
<url>https://issues.apache.org/jira/secure/attachment/13081311/form_selected_ASCII_NUL_acrobat.pdf</url>
+                            
<outputDirectory>${project.build.directory}/pdfs</outputDirectory>
+                            <outputFileName>PDFBOX-6178-1.pdf</outputFileName>
+                            
<sha512>83bc557e6f7d3e98de6e81168b2e2fb3def5025cc5fab1ddb3ef658505351c615253587f09b93503bb73a6225f3d3898be894e9d05dba8d464f4dd9c54514bc3</sha512>
+                        </configuration>
+                    </execution>
                 </executions>
             </plugin>
         </plugins>

Modified: 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java 
Mon Mar 23 04:20:05 2026        (r1932472)
+++ pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java 
Mon Mar 23 05:52:27 2026        (r1932473)
@@ -18,6 +18,8 @@ package org.apache.pdfbox.cos;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -33,12 +35,12 @@ import org.apache.pdfbox.util.Hex;
 public final class COSName extends COSBase implements Comparable<COSName>
 {
     // using ConcurrentHashMap because this can be accessed by multiple threads
-    private static final Map<String, COSName> nameMap = new 
ConcurrentHashMap<String, COSName>(
+    private static final Map<ByteBuffer, COSName> nameMap = new 
ConcurrentHashMap<ByteBuffer, COSName>(
             8192);
 
     // all common COSName values are stored in this HashMap
     // they are already defined as static constants and don't need to be 
synchronized
-    private static final Map<String, COSName> commonNameMap = new 
HashMap<String, COSName>(768);
+    private static final Map<ByteBuffer, COSName> commonNameMap = new 
HashMap<ByteBuffer, COSName>(768);
 
     //
     // IMPORTANT: this list is *alphabetized* and does not need any JavaDoc
@@ -642,98 +644,171 @@ public final class COSName extends COSBa
     public static final COSName ZA_DB = new COSName("ZaDb");
 
     // fields
-    private final String name;
 
     /**
-     * This will get a COSName object with that name.
-     * 
-     * @param aName The name of the object.
-     * 
-     * @return A COSName with the specified name.
+     * <p>Per PDF 32000-1:2008 §7.3.5: Beginning with PDF 1.2 a name object is 
an atomic symbol 
+     * uniquely defined by a sequence of any characters (8-bit values) except 
null 
+     * (character code 0).</p>
+     */
+    private final byte[] nameBytes;
+
+    /**
+     * Returns a {@code COSName} whose byte sequence is the UTF-8 encoding of 
{@code aName}.
+     *
+     * <p>This is the standard factory for names defined in Java source code 
(e.g. the static
+     * constants above). All well-formed PDF names defined by the spec are 
ASCII, so the UTF-8
+     * encoding is a transparent identity transform for those cases.</p>
+     *
+     * @param aName the name string; must not be {@code null}
+     * @return a canonicalised {@code COSName} instance
      */
     public static COSName getPDFName(String aName)
     {
-        COSName name = null;
-        if (aName != null)
+        return getPDFName(aName.getBytes(Charsets.UTF_8));
+    }
+
+    /**
+     * Returns a {@code COSName} whose byte sequence is exactly {@code bytes}.
+     *
+     * <p>This is the preferred factory when constructing a name directly from 
a PDF byte stream
+     * (i.e. after the parser has stripped the leading {@code /} and expanded 
all {@code #XX}
+     * escape sequences). Using this method preserves the spec-correct, 
byte-level identity of the
+     * name even when the bytes are not valid UTF-8.</p>
+     *
+     * <p>Null bytes (0x00) are rejected; the spec explicitly excludes 
them.</p>
+     *
+     * @param bytes the raw decoded byte sequence; must not be {@code null} 
and must not contain 0x00
+     * @return a canonicalised {@code COSName} instance
+     * @throws IllegalArgumentException if {@code bytes} contains a null byte
+     */
+    public static COSName getPDFName(byte[] bytes)
+    {
+        if (bytes == null)
+        {
+            return null;
+        }
+
+        // Wrap for lookup only to avoid unnecessary copying of the byte array 
for the key.
+        ByteBuffer lookupKey = ByteBuffer.wrap(bytes);
+
+        // Is it a common COSName ??
+        COSName name = commonNameMap.get(lookupKey);
+        if (name == null)
         {
-            // Is it a common COSName ??
-            name = commonNameMap.get(aName);
+            // It seems to be a document specific COSName
+            name = nameMap.get(lookupKey);
             if (name == null)
             {
-                // It seems to be a document specific COSName
-                name = nameMap.get(aName);
-                if (name == null)
-                {
-                    // name is added to the synchronized map in the constructor
-                    name = new COSName(aName, false);
-                }
+                // name is added to the synchronized map in the constructor
+                name = new COSName(bytes, false);
             }
         }
+
         return name;
     }
 
     /**
-     * Private constructor. This will limit the number of COSName objects. 
that are created.
-     * 
-     * @param aName The name of the COSName object.
+     * Private constructor. This will limit the number of COSName objects that 
are created.
+     *
+     * @param storedBytes The name of the COSName object.
      * @param staticValue Indicates if the COSName object is static so that it 
can be stored in the HashMap without
      * synchronizing.
      */
-    private COSName(String aName, boolean staticValue)
+    private COSName(byte[] storedBytes, boolean staticValue)
     {
-        name = aName;
+        this.nameBytes = storedBytes;
+        ByteBuffer storedKey = ByteBuffer.wrap(storedBytes);
         if (staticValue)
         {
-            commonNameMap.put(aName, this);
+            commonNameMap.put(storedKey, this);
         }
         else
         {
-            nameMap.put(aName, this);
+            nameMap.put(storedKey, this);
         }
     }
 
     /**
-     * Private constructor. This will limit the number of COSName objects. 
that are created.
+     * Private constructor. This will limit the number of COSName objects that 
are created.
      * 
      * @param aName The name of the COSName object.
      */
     private COSName(String aName)
     {
-        this(aName, true);
+        this(aName.getBytes(Charsets.UTF_8), true);
+    }
+
+    /**
+     * Returns the raw byte sequence that defines this name.
+     *
+     * <p>This is the atomic content/identity of the name. Prefer this over
+     * {@link #getName()} whenever you need to write name bytes to an output 
stream, compare names
+     * parsed from a PDF, or otherwise operate at the byte level.</p>
+     *
+     * @return a defensive copy of the internal byte array; never {@code null}
+     */
+    public byte[] getBytes()
+    {
+        return Arrays.copyOf(nameBytes, nameBytes.length);
     }
 
     /**
-     * This will get the name of this COSName object.
+     * Returns the name decoded as a UTF-8 {@code String}.
+     * 
+     * <p>This method exists primarily for backward compatibility and for 
cases where the
+     * readable value needs to be stored.</p>
+     * 
+     * <p>Per PDF 32000-1:2008 §7.3.5, ... However, occasionally the need 
arises to treat a name object
+     * as text, such as one that represents a font ... </p>
+     * 
+     * <p>... In such situations, the sequence of bytes (after expansion of 
NUMBER SIGN sequences, if any) 
+     * should be interpreted according to UTF-8... </p>
+     * 
+     * <p>Use {@link #getBytes()} when byte-level fidelity is required.</p>
      * 
      * @return The name of the object.
      */
     public String getName()
     {
-        return name;
+        String utf8String = new String(nameBytes, Charsets.UTF_8);
+
+        //check for lossy decoding, which can happen if the name contains
+        // bytes that are not valid UTF-8
+        if (utf8String.indexOf('\uFFFD') >= 0)
+        {
+            // fall back to ISO-8859-1, which is a single-byte encoding that 
can decode any
+            // byte sequence without loss
+            return new String(nameBytes, Charsets.ISO_8859_1);
+        }
+        return utf8String;
     }
 
     @Override
     public String toString()
     {
-        return "COSName{" + name + "}";
+        return "COSName{" + getName() + "}";
     }
 
     @Override
     public boolean equals(Object object)
     {
-        return object instanceof COSName && name.equals(((COSName) 
object).name);
+        return object instanceof COSName && Arrays.equals(nameBytes, 
((COSName) object).nameBytes);
     }
 
     @Override
     public int hashCode()
     {
-        return name.hashCode();
+        return Arrays.hashCode(nameBytes);
     }
 
     @Override
     public int compareTo(COSName other)
     {
-        return name.compareTo(other.name);
+        if (this == other)
+        {
+            return 0;
+        }
+        return getName().compareTo(other.getName()); // maybe poor 
implementation compared to 3.0?
     }
 
     /**
@@ -742,7 +817,7 @@ public final class COSName extends COSBa
      */
     public boolean isEmpty()
     {
-        return name.isEmpty();
+        return nameBytes.length == 0;
     }
 
     @Override
@@ -760,7 +835,7 @@ public final class COSName extends COSBa
     public void writePDF(OutputStream output) throws IOException
     {
         output.write('/');
-        byte[] bytes = getName().getBytes(Charsets.UTF_8);
+        byte[] bytes = getBytes();
         for (byte b : bytes)
         {
             int current = b & 0xFF;

Modified: 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/BaseParser.java
==============================================================================
--- 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/BaseParser.java
        Mon Mar 23 04:20:05 2026        (r1932472)
+++ 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/BaseParser.java
        Mon Mar 23 05:52:27 2026        (r1932473)
@@ -887,7 +887,7 @@ public abstract class BaseParser
             seqSource.unread(c);
         }
 
-        return COSName.getPDFName(decodeBuffer(buffer));
+        return COSName.getPDFName(buffer.toByteArray());
     }
 
     /**

Modified: 
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/cos/TestCOSName.java
==============================================================================
--- 
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/cos/TestCOSName.java 
    Mon Mar 23 04:20:05 2026        (r1932472)
+++ 
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/cos/TestCOSName.java 
    Mon Mar 23 05:52:27 2026        (r1932473)
@@ -17,14 +17,18 @@
 package org.apache.pdfbox.cos;
 
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.IOException;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.interactive.form.PDField;
 import org.junit.Assert;
 import org.junit.Test;
 
 public class TestCOSName
 {
+    private static final File TARGETPDFDIR = new File("target/pdfs");
+
     /**
      * PDFBOX-4076: Check that characters outside of US_ASCII are not replaced 
with "?".
      * 
@@ -48,4 +52,71 @@ public class TestCOSName
         document.close();
     }
 
+    /**
+     * PDFBOX-6178: Ensure that names with escape sequences #xx are written as 
is.
+     * 
+     * @throws IOException 
+     */
+    @Test
+    public void PDFBox6178() throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+        PDDocument document = PDDocument.load(new 
File(TARGETPDFDIR,"PDFBOX-6178.pdf"));
+        PDField field = document.getDocumentCatalog()
+            .getAcroForm(null)
+            .getField("Geschlecht");
+
+        field.setValue("männlich");
+
+        COSDictionary dict = (COSDictionary) field.getWidgets()
+                .get(0).getAppearance().getNormalAppearance().getCOSObject();
+        for (COSName k : dict.keySet())
+        {
+            try
+            {
+                k.writePDF(baos);
+            }
+            catch (IOException e)
+            {
+                // ignored
+            }
+        }
+        String writtenKeys = new String(baos.toByteArray(), "UTF-8");
+        Assert.assertTrue("Output should be /m#e4nnlich (with 0xE4 as hex 
escape)", writtenKeys.contains("/m#E4nnlich"));
+        document.close();
+    }
+
+    /**
+     * PDFBOX-6178: Ensure that names with escape sequences #xx are written as 
is.
+     * 
+     * @throws IOException 
+     */
+    @Test
+    public void NameWithASCII_NUL() throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+        PDDocument document = PDDocument.load(new 
File(TARGETPDFDIR,"PDFBOX-6178-1.pdf"));
+        PDField field = document.getDocumentCatalog()
+            .getAcroForm(null)
+            .getField("Geschlecht");
+
+        COSDictionary dict = (COSDictionary) field.getWidgets()
+            .get(0).getAppearance().getNormalAppearance().getCOSObject();
+        for (COSName k : dict.keySet())
+        {
+            try
+            {
+                k.writePDF(baos);
+            }
+            catch (IOException e)
+            {
+                // ignored
+            }
+        }
+        String writtenKeys = new String(baos.toByteArray(), "UTF-8");
+        Assert.assertTrue("Output should be /m#00nnlich (with 0xE4 as hex 
escape)", writtenKeys.contains("/m#00nnlich"));
+        document.close();
+    }
 }

Copied and modified: 
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdfparser/TestCOSParser.java
 (from r1932462, 
pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdfparser/TestCOSParser.java)
==============================================================================
--- 
pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdfparser/TestCOSParser.java
    Sun Mar 22 12:45:03 2026        (r1932462, copy source)
+++ 
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdfparser/TestCOSParser.java
     Mon Mar 23 05:52:27 2026        (r1932473)
@@ -17,232 +17,170 @@
 
 package org.apache.pdfbox.pdfparser;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
 
 import java.io.IOException;
-import java.nio.charset.StandardCharsets;
 
 import org.apache.pdfbox.cos.COSName;
-import org.apache.pdfbox.cos.COSString;
-import org.apache.pdfbox.io.RandomAccessReadBuffer;
-import org.junit.jupiter.api.Test;
+import org.apache.pdfbox.io.RandomAccessBuffer;
+import org.apache.pdfbox.util.Charsets;
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
 
-class TestCOSParser
+public class TestCOSParser
 {
-    @Test
-    void testCheckForEndOfString() throws IOException
-    {
-        // (Test)
-        byte[] inputBytes = { 40, 84, 101, 115, 116, 41 };
-
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
-        COSParser cosParser = new COSParser(buffer);
-        COSString cosString = cosParser.parseCOSLiteralString();
-        assertEquals("Test", cosString.getString());
-
-        String output = "(Test";
-        // ((Test) + LF + "/ "
-        inputBytes = new byte[] { '(', '(', 'T', 'e', 's', 't', ')', 10, '/', 
' ' };
-
-        buffer = new RandomAccessReadBuffer(inputBytes);
-        cosParser = new COSParser(buffer);
-        cosString = cosParser.parseCOSLiteralString();
-        assertEquals(output, cosString.getString());
-
-        // ((Test) + CR + "/ "
-        inputBytes = new byte[] { '(', '(', 'T', 'e', 's', 't', ')', 13, '/', 
' ' };
-
-        buffer = new RandomAccessReadBuffer(inputBytes);
-        cosParser = new COSParser(buffer);
-        cosString = cosParser.parseCOSLiteralString();
-        assertEquals(output, cosString.getString());
-
-        // ((Test) + CR + LF + "/ "
-        inputBytes = new byte[] { '(', '(', 'T', 'e', 's', 't', ')', 13, 10, 
'/' };
-
-        buffer = new RandomAccessReadBuffer(inputBytes);
-        cosParser = new COSParser(buffer);
-        cosString = cosParser.parseCOSLiteralString();
-        assertEquals(output, cosString.getString());
-
-        // ((Test) + LF + "> "
-        inputBytes = new byte[] { '(', '(', 'T', 'e', 's', 't', ')', 10, '>', 
' ' };
-
-        buffer = new RandomAccessReadBuffer(inputBytes);
-        cosParser = new COSParser(buffer);
-        cosString = cosParser.parseCOSLiteralString();
-        assertEquals(output, cosString.getString());
-
-        // ((Test) + CR + "> "
-        inputBytes = new byte[] { '(', '(', 'T', 'e', 's', 't', ')', 13, '>', 
' ' };
-
-        buffer = new RandomAccessReadBuffer(inputBytes);
-        cosParser = new COSParser(buffer);
-        cosString = cosParser.parseCOSLiteralString();
-        assertEquals(output, cosString.getString());
-
-        // ((Test) + CR + LF + "> "
-        inputBytes = new byte[] { '(', '(', 'T', 'e', 's', 't', ')', 13, 10, 
'>' };
-
-        buffer = new RandomAccessReadBuffer(inputBytes);
-        cosParser = new COSParser(buffer);
-        cosString = cosParser.parseCOSLiteralString();
-        assertEquals(output, cosString.getString());
-    }
-
     // COSName parsing tests based on examples from PDF 32000-1:2008, Table 4, 
Section 7.3.5
 
     @Test
-    void testTable4Example_Name1() throws IOException
+    public void testTable4Example_Name1() throws IOException
     {
         // /Name1 → "Name1"
-        byte[] inputBytes = "/Name1 ".getBytes(StandardCharsets.US_ASCII);
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        byte[] inputBytes = "/Name1 ".getBytes(Charsets.US_ASCII);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         assertEquals("Name1", name.getName());
     }
 
     @Test
-    void testTable4Example_ASomewhatLongerName() throws IOException
+    public void testTable4Example_ASomewhatLongerName() throws IOException
     {
         // /ASomewhatLongerName → "ASomewhatLongerName"
-        byte[] inputBytes = "/ASomewhatLongerName 
".getBytes(StandardCharsets.US_ASCII);
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        byte[] inputBytes = "/ASomewhatLongerName 
".getBytes(Charsets.US_ASCII);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         assertEquals("ASomewhatLongerName", name.getName());
     }
 
     @Test
-    void testTable4Example_WithSpecialCharacters() throws IOException
+    public void testTable4Example_WithSpecialCharacters() throws IOException
     {
         // /A;Name_With-Various***Characters? → 
"A;Name_With-Various***Characters?"
-        byte[] inputBytes = "/A;Name_With-Various***Characters? 
".getBytes(StandardCharsets.US_ASCII);
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        byte[] inputBytes = "/A;Name_With-Various***Characters? 
".getBytes(Charsets.US_ASCII);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         assertEquals("A;Name_With-Various***Characters?", name.getName());
     }
 
     @Test
-    void testTable4Example_Numeric() throws IOException
+    public void testTable4Example_Numeric() throws IOException
     {
         // /1.2 → "1.2"
-        byte[] inputBytes = "/1.2 ".getBytes(StandardCharsets.US_ASCII);
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        byte[] inputBytes = "/1.2 ".getBytes(Charsets.US_ASCII);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         assertEquals("1.2", name.getName());
     }
 
     @Test
-    void testTable4Example_DollarSigns() throws IOException
+    public void testTable4Example_DollarSigns() throws IOException
     {
         // /$$ → "$$"
-        byte[] inputBytes = "/$$ ".getBytes(StandardCharsets.US_ASCII);
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        byte[] inputBytes = "/$$ ".getBytes(Charsets.US_ASCII);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         assertEquals("$$", name.getName());
     }
 
     @Test
-    void testTable4Example_AtPattern() throws IOException
+    public void testTable4Example_AtPattern() throws IOException
     {
         // /@pattern → "@pattern"
-        byte[] inputBytes = "/@pattern ".getBytes(StandardCharsets.US_ASCII);
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        byte[] inputBytes = "/@pattern ".getBytes(Charsets.US_ASCII);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         assertEquals("@pattern", name.getName());
     }
 
     @Test
-    void testTable4Example_DotNotdef() throws IOException
+    public void testTable4Example_DotNotdef() throws IOException
     {
         // /.notdef → ".notdef" (space is 0x20, hex-encoded as #20)
-        byte[] inputBytes = "/#2Enotdef ".getBytes(StandardCharsets.US_ASCII);
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        byte[] inputBytes = "/#2Enotdef ".getBytes(Charsets.US_ASCII);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         assertEquals(".notdef", name.getName());
     }
 
     @Test
-    void testTable4Example_HexEncodedSpace() throws IOException
+    public void testTable4Example_HexEncodedSpace() throws IOException
     {
         // /lime#20Green → "lime Green"
-        byte[] inputBytes = "/lime#20Green 
".getBytes(StandardCharsets.US_ASCII);
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        byte[] inputBytes = "/lime#20Green ".getBytes(Charsets.US_ASCII);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         assertEquals("lime Green", name.getName());
     }
 
     @Test
-    void testTable4Example_HexEncodedParentheses() throws IOException
+    public void testTable4Example_HexEncodedParentheses() throws IOException
     {
         // /paired#28#29parentheses → "paired()parentheses"
         // (#28 = '(', #29 = ')')
-        byte[] inputBytes = "/paired#28#29parentheses 
".getBytes(StandardCharsets.US_ASCII);
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        byte[] inputBytes = "/paired#28#29parentheses 
".getBytes(Charsets.US_ASCII);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         assertEquals("paired()parentheses", name.getName());
     }
 
     @Test
-    void testTable4Example_HexEncodedNumberSign() throws IOException
+    public void testTable4Example_HexEncodedNumberSign() throws IOException
     {
         // /The_Key_of_F#23_Minor → "The_Key_of_F#_Minor"
         // (#23 = '#')
-        byte[] inputBytes = "/The_Key_of_F#23_Minor 
".getBytes(StandardCharsets.US_ASCII);
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        byte[] inputBytes = "/The_Key_of_F#23_Minor 
".getBytes(Charsets.US_ASCII);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         assertEquals("The_Key_of_F#_Minor", name.getName());
     }
 
     @Test
-    void testTable4Example_HexEncodedLetter() throws IOException
+    public void testTable4Example_HexEncodedLetter() throws IOException
     {
         // /A#42 → "AB" (note #42 = 'B')
-        byte[] inputBytes = "/A#42 ".getBytes(StandardCharsets.US_ASCII);
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        byte[] inputBytes = "/A#42 ".getBytes(Charsets.US_ASCII);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         assertEquals("AB", name.getName());
     }
 
     @Test
-    void testTable4Example_EmptyName() throws IOException
+    public void testTable4Example_EmptyName() throws IOException
     {
         // / → "" (empty name is valid per spec)
-        byte[] inputBytes = "/ ".getBytes(StandardCharsets.US_ASCII);
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        byte[] inputBytes = "/ ".getBytes(Charsets.US_ASCII);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         assertEquals("", name.getName());
     }
 
     @Test
-    void testNullCharacterTermination() throws IOException
+    public void testNullCharacterTermination() throws IOException
     {
         // /Name\0Extra should parse as "Name" and stop at null
         byte[] inputBytes = new byte[] { '/', 'N', 'a', 'm', 'e', 0, 'E', 'x', 
't', 'r', 'a', ' ' };
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         assertEquals("Name", name.getName());
     }
 
     @Test
-    void testInvalidHexSequence() throws IOException
+    public void testInvalidHexSequence() throws IOException
     {
         // /Name#GG should keep #G literally since G is not a valid hex digit
-        byte[] inputBytes = "/Name#GG ".getBytes(StandardCharsets.US_ASCII);
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        byte[] inputBytes = "/Name#GG ".getBytes(Charsets.US_ASCII);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         // When # is not followed by two hex digits, both chars are kept 
literally
@@ -250,94 +188,94 @@ class TestCOSParser
     }
 
     @Test
-    void testHexEscapeLowercase() throws IOException
+    public void testHexEscapeLowercase() throws IOException
     {
         // /Name#2fTest (lowercase hex #2f = '/')
-        byte[] inputBytes = "/Name#2fTest 
".getBytes(StandardCharsets.US_ASCII);
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        byte[] inputBytes = "/Name#2fTest ".getBytes(Charsets.US_ASCII);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         assertEquals("Name/Test", name.getName());
     }
 
     @Test
-    void testHexEscapeUppercase() throws IOException
+    public void testHexEscapeUppercase() throws IOException
     {
         // /Name#2FTest (uppercase hex #2F = '/')
-        byte[] inputBytes = "/Name#2FTest 
".getBytes(StandardCharsets.US_ASCII);
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        byte[] inputBytes = "/Name#2FTest ".getBytes(Charsets.US_ASCII);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         assertEquals("Name/Test", name.getName());
     }
 
     @Test
-    void testNameTerminationByDelimiters() throws IOException
+    public void testNameTerminationByDelimiters() throws IOException
     {
         // Test termination by '>'
-        byte[] inputBytes = "/Name1>".getBytes(StandardCharsets.US_ASCII);
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        byte[] inputBytes = "/Name1>".getBytes(Charsets.US_ASCII);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         assertEquals("Name1", name.getName());
 
         // Test termination by '<'
-        inputBytes = "/Name2<".getBytes(StandardCharsets.US_ASCII);
-        buffer = new RandomAccessReadBuffer(inputBytes);
+        inputBytes = "/Name2<".getBytes(Charsets.US_ASCII);
+        buffer = new RandomAccessBuffer(inputBytes);
         cosParser = new COSParser(buffer);
         name = cosParser.parseCOSName();
         assertEquals("Name2", name.getName());
 
         // Test termination by '['
-        inputBytes = "/Name3[".getBytes(StandardCharsets.US_ASCII);
-        buffer = new RandomAccessReadBuffer(inputBytes);
+        inputBytes = "/Name3[".getBytes(Charsets.US_ASCII);
+        buffer = new RandomAccessBuffer(inputBytes);
         cosParser = new COSParser(buffer);
         name = cosParser.parseCOSName();
         assertEquals("Name3", name.getName());
 
         // Test termination by ']'
-        inputBytes = "/Name4]".getBytes(StandardCharsets.US_ASCII);
-        buffer = new RandomAccessReadBuffer(inputBytes);
+        inputBytes = "/Name4]".getBytes(Charsets.US_ASCII);
+        buffer = new RandomAccessBuffer(inputBytes);
         cosParser = new COSParser(buffer);
         name = cosParser.parseCOSName();
         assertEquals("Name4", name.getName());
 
         // Test termination by '('
-        inputBytes = "/Name5(".getBytes(StandardCharsets.US_ASCII);
-        buffer = new RandomAccessReadBuffer(inputBytes);
+        inputBytes = "/Name5(".getBytes(Charsets.US_ASCII);
+        buffer = new RandomAccessBuffer(inputBytes);
         cosParser = new COSParser(buffer);
         name = cosParser.parseCOSName();
         assertEquals("Name5", name.getName());
 
         // Test termination by ')'
-        inputBytes = "/Name6)".getBytes(StandardCharsets.US_ASCII);
-        buffer = new RandomAccessReadBuffer(inputBytes);
+        inputBytes = "/Name6)".getBytes(Charsets.US_ASCII);
+        buffer = new RandomAccessBuffer(inputBytes);
         cosParser = new COSParser(buffer);
         name = cosParser.parseCOSName();
         assertEquals("Name6", name.getName());
 
         // Test termination by '/'
-        inputBytes = "/Name7/".getBytes(StandardCharsets.US_ASCII);
-        buffer = new RandomAccessReadBuffer(inputBytes);
+        inputBytes = "/Name7/".getBytes(Charsets.US_ASCII);
+        buffer = new RandomAccessBuffer(inputBytes);
         cosParser = new COSParser(buffer);
         name = cosParser.parseCOSName();
         assertEquals("Name7", name.getName());
 
         // Test termination by '%'
-        inputBytes = "/Name8%".getBytes(StandardCharsets.US_ASCII);
-        buffer = new RandomAccessReadBuffer(inputBytes);
+        inputBytes = "/Name8%".getBytes(Charsets.US_ASCII);
+        buffer = new RandomAccessBuffer(inputBytes);
         cosParser = new COSParser(buffer);
         name = cosParser.parseCOSName();
         assertEquals("Name8", name.getName());
     }
 
     @Test
-    void testASCIIRegularCharacters() throws IOException
+    public void testASCIIRegularCharacters() throws IOException
     {
         // Test a range of ASCII characters that are not delimiters
         // PDF delimiters that terminate name parsing: whitespace, <, >, [, ], 
{, }, /, %, (, )
-        byte[] inputBytes = 
"/!\"$'*+-._:;=@~^`|\\".getBytes(StandardCharsets.US_ASCII);
-        RandomAccessReadBuffer buffer = new RandomAccessReadBuffer(inputBytes);
+        byte[] inputBytes = 
"/!\"$'*+-._:;=@~^`|\\".getBytes(Charsets.US_ASCII);
+        RandomAccessBuffer buffer = new RandomAccessBuffer(inputBytes);
         COSParser cosParser = new COSParser(buffer);
         COSName name = cosParser.parseCOSName();
         // All these non-delimiter characters should be preserved
@@ -345,25 +283,25 @@ class TestCOSParser
     }
 
     @Test
-    void testUTF8InNames()
+    public void testUTF8InNames()
     {
         // Create a name with UTF-8 encoded characters
         String nameStr = "Test中国";
-        byte[] nameBytes = nameStr.getBytes(StandardCharsets.UTF_8);
+        byte[] nameBytes = nameStr.getBytes(Charsets.UTF_8);
         COSName name = COSName.getPDFName(nameBytes);
         
         // The name should preserve the UTF-8 bytes
         byte[] retrievedBytes = name.getBytes();
         // Verify by recreating the string
-        String retrievedStr = new String(retrievedBytes, 
StandardCharsets.UTF_8);
+        String retrievedStr = new String(retrievedBytes, Charsets.UTF_8);
         assertEquals(nameStr, retrievedStr);
     }
 
     @Test
-    void testNameCanonicaliation()
+    public void testNameCanonicaliation()
     {
-        byte[] bytes1 = "TestName".getBytes(StandardCharsets.US_ASCII);
-        byte[] bytes2 = "TestName".getBytes(StandardCharsets.US_ASCII);
+        byte[] bytes1 = "TestName".getBytes(Charsets.US_ASCII);
+        byte[] bytes2 = "TestName".getBytes(Charsets.US_ASCII);
         
         COSName name1 = COSName.getPDFName(bytes1);
         COSName name2 = COSName.getPDFName(bytes2);


Reply via email to