PdfDictionaryTest.java

/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2025 Apryse Group NV
    Authors: Apryse Software.

    This program is offered under a commercial and under the AGPL license.
    For commercial licensing, contact us at https://itextpdf.com/sales.  For AGPL licensing, see below.

    AGPL licensing:
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package com.itextpdf.kernel.pdf;

import com.itextpdf.io.source.ByteArrayOutputStream;
import com.itextpdf.test.ExtendedITextTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Tag;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

@Tag("UnitTest")
public class PdfDictionaryTest extends ExtendedITextTest {

    @Test
    public void testEntrySet() {
        PdfDocument doc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));

        PdfDictionary dict = new PdfDictionary();
        dict.put(new PdfName("1"), new PdfNumber(1).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("2"), new PdfNumber(2).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("3"), new PdfNumber(3).makeIndirect(doc));
        dict.put(new PdfName("4"), new PdfNumber(4).makeIndirect(doc));
        dict.put(new PdfName("5"), new PdfNumber(5));
        dict.put(new PdfName("6"), new PdfNumber(6));

        List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6));
        for (Map.Entry<PdfName, PdfObject> e: dict.entrySet()) {
            Assertions.assertEquals(e.getKey().toString(), "/"+e.getValue());
            if (!nums.remove(Integer.valueOf(((PdfNumber)e.getValue()).intValue()))) {
                Assertions.fail("Element not found");
            }
        }
    }

    @Test
    public void testEntrySetContains() {
        PdfDocument doc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));

        PdfDictionary dict = new PdfDictionary();
        dict.put(new PdfName("1"), new PdfNumber(1).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("2"), new PdfNumber(2).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("3"), new PdfNumber(3).makeIndirect(doc));
        dict.put(new PdfName("4"), new PdfNumber(4).makeIndirect(doc));
        dict.put(new PdfName("5"), new PdfNumber(5));
        dict.put(new PdfName("6"), new PdfNumber(6));

        for (Map.Entry<PdfName, PdfObject> e: dict.entrySet()) {
            Assertions.assertTrue(dict.entrySet().contains(e));
        }
    }

    @Test
    public void testEntrySetRemove() {
        PdfDocument doc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));

        PdfDictionary dict = new PdfDictionary();
        dict.put(new PdfName("1"), new PdfNumber(1).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("2"), new PdfNumber(2).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("3"), new PdfNumber(3).makeIndirect(doc));
        dict.put(new PdfName("4"), new PdfNumber(4).makeIndirect(doc));
        dict.put(new PdfName("5"), new PdfNumber(5));
        dict.put(new PdfName("6"), new PdfNumber(6));

        List<Map.Entry<PdfName, PdfObject>> toRemove = new ArrayList<>();
        for (Map.Entry<PdfName, PdfObject> e: dict.entrySet()) {
            toRemove.add(e);
        }
        for (Map.Entry<PdfName, PdfObject> e: toRemove) {
            Assertions.assertTrue(dict.entrySet().remove(e));
        }
        Assertions.assertEquals(0, dict.entrySet().size());
        Assertions.assertEquals(0, dict.values().size());
        Assertions.assertEquals(0, dict.size());
    }

    @Test
    public void testEntrySetRemove2() {
        PdfDocument doc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));

        PdfDictionary dict = new PdfDictionary();
        dict.put(new PdfName("1"), new PdfNumber(1).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("2"), new PdfNumber(2).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("3"), new PdfNumber(3).makeIndirect(doc));
        dict.put(new PdfName("4"), new PdfNumber(4).makeIndirect(doc));
        dict.put(new PdfName("5"), new PdfNumber(5));
        dict.put(new PdfName("6"), new PdfNumber(6));

        PdfDictionary dict2 = new PdfDictionary();
        dict2.put(new PdfName("1"), new PdfNumber(1).makeIndirect(doc).getIndirectReference());
        dict2.put(new PdfName("2"), new PdfNumber(2).makeIndirect(doc).getIndirectReference());
        dict2.put(new PdfName("3"), new PdfNumber(3).makeIndirect(doc));
        dict2.put(new PdfName("4"), new PdfNumber(4).makeIndirect(doc));
        dict2.put(new PdfName("5"), new PdfNumber(5));
        dict2.put(new PdfName("6"), new PdfNumber(6));

        for (Map.Entry<PdfName, PdfObject> e: dict2.entrySet()) {
            Assertions.assertTrue(dict.entrySet().remove(e));
        }

        Assertions.assertEquals(0, dict.entrySet().size());
        Assertions.assertEquals(0, dict.values().size());
        Assertions.assertEquals(0, dict.size());
    }

    @Test
    public void testEntrySetRemoveAll() {
        PdfDocument doc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));

        PdfDictionary dict = new PdfDictionary();
        dict.put(new PdfName("1"), new PdfNumber(1).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("2"), new PdfNumber(2).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("3"), new PdfNumber(3).makeIndirect(doc));
        dict.put(new PdfName("4"), new PdfNumber(4).makeIndirect(doc));
        dict.put(new PdfName("5"), new PdfNumber(5));
        dict.put(new PdfName("6"), new PdfNumber(6));

        List<Map.Entry<PdfName, PdfObject>> toRemove = new ArrayList<>();
        for (Map.Entry<PdfName, PdfObject> e: dict.entrySet()) {
            toRemove.add(e);
        }

        dict.entrySet().removeAll(toRemove);
        Assertions.assertEquals(0, dict.entrySet().size());
        Assertions.assertEquals(0, dict.values().size());
        Assertions.assertEquals(0, dict.size());
    }

    @Test
    public void testEntrySetRemoveAll2() {
        PdfDocument doc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));

        PdfDictionary dict = new PdfDictionary();
        dict.put(new PdfName("1"), new PdfNumber(1).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("2"), new PdfNumber(2).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("3"), new PdfNumber(3).makeIndirect(doc));
        dict.put(new PdfName("4"), new PdfNumber(4).makeIndirect(doc));
        dict.put(new PdfName("5"), new PdfNumber(5));
        dict.put(new PdfName("6"), new PdfNumber(6));

        PdfDictionary dict2 = new PdfDictionary();
        dict2.put(new PdfName("1"), new PdfNumber(1).makeIndirect(doc).getIndirectReference());
        dict2.put(new PdfName("2"), new PdfNumber(2).makeIndirect(doc).getIndirectReference());
        dict2.put(new PdfName("3"), new PdfNumber(3).makeIndirect(doc));
        dict2.put(new PdfName("4"), new PdfNumber(4).makeIndirect(doc));
        dict2.put(new PdfName("5"), new PdfNumber(5));
        dict2.put(new PdfName("6"), new PdfNumber(6));

        dict.entrySet().removeAll(dict2.entrySet());
        Assertions.assertEquals(0, dict.entrySet().size());
        Assertions.assertEquals(0, dict.values().size());
        Assertions.assertEquals(0, dict.size());
    }

    @Test
    public void testEntrySetRetainAll() {
        PdfDocument doc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));

        PdfDictionary dict = new PdfDictionary();
        dict.put(new PdfName("1"), new PdfNumber(1).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("2"), new PdfNumber(2).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("3"), new PdfNumber(3).makeIndirect(doc));
        dict.put(new PdfName("4"), new PdfNumber(4).makeIndirect(doc));
        dict.put(new PdfName("5"), new PdfNumber(5));
        dict.put(new PdfName("6"), new PdfNumber(6));

        List<Map.Entry<PdfName, PdfObject>> toRemove = new ArrayList<>();
        int i = 0;
        for (Map.Entry<PdfName, PdfObject> e: dict.entrySet()) {
            toRemove.add(e);
            if (i++ > 2) break;
        }

        dict.entrySet().retainAll(toRemove);
        Assertions.assertEquals(4, dict.entrySet().size());
        Assertions.assertEquals(4, dict.values().size());
        Assertions.assertEquals(4, dict.size());
    }

    @Test
    public void testEntrySetClear() {
        PdfDocument doc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));

        PdfDictionary dict = new PdfDictionary();
        dict.put(new PdfName("1"), new PdfNumber(1).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("2"), new PdfNumber(2).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("3"), new PdfNumber(3).makeIndirect(doc));
        dict.put(new PdfName("4"), new PdfNumber(4).makeIndirect(doc));
        dict.put(new PdfName("5"), new PdfNumber(5));
        dict.put(new PdfName("6"), new PdfNumber(6));

        dict.entrySet().clear();
        Assertions.assertEquals(0, dict.entrySet().size());
        Assertions.assertEquals(0, dict.values().size());
        Assertions.assertEquals(0, dict.size());
    }

    @Test
    public void testValues() {
        PdfDocument doc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));

        PdfDictionary dict = new PdfDictionary();
        dict.put(new PdfName("1"), new PdfNumber(1).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("2"), new PdfNumber(2).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("3"), new PdfNumber(3).makeIndirect(doc));
        dict.put(new PdfName("4"), new PdfNumber(4).makeIndirect(doc));
        dict.put(new PdfName("5"), new PdfNumber(5));
        dict.put(new PdfName("6"), new PdfNumber(6));

        List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6));
        for (Map.Entry<PdfName, PdfObject> e: dict.entrySet()) {
            Assertions.assertEquals(e.getKey().toString(), "/"+e.getValue());
            if (!nums.remove(Integer.valueOf(((PdfNumber)e.getValue()).intValue()))) {
                Assertions.fail("Element not found");
            }
        }
    }

    @Test
    public void testValuesContains() {
        PdfDocument doc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));

        PdfDictionary dict = new PdfDictionary();
        dict.put(new PdfName("1"), new PdfNumber(1).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("2"), new PdfNumber(2).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("3"), new PdfNumber(3).makeIndirect(doc));
        dict.put(new PdfName("4"), new PdfNumber(4).makeIndirect(doc));
        dict.put(new PdfName("5"), new PdfNumber(5));
        dict.put(new PdfName("6"), new PdfNumber(6));

        for (PdfObject v: dict.values()) {
            Assertions.assertTrue(dict.values().contains(v));
        }
    }

    @Test
    public void testValuesIndirectContains() {
        PdfDocument doc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));

        PdfDictionary dict = new PdfDictionary();
        dict.put(new PdfName("1"), new PdfNumber(1).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("2"), new PdfNumber(2).makeIndirect(doc).getIndirectReference());
        dict.put(new PdfName("3"), new PdfNumber(3).makeIndirect(doc));
        dict.put(new PdfName("4"), new PdfNumber(4).makeIndirect(doc));
        dict.put(new PdfName("5"), new PdfNumber(5));
        dict.put(new PdfName("6"), new PdfNumber(6));

        Assertions.assertTrue(dict.values().contains(dict.get(new PdfName("1"), false)));
        Assertions.assertTrue(dict.values().contains(dict.get(new PdfName("2"), false)));
        Assertions.assertTrue(dict.values().contains(dict.get(new PdfName("3")).getIndirectReference()));
        Assertions.assertTrue(dict.values().contains(dict.get(new PdfName("4")).getIndirectReference()));
    }

    @Test
    public void testPdfNamesFetching() {
        byte[][] namesBytes = new byte[][] {
                // /#C3#9Cberschrift_1
                new byte[]{35, 67, 51, 35, 57, 67, 98, 101, 114, 115, 99, 104, 114, 105, 102, 116, 95, 49},
                // /#C3#9Cberschrift_2
                new byte[]{35, 67, 51, 35, 57, 67, 98, 101, 114, 115, 99, 104, 114, 105, 102, 116, 95, 50},
                // /Article
                new byte[]{65, 114, 116, 105, 99, 108, 101},
                // /Bildunterschrift
                new byte[]{66, 105, 108, 100, 117, 110, 116, 101, 114, 115, 99, 104, 114, 105, 102, 116},
                // /NormalParagraphStyle
                new byte[]{78, 111, 114, 109, 97, 108, 80, 97, 114, 97, 103, 114, 97, 112, 104, 83, 116, 121, 108, 101},
                // /Story
                new byte[]{83, 116, 111, 114, 121},
                // /TOC-1
                new byte[]{84, 79, 67, 45, 49,},
                // /TOC-2-2
                new byte[]{84, 79, 67, 45, 50, 45, 50,},
                // /TOC-Head
                new byte[]{84, 79, 67, 45, 72, 101, 97, 100,},
                // /Tabelle
                new byte[]{84, 97, 98, 101, 108, 108, 101,},
                // /Tabelle_Head
                new byte[]{84, 97, 98, 101, 108, 108, 101, 95, 72, 101, 97, 100,},
                // /Tabelle_fett
                new byte[]{84, 97, 98, 101, 108, 108, 101, 95, 102, 101, 116, 116,},
                // /Text_INFO
                new byte[]{84, 101, 120, 116, 95, 73, 78, 70, 79,},
                // /Text_Info_Head
                new byte[]{84, 101, 120, 116, 95, 73, 110, 102, 111, 95, 72, 101, 97, 100,},
                // /Textk#C3#B6rper
                new byte[]{84, 101, 120, 116, 107, 35, 67, 51, 35, 66, 54, 114, 112, 101, 114,},
                // /Textk#C3#B6rper-Erstzeile
                new byte[]{84, 101, 120, 116, 107, 35, 67, 51, 35, 66, 54, 114, 112, 101, 114, 45, 69, 114, 115, 116, 122, 101, 105, 108, 101,},
                // /Textk#C3#B6rper_Back
                new byte[]{84, 101, 120, 116, 107, 35, 67, 51, 35, 66, 54, 114, 112, 101, 114, 95, 66, 97, 99, 107,},
                // /_No_paragraph_style_
                new byte[]{95, 78, 111, 95, 112, 97, 114, 97, 103, 114, 97, 112, 104, 95, 115, 116, 121, 108, 101, 95}
        };
        boolean[] haveValue = new boolean[] {true, true, false, true, true, true, false, false, false, false, false, false, false, false, false, false, false, false};
        List<PdfName> names = new ArrayList<>();
        for (int i = 0; i < namesBytes.length; i++) {
            byte[] b = namesBytes[i];
            PdfName n = new PdfName(b);
            names.add(n);
            if (haveValue[i]) {
                n.generateValue();
            }
        }

        PdfDictionary dict = new PdfDictionary();
        for (PdfName name : names) {
            dict.put(name, new PdfName("dummy"));
        }

        PdfName expectedToContain = new PdfName("Article");
        boolean found = false;
        for (PdfName pdfName : dict.keySet()) {
            found = pdfName.equals(expectedToContain);
            if (found) {
                break;
            }
        }
        Assertions.assertTrue(found);
        Assertions.assertTrue(dict.containsKey(expectedToContain));
    }
}