/*
* 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.pdfbox.cos;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.pdfbox.pdfwriter.COSWriter;
/**
* This will test all of the filters in the PDFBox system.
*
* @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
* @version $Revision$
*/
public class TestCOSString extends TestCOSBase
{
private static final String ESC_CHAR_STRING =
"( test#some) escaped< \\chars>!~1239857 ";
private static final String ESC_CHAR_STRING_PDF_FORMAT =
"\\( test#some\\) escaped< \\\\chars>!~1239857 ";
/**
* This will get the suite of test that this class holds.
*
* @return All of the tests that this class holds.
*/
public static Test suite()
{
return new TestSuite(TestCOSString.class);
}
@Override
public void setUp()
{
testCOSBase = new COSString("test cos string");
}
/**
* infamous main method.
*
* @param args The command line arguments.
*/
public static void main(String[] args)
{
String[] arg = {TestCOSString.class.getName()};
junit.textui.TestRunner.main(arg);
}
/**
* Tests the public static members within the class that are purely PDF format string objects
* like open/closing strings, escape characters etc...
*/
public void testStaticMembers()
{
stringByteArrayComparison("(", COSString.STRING_OPEN);
stringByteArrayComparison(")", COSString.STRING_CLOSE);
stringByteArrayComparison("<", COSString.HEX_STRING_OPEN);
stringByteArrayComparison(">", COSString.HEX_STRING_CLOSE);
stringByteArrayComparison("\\", COSString.ESCAPE);
stringByteArrayComparison("\\r", COSString.CR_ESCAPE);
stringByteArrayComparison("\\n", COSString.LF_ESCAPE);
stringByteArrayComparison("\\t", COSString.HT_ESCAPE);
stringByteArrayComparison("\\b", COSString.BS_ESCAPE);
stringByteArrayComparison("\\f", COSString.FF_ESCAPE);
}
/**
* Helper method for comparing a string to it's PDF byte array.
*
* @param expected the String expected
* @param member the byte array being tested
*/
private void stringByteArrayComparison(String expected, byte[] member)
{
byte[] expectedBytes = null;
try
{
expectedBytes = expected.getBytes("ISO-8859-1");
}
catch (UnsupportedEncodingException e)
{
fail("ISO-8859-1 encoding threw an exception: " + e.getMessage());
}
testByteArrays(expectedBytes, member);
}
/**
* Test setForceHexForm() and setForceLiteralForm() - tests these two methods do enforce the
* different String output forms within PDF.
*/
public void testSetForceHexLiteralForm()
{
String inputString = "Test with a text and a few numbers 1, 2 and 3";
String pdfLiteral = "(" + inputString + ")";
String pdfHex = "<" + createHex(inputString) + ">";
COSString cosStr = new COSString(inputString);
cosStr.setForceLiteralForm(true);
writePDFTests(pdfLiteral, cosStr);
cosStr.setForceHexForm(true);
writePDFTests(pdfHex, cosStr);
cosStr.setForceLiteralForm(true);
writePDFTests(pdfLiteral, cosStr);
COSString escStr = new COSString(ESC_CHAR_STRING);
writePDFTests("(" + ESC_CHAR_STRING_PDF_FORMAT + ")", escStr);
escStr.setForceHexForm(true);
// Escape characters not escaped in hex version
writePDFTests("<" + createHex(ESC_CHAR_STRING) + ">", escStr);
}
/**
* Helper method for testing writePDF().
*
* @param expected the String expected when writePDF() is invoked
* @param testSubj the test subject
*/
private void writePDFTests(String expected, COSString testSubj)
{
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
try
{
testSubj.writePDF(outStream);
}
catch (IOException e)
{
fail("IOException: " + e.getMessage());
}
assertEquals(expected, outStream.toString());
}
/**
* Test createFromHexString() - tests that the proper String is created from a hex string input.
*/
public void testCreateFromHexString()
{
String expected = "Quick and simple test";
String hexForm = createHex(expected);
try
{
COSString test1 = COSString.createFromHexString(hexForm);
writePDFTests("(" + expected + ")", test1);
COSString test2 = COSString.createFromHexString(createHex(ESC_CHAR_STRING));
writePDFTests("(" + ESC_CHAR_STRING_PDF_FORMAT + ")", test2);
COSString test3 = COSString.createFromHexString(hexForm + "xx", true);
writePDFTests("(" + expected + "?)", test3);
}
catch (IOException e)
{
fail("IOException thrown: " + e.getMessage());
}
try
{
COSString test4 = COSString.createFromHexString(hexForm + "xx", false);
fail("Should have thrown an IOException here");
}
catch (IOException e)
{
// PASS
}
}
private String createHex(String str)
{
StringBuilder sb = new StringBuilder();
for (char c : str.toCharArray())
{
sb.append(Integer.toString(c, 16));
}
return sb.toString().toUpperCase();
}
/**
* Tests getHex() - ensure the hex String returned is properly formatted.
*/
public void testGetHex()
{
String expected = "Test subject for testing getHex";
COSString test1 = new COSString(expected);
String hexForm = createHex(expected);
assertEquals(hexForm, test1.getHexString());
test1.setForceLiteralForm(true);
assertEquals(hexForm, test1.getHexString());
COSString escCS = new COSString(ESC_CHAR_STRING);
// Not sure whether the escaped characters should be escaped or not, presumably since
// writePDF() gives you the proper formatted text, getHex() should ONLY convert to hex.
assertEquals(createHex(ESC_CHAR_STRING), escCS.getHexString());
}
/**
* Test getString() - ensure string are returned in the correct format.
*/
public void testGetString()
{
COSString nullStr = new COSString();
assertEquals("", nullStr.getString());
try
{
String testStr = "Test subject for getString()";
COSString test1 = new COSString(testStr);
assertEquals(testStr, test1.getString());
String appendedStr = "appended text";
test1.append(appendedStr.getBytes());
assertEquals(testStr + appendedStr, test1.getString());
test1.append(ESC_CHAR_STRING.getBytes());
assertEquals(testStr + appendedStr + ESC_CHAR_STRING, test1.getString());
COSString hexStr = COSString.createFromHexString(createHex(testStr));
assertEquals(testStr, hexStr.getString());
COSString escapedString = new COSString(ESC_CHAR_STRING);
assertEquals(ESC_CHAR_STRING, escapedString.getString());
testStr = "Line1\nLine2\nLine3\n";
COSString lineFeedString = new COSString(testStr);
assertEquals(testStr, lineFeedString.getString());
//Same as previous but this time it is constructed incrementally (like in a dictionary)
lineFeedString = new COSString();
for (int i = 0; i < testStr.length(); i++)
{
lineFeedString.append(testStr.charAt(i));
}
assertEquals(testStr, lineFeedString.getString());
testStr = "Text\u2026"; //PDFBOX-1437
COSString pdfbox1437 = new COSString();
pdfbox1437.append(new byte[] {
0x54, 0x65, 0x78, 0x74, (byte)(0x83 & 0xFF)
});
assertEquals(testStr, pdfbox1437.getString());
}
catch (IOException e)
{
fail("IOException thrown: " + e.getMessage());
}
}
/**
* Test append(int) and append(byte[]) - test both code paths.
*/
public void testAppend()
{
try
{
// Mostly tested in testGetString()
COSString testSubj = new COSString();
StringBuilder sb = new StringBuilder();
assertEquals(sb.toString(), testSubj.getString());
// Arbitrary int but makes it easy to test
testSubj.append('a');
sb.append("a");
assertEquals(sb.toString(), testSubj.getString());
testSubj.append(ESC_CHAR_STRING.getBytes());
sb.append(ESC_CHAR_STRING);
assertEquals(sb.toString(), testSubj.getString());
try
{
testSubj.append(null);
assertEquals(sb.toString(), testSubj.getString());
fail("NullPointerException not thrown.");
}
catch (NullPointerException e)
{
// PASS
}
}
catch (IOException e)
{
fail("IOException thrown: " + e.getMessage());
}
}
/**
* Test reset() - tests that the internal buffer is reset. Not a great deal to test here...
*/
public void testReset()
{
String str = "This string is going to be reset";
COSString testSubj = new COSString(str);
assertEquals(str, testSubj.getString());
testSubj.reset();
assertEquals("", testSubj.getString());
}
/**
* Test getBytes() - again not much to test, just ensure the proper byte array is returned.
*/
public void testGetBytes()
{
COSString str = new COSString(ESC_CHAR_STRING);
testByteArrays(ESC_CHAR_STRING.getBytes(), str.getBytes());
}
/**
* Tests writePDF() - tests that the string is in PDF format.
*/
public void testWritePDF()
{
// This has been tested quite thorougly above but do a couple tests anyway
COSString testSubj = new COSString(ESC_CHAR_STRING);
writePDFTests("(" + ESC_CHAR_STRING_PDF_FORMAT + ")", testSubj);
String textString = "This is just an arbitrary piece of text for testing";
COSString testSubj2 = new COSString(textString);
writePDFTests("(" + textString + ")", testSubj2);
}
/**
* This will test all of the filters in the system.
*
* @throws IOException If there is an exception while encoding.
*/
public void testUnicode() throws IOException
{
String theString = "\u4e16";
COSString string = new COSString(theString);
assertTrue(string.getString().equals(theString));
}
@Override
public void testAccept() throws IOException
{
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
ICOSVisitor visitor = new COSWriter(outStream);
COSString testSubj = new COSString(ESC_CHAR_STRING);
testSubj.accept(visitor);
assertEquals("(" + ESC_CHAR_STRING_PDF_FORMAT + ")", outStream.toString());
outStream.reset();
testSubj.setForceHexForm(true);
testSubj.accept(visitor);
assertEquals("<" + createHex(ESC_CHAR_STRING) + ">", outStream.toString());
}
/**
* Tests equals(Object) - ensure that the Object.equals() contract is obeyed.
*/
public void testEquals()
{
// Check all these several times for consistency
for (int i = 0; i < 10; i++)
{
// Reflexive
COSString x1 = new COSString("Test");
assertTrue(x1.equals(x1));
// Symmetry i.e. if x == y then y == x
COSString y1 = new COSString("Test");
assertTrue(x1.equals(y1));
assertTrue(y1.equals(x1));
COSString x2 = new COSString("Test");
x2.setForceHexForm(true);
// also if x != y then y != x
assertFalse(x1.equals(x2));
assertFalse(x2.equals(x1));
// Transitive if x == y && y == z then x == z
COSString z1 = new COSString("Test");
assertTrue(x1.equals(y1));
assertTrue(y1.equals(z1));
assertTrue(x1.equals(z1));
// Test the negative as well if x1 == y1 && y1 != x2 then x1 != x2
assertTrue(x1.equals(y1));
assertFalse(y1.equals(x2));
assertFalse(x1.equals(x2));
// Non-nullity
assertFalse(x1 == null);
assertFalse(y1 == null);
assertFalse(z1 == null);
assertFalse(x2 == null);
// Also check other state
COSString y2 = new COSString("Test");
y2.setForceLiteralForm(true);
assertFalse(y2.equals(x2));
assertTrue(y2.equals(x1));
}
}
/**
* Test hashCode() - tests that the Object.hashCode() contract is obeyed.
*/
public void testHashCode()
{
COSString str1 = new COSString("Test1");
COSString str2 = new COSString("Test2");
assertFalse(str1.hashCode() == str2.hashCode());
COSString str3 = new COSString("Test1");
assertTrue(str1.hashCode() == str3.hashCode());
str3.setForceHexForm(true);
assertFalse(str1.hashCode() == str3.hashCode());
str3.setForceLiteralForm(true);
assertTrue(str1.hashCode() == str3.hashCode());
}
/**
* Test testCompareFromHexString() - tests that Strings created from hex
* compare correctly (PDFBOX-2401)
*/
public void testCompareFromHexString() throws IOException
{
COSString test1 = COSString.createFromHexString("000000FF000000");
COSString test2 = COSString.createFromHexString("000000FF00FFFF");
assertEquals(test1, test1);
assertEquals(test2, test2);
assertFalse(test1.getHexString().equals(test2.getHexString()));
assertFalse(Arrays.equals(test1.getBytes(), test2.getBytes()));
assertFalse(test1.equals(test2));
assertFalse(test2.equals(test1));
assertFalse(test1.getString().equals(test2.getString()));
}
}