Skip to content

Commit

Permalink
Upgrade PDFBox API to 2.0.27 (#469)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomRoush committed Jan 2, 2023
1 parent 47824b4 commit 45da926
Show file tree
Hide file tree
Showing 76 changed files with 3,789 additions and 3,921 deletions.
Expand Up @@ -126,7 +126,7 @@ public void setUp() throws Exception
permission1.setCanModify(false);
permission1.setCanModifyAnnotations(false);
permission1.setCanPrint(false);
permission1.setCanPrintDegraded(false);
permission1.setCanPrintFaithful(false);

permission2 = new AccessPermission();
permission2.setCanAssembleDocument(false);
Expand All @@ -136,7 +136,7 @@ public void setUp() throws Exception
permission2.setCanModify(false);
permission2.setCanModifyAnnotations(false);
permission2.setCanPrint(true); // it is true now !
permission2.setCanPrintDegraded(false);
permission2.setCanPrintFaithful(false);

recipient1 = getRecipient("test1.der", permission1);
recipient2 = getRecipient("test2.der", permission2);
Expand Down Expand Up @@ -228,7 +228,7 @@ public void testProtection() throws Exception
Assert.assertFalse(permission.canModify());
Assert.assertFalse(permission.canModifyAnnotations());
Assert.assertFalse(permission.canPrint());
Assert.assertFalse(permission.canPrintDegraded());
Assert.assertFalse(permission.canPrintFaithful());
}
finally
{
Expand Down Expand Up @@ -264,7 +264,7 @@ public void testMultipleRecipients() throws Exception
Assert.assertFalse(permission.canModify());
Assert.assertFalse(permission.canModifyAnnotations());
Assert.assertFalse(permission.canPrint());
Assert.assertFalse(permission.canPrintDegraded());
Assert.assertFalse(permission.canPrintFaithful());
}
finally
{
Expand All @@ -283,7 +283,7 @@ public void testMultipleRecipients() throws Exception
Assert.assertFalse(permission.canModify());
Assert.assertFalse(permission.canModifyAnnotations());
Assert.assertTrue(permission.canPrint());
Assert.assertFalse(permission.canPrintDegraded());
Assert.assertFalse(permission.canPrintFaithful());
}
finally
{
Expand Down
Expand Up @@ -110,7 +110,7 @@ public void setUp() throws Exception
permission.setCanModify(false);
permission.setCanModifyAnnotations(false);
permission.setCanPrint(true);
permission.setCanPrintDegraded(false);
permission.setCanPrintFaithful(false);
permission.setReadOnly();
}

Expand Down Expand Up @@ -147,7 +147,7 @@ public void testPermissions() throws Exception

restrAP.setCanAssembleDocument(false);
restrAP.setCanExtractForAccessibility(false);
restrAP.setCanPrintDegraded(false);
restrAP.setCanPrintFaithful(false);

inputFileAsByteArray = getFileResourceAsByteArray("PasswordSample-128bit.pdf");
checkPerms(inputFileAsByteArray, "owner", fullAP);
Expand Down Expand Up @@ -196,7 +196,7 @@ private void checkPerms(byte[] inputFileAsByteArray, String password,
assertEquals(expectedPermissions.canModify(), currentAccessPermission.canModify());
assertEquals(expectedPermissions.canModifyAnnotations(), currentAccessPermission.canModifyAnnotations());
assertEquals(expectedPermissions.canPrint(), currentAccessPermission.canPrint());
assertEquals(expectedPermissions.canPrintDegraded(), currentAccessPermission.canPrintDegraded());
assertEquals(expectedPermissions.canPrintFaithful(), currentAccessPermission.canPrintFaithful());

new PDFRenderer(doc).renderImage(0);

Expand Down
Expand Up @@ -18,6 +18,8 @@
package com.tom_roush.pdfbox.pdmodel.font;

import android.content.Context;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.Log;

import androidx.test.platform.app.InstrumentationRegistry;
Expand Down Expand Up @@ -51,7 +53,6 @@
import com.tom_roush.pdfbox.text.PDFTextStripper;

import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;

Expand Down Expand Up @@ -228,7 +229,7 @@ public void testFullEmbeddingTTC() throws IOException
break;
}
}
Assume.assumeTrue("testFullEmbeddingTTC skipped, no .ttc files available", ttc != null);
assumeTrue("testFullEmbeddingTTC skipped, no .ttc files available", ttc != null);

final List<String> names = new ArrayList<String>();
ttc.processAllFonts(new TrueTypeCollection.TrueTypeFontProcessor()
Expand Down Expand Up @@ -431,4 +432,31 @@ public void testSoftHyphen() throws IOException
Assert.assertEquals(text + "\n" + text, extractedText.trim());
doc.close();
}

/**
* Test font with an unusual cmap table combination (0, 3).
*
* @throws IOException
*/
@Test
public void testPDFBox5484() throws IOException
{
File fontFile = TestResourceGenerator.downloadTestResource(IN_DIR, "PDFBOX-5484.ttf", "https://issues.apache.org/jira/secure/attachment/13047577/PDFBOX-5484.ttf");
assumeTrue(fontFile.exists());

TrueTypeFont ttf = new TTFParser().parse(fontFile);
PDDocument doc = new PDDocument();
PDTrueTypeFont tr = PDTrueTypeFont.load(doc, ttf, WinAnsiEncoding.INSTANCE);
Path path1 = tr.getPath("oslash");
Path path2 = tr.getPath(248);
Assert.assertFalse(path2.isEmpty()); // not empty

RectF area1 = new RectF();
path1.computeBounds(area1, true);
RectF area2 = new RectF();
path2.computeBounds(area2, true);

Assert.assertTrue(area1.equals(area2)); // assertEquals does not test equals()
doc.close();
}
}
Expand Up @@ -487,6 +487,10 @@ private CFFFont parseFont(CFFDataInput input, String name, byte[] topDictIndex)
int charStringsOffset = charStringsEntry.getNumber(0).intValue();
input.setPosition(charStringsOffset);
byte[][] charStringsIndex = readIndexData(input);
if (charStringsIndex == null)
{
throw new IOException("CharStringsIndex is missing");
}

// charset
DictData.Entry charsetEntry = topDict.getEntry("charset");
Expand Down
32 changes: 24 additions & 8 deletions library/src/main/java/com/tom_roush/fontbox/cmap/CMapParser.java
Expand Up @@ -275,6 +275,10 @@ private void parseBegincodespacerange(Number cosCount, PushbackInputStream cmapS
checkExpectedOperator((Operator) nextToken, "endcodespacerange", "codespacerange");
break;
}
if (!(nextToken instanceof byte[]))
{
throw new IOException("start range missing");
}
byte[] startRange = (byte[]) nextToken;
byte[] endRange = (byte[]) parseNextToken(cmapStream);
try
Expand All @@ -298,6 +302,10 @@ private void parseBeginbfchar(Number cosCount, PushbackInputStream cmapStream, C
checkExpectedOperator((Operator) nextToken, "endbfchar", "bfchar");
break;
}
if (!(nextToken instanceof byte[]))
{
throw new IOException("input code missing");
}
byte[] inputCode = (byte[]) nextToken;
nextToken = parseNextToken(cmapStream);
if (nextToken instanceof byte[])
Expand Down Expand Up @@ -328,6 +336,10 @@ private void parseBegincidrange(int numberOfLines, PushbackInputStream cmapStrea
checkExpectedOperator((Operator) nextToken, "endcidrange", "cidrange");
break;
}
if (!(nextToken instanceof byte[]))
{
throw new IOException("start range missing");
}
byte[] startCode = (byte[]) nextToken;
int start = createIntFromBytes(startCode);
byte[] endCode = (byte[]) parseNextToken(cmapStream);
Expand Down Expand Up @@ -369,6 +381,10 @@ private void parseBegincidchar(Number cosCount, PushbackInputStream cmapStream,
checkExpectedOperator((Operator) nextToken, "endcidchar", "cidchar");
break;
}
if (!(nextToken instanceof byte[]))
{
throw new IOException("start code missing");
}
byte[] inputCode = (byte[]) nextToken;
int mappedCode = (Integer) parseNextToken(cmapStream);
int mappedCID = createIntFromBytes(inputCode);
Expand All @@ -381,26 +397,26 @@ private void parseBeginbfrange(Number cosCount, PushbackInputStream cmapStream,
for (int j = 0; j < cosCount.intValue(); j++)
{
Object nextToken = parseNextToken(cmapStream);
if (nextToken == null)
{
throw new IOException("start code missing");
}
if (nextToken instanceof Operator)
{
checkExpectedOperator((Operator) nextToken, "endbfrange", "bfrange");
break;
}
byte[] startCode = (byte[]) nextToken;
nextToken = parseNextToken(cmapStream);
if (nextToken == null)
if (!(nextToken instanceof byte[]))
{
throw new IOException("end code missing");
throw new IOException("start code missing");
}
byte[] startCode = (byte[]) nextToken;
nextToken = parseNextToken(cmapStream);
if (nextToken instanceof Operator)
{
checkExpectedOperator((Operator) nextToken, "endbfrange", "bfrange");
break;
}
if (!(nextToken instanceof byte[]))
{
throw new IOException("end code missing");
}
byte[] endCode = (byte[]) nextToken;
int start = CMap.toInt(startCode, startCode.length);
int end = CMap.toInt(endCode, endCode.length);
Expand Down
Expand Up @@ -167,9 +167,9 @@ private void parsePfb(final byte[] pfb) throws IOException
}
if (size > pfbdata.length - pointer)
{
throw new IOException("PFB record size (" + size +
") doesn't fit in buffer, position: " + pointer +
", total length: " + pfbdata.length);
throw new EOFException("attempted to read " + size + " bytes at position " + pointer +
" into array of size " + pfbdata.length + ", but only space for " +
(pfbdata.length - pointer) + " bytes left");
}
int got = in.read(pfbdata, pointer, size);
if (got < 0)
Expand Down
Expand Up @@ -663,7 +663,7 @@ public Integer getCharacterCode(int gid)

private int getCharCode(int gid)
{
if (gid < 0 || gid >= glyphIdToCharacterCode.length)
if (gid < 0 || glyphIdToCharacterCode == null || gid >= glyphIdToCharacterCode.length)
{
return -1;
}
Expand Down
Expand Up @@ -39,6 +39,8 @@ public class GlyphTable extends TTFTable

private int cached = 0;

private HorizontalMetricsTable hmt = null;

/**
* Don't even bother to cache huge fonts.
*/
Expand Down Expand Up @@ -75,6 +77,12 @@ void read(TrueTypeFont ttf, TTFDataStream data) throws IOException

// we don't actually read the complete table here because it can contain tens of thousands of glyphs
this.data = data;

// PDFBOX-5460: read hmtx table early to avoid deadlock if getGlyph() locks "data"
// and then locks TrueTypeFont to read this table, while another thread
// locks TrueTypeFont and then tries to lock "data"
hmt = font.getHorizontalMetrics();

initialized = true;
}

Expand Down Expand Up @@ -207,7 +215,6 @@ public GlyphData getGlyph(int gid) throws IOException
private GlyphData getGlyphData(int gid) throws IOException
{
GlyphData glyph = new GlyphData();
HorizontalMetricsTable hmt = font.getHorizontalMetrics();
int leftSideBearing = hmt == null ? 0 : hmt.getLeftSideBearing(gid);
glyph.initData(this, data, leftSideBearing);
// resolve composite glyph
Expand Down
Expand Up @@ -744,7 +744,11 @@ private void readSubrs(int lenIV) throws IOException

// RD
Token charstring = read(Token.CHARSTRING);
font.subrs.set(index.intValue(), decrypt(charstring.getData(), CHARSTRING_KEY, lenIV));
int j = index.intValue();
if (j < font.subrs.size())
{
font.subrs.set(j, decrypt(charstring.getData(), CHARSTRING_KEY, lenIV));
}
readPut();
}
readDef();
Expand Down
Expand Up @@ -682,12 +682,12 @@ else if(obj instanceof COSString)
}
else if (obj instanceof COSArray)
{
Log.e("PdfBox-Android", "Nested arrays are not allowed in an array for TJ operation:" + obj);
Log.e("PdfBox-Android", "Nested arrays are not allowed in an array for TJ operation: " + obj);
}
else
{
throw new IOException("Unknown type " + obj.getClass().getSimpleName()
+ " in array for TJ operation:" + obj);
Log.e("PdfBox-Android", "Unknown type " + obj.getClass().getSimpleName()
+ " in array for TJ operation: " + obj);
}
}
}
Expand Down
17 changes: 12 additions & 5 deletions library/src/main/java/com/tom_roush/pdfbox/cos/COSDictionary.java
Expand Up @@ -22,6 +22,7 @@
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand All @@ -40,6 +41,7 @@
public class COSDictionary extends COSBase implements COSUpdateInfo
{
private static final String PATH_SEPARATOR = "/";
private static final int MAP_THRESHOLD = 1000;
private boolean needToBeUpdated;

/**
Expand All @@ -62,7 +64,7 @@ public COSDictionary()
*/
public COSDictionary(COSDictionary dict)
{
items.putAll(dict.items);
addAll(dict);
}

/**
Expand Down Expand Up @@ -214,6 +216,10 @@ public void setItem(COSName key, COSBase value)
}
else
{
if (items instanceof SmallMap && items.size() >= MAP_THRESHOLD)
{
items = new LinkedHashMap<COSName, COSBase>(items);
}
items.put(key, value);
}
}
Expand Down Expand Up @@ -1438,14 +1444,15 @@ public void setNeedToBeUpdated(boolean flag)
* This will add all of the dictionaries keys/values to this dictionary. Existing key/value pairs will be
* overwritten.
*
* @param dic The dictionaries to get the key/value pairs from.
* @param dict The dictionaries to get the key/value pairs from.
*/
public void addAll(COSDictionary dic)
public void addAll(COSDictionary dict)
{
for (Map.Entry<COSName, COSBase> entry : dic.entrySet())
if (items instanceof SmallMap && items.size() + dict.items.size() >= MAP_THRESHOLD)
{
setItem(entry.getKey(), entry.getValue());
items = new LinkedHashMap<COSName, COSBase>(items);
}
items.putAll(dict.items);
}

/**
Expand Down

0 comments on commit 45da926

Please sign in to comment.