Skip to content

Commit 2a6f133

Browse files
committed
PDFBOX-6065: handle KwKwK special case, by Daniel Persson with ChatGPT
git-svn-id: https://svn.apache.org/repos/asf/pdfbox/trunk@1928315 13f79535-47bb-0310-9956-ffa450edef68
1 parent e31e317 commit 2a6f133

1 file changed

Lines changed: 35 additions & 47 deletions

File tree

pdfbox/src/main/java/org/apache/pdfbox/filter/LZWFilter.java

Lines changed: 35 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,6 @@ public class LZWFilter extends Filter
5454
* The LZW end of data code.
5555
*/
5656
public static final long EOD = 257;
57-
58-
//BEWARE: codeTable must be local to each method, because there is only
59-
// one instance of each filter
6057

6158
/**
6259
* {@inheritDoc}
@@ -73,11 +70,12 @@ public DecodeResult decode(InputStream encoded, OutputStream decoded,
7370

7471
private static void doLZWDecode(InputStream encoded, OutputStream decoded, boolean earlyChange) throws IOException
7572
{
76-
List<byte[]> codeTable = new ArrayList<>();
73+
List<byte[]> codeTable = createCodeTable(); // includes CLEAR/EOD handling as needed
7774
int chunk = 9;
7875
final MemoryCacheImageInputStream in = new MemoryCacheImageInputStream(encoded);
76+
77+
byte[] prev = null; // no previous string yet
7978
long nextCommand;
80-
long prevCommand = -1;
8179

8280
try
8381
{
@@ -87,60 +85,50 @@ private static void doLZWDecode(InputStream encoded, OutputStream decoded, boole
8785
{
8886
chunk = 9;
8987
codeTable = createCodeTable();
90-
prevCommand = -1;
88+
prev = null;
89+
continue;
9190
}
92-
else
91+
92+
byte[] curr;
93+
94+
if (nextCommand < codeTable.size())
9395
{
94-
if (nextCommand < codeTable.size())
95-
{
96-
byte[] data = codeTable.get((int) nextCommand);
97-
byte firstByte = data[0];
98-
decoded.write(data);
99-
if (prevCommand != -1)
100-
{
101-
checkIndexBounds(codeTable, prevCommand, in);
102-
data = codeTable.get((int) prevCommand);
103-
byte[] newData = Arrays.copyOf(data, data.length + 1);
104-
newData[data.length] = firstByte;
105-
codeTable.add(newData);
106-
}
107-
}
108-
else
96+
// Normal case: code exists
97+
curr = codeTable.get((int) nextCommand);
98+
decoded.write(curr);
99+
100+
if (prev != null)
109101
{
110-
checkIndexBounds(codeTable, prevCommand, in);
111-
byte[] data = codeTable.get((int) prevCommand);
112-
byte[] newData = Arrays.copyOf(data, data.length + 1);
113-
newData[data.length] = data[0];
114-
decoded.write(newData);
115-
codeTable.add(newData);
102+
// Add prev + first(curr)
103+
byte[] entry = Arrays.copyOf(prev, prev.length + 1);
104+
entry[prev.length] = curr[0];
105+
codeTable.add(entry);
116106
}
117-
118-
chunk = calculateChunk(codeTable.size(), earlyChange);
119-
prevCommand = nextCommand;
120107
}
108+
else if (nextCommand == codeTable.size() && prev != null)
109+
{
110+
// KwKwK case: code equals next available index
111+
curr = Arrays.copyOf(prev, prev.length + 1);
112+
curr[prev.length] = prev[0];
113+
decoded.write(curr);
114+
codeTable.add(curr);
115+
}
116+
else
117+
{
118+
// Corrupt stream (code out of range, or KwKwK without prev)
119+
throw new EOFException("Invalid LZW code: " + nextCommand);
120+
}
121+
122+
prev = curr; // move forward
123+
chunk = calculateChunk(codeTable.size(), earlyChange);
121124
}
122125
}
123126
catch (EOFException ex)
124127
{
125128
LOG.warn("Premature EOF in LZW stream, EOD code missing", ex);
126129
}
127-
decoded.flush();
128-
}
129130

130-
private static void checkIndexBounds(List<byte[]> codeTable, long index, MemoryCacheImageInputStream in)
131-
throws IOException
132-
{
133-
if (index < 0)
134-
{
135-
throw new IOException("negative array index: " + index + " near offset "
136-
+ in.getStreamPosition());
137-
}
138-
if (index >= codeTable.size())
139-
{
140-
throw new IOException("array index overflow: " + index +
141-
" >= " + codeTable.size() + " near offset "
142-
+ in.getStreamPosition());
143-
}
131+
decoded.flush();
144132
}
145133

146134
/**

0 commit comments

Comments
 (0)