XmlUtilsTest.java
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2026 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.utils;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import com.itextpdf.kernel.exceptions.PdfException;
import java.nio.charset.StandardCharsets;
import com.itextpdf.test.ExceptionTestUtil;
import com.itextpdf.test.ExtendedITextTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
@Tag("UnitTest")
public class XmlUtilsTest extends ExtendedITextTest {
private static final String XML_WITH_XXE = "<?xml version=\"1.0\"?>\n"
+ "<!DOCTYPE r [ <!ENTITY xxe SYSTEM \"xxe-data.txt\"> ]>\n"
+ "<body xmlns=\"http://www.w3.org/1999/xhtml\"><p>&xxe;</p></body>";
@BeforeEach
public void resetXmlParserFactoryToDefault() {
XmlProcessorCreator.setXmlParserFactory(null);
}
@Test
public void compareXmlsSameStructureDifferentWhitespace() throws Exception {
String pretty =
"<root>\n" +
" <a>1</a>\n" +
" <b>2</b>\n" +
"</root>";
String compact =
"<root><a>1</a><b>2</b></root>";
Assertions.assertTrue(XmlUtils.compareXmls(getStream(pretty), getStream(compact)));
}
@Test
public void compareXmlsMixedContentDifferentFormatting() throws Exception {
String xml1 =
"<Title>Text\n" +
" <Link>link</Link>\n" +
"</Title>";
String xml2 =
"<Title>Text<Link>link</Link></Title>";
Assertions.assertFalse(XmlUtils.compareXmls(getStream(xml1), getStream(xml2)));
}
@Test
public void compareXmlsDifferentTextContent() throws Exception {
Assertions.assertFalse(XmlUtils.compareXmls(getStream("<root><a>1</a></root>"), getStream("<root><a>2</a></root>")));
}
@Test
public void compareXmlsEmptyElementsWithAttributes() throws Exception {
String xml1 = "<root><a x=\"1\"/></root>";
String xml2 = "<root>\n <a x=\"1\" />\n</root>";
Assertions.assertTrue(XmlUtils.compareXmls(getStream(xml1), getStream(xml2)));
}
@Test
public void emptyElementWithAttributesIsNotRemoved() throws Exception {
String xmlWithWhitespace =
"<root>\n" +
" <a x=\"1\"> \n </a>\n" +
"</root>";
String xmlExpected =
"<root><a x=\"1\"/></root>";
Assertions.assertTrue(XmlUtils.compareXmls(getStream(xmlWithWhitespace), getStream(xmlExpected)));
}
@Test
public void safeXmlDocumentTest() {
Exception e = Assertions.assertThrows(PdfException.class,
() -> XmlUtils.initXmlDocument(getStream(XML_WITH_XXE)));
Assertions.assertEquals(ExceptionTestUtil.getDoctypeIsDisallowedExceptionMessage(), e.getMessage());
}
@Test
public void initNewXmlDocumentTest() {
Document doc = XmlUtils.initNewXmlDocument();
Assertions.assertNotNull(doc);
}
private static InputStream getStream(String s) {
return new ByteArrayInputStream(s.getBytes(StandardCharsets.UTF_8));
}
}