Skip to content
Snippets Groups Projects
Commit aa829d87 authored by Markus Koschany's avatar Markus Koschany
Browse files

New upstream version 1.1.41

parent 697b0f3c
No related branches found
No related tags found
No related merge requests found
Showing
with 871 additions and 427 deletions
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<artifactId>sambox</artifactId> <artifactId>sambox</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>sambox</name> <name>sambox</name>
<version>1.1.19</version> <version>1.1.41</version>
<description>An Apache PDFBox fork intended to be used as PDF processor for Sejda and PDFsam related projects</description> <description>An Apache PDFBox fork intended to be used as PDF processor for Sejda and PDFsam related projects</description>
<url>http://www.sejda.org</url> <url>http://www.sejda.org</url>
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
<connection>scm:git:git@github.com:torakiki/sambox.git</connection> <connection>scm:git:git@github.com:torakiki/sambox.git</connection>
<developerConnection>scm:git:git@github.com:torakiki/sambox.git</developerConnection> <developerConnection>scm:git:git@github.com:torakiki/sambox.git</developerConnection>
<url>scm:git:git@github.com:torakiki/sambox.git</url> <url>scm:git:git@github.com:torakiki/sambox.git</url>
<tag>v1.1.19</tag> <tag>v1.1.41</tag>
</scm> </scm>
<developers> <developers>
...@@ -138,6 +138,47 @@ ...@@ -138,6 +138,47 @@
</plugins> </plugins>
</build> </build>
</profile> </profile>
<profile>
<id>private-release</id>
<distributionManagement>
<snapshotRepository>
<id>sejda-pro-snapshot</id>
<url>http://mvn.sejda.com/artifactory/libs-snapshot</url>
</snapshotRepository>
<repository>
<id>sejda-pro</id>
<url>http://mvn.sejda.com/artifactory/libs-release</url>
</repository>
</distributionManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.2</version>
<configuration>
<tagNameFormat>v@{project.version}</tagNameFormat>
<preparationGoals>clean install</preparationGoals>
<localCheckout>true</localCheckout>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles> </profiles>
<build> <build>
...@@ -192,7 +233,7 @@ ...@@ -192,7 +233,7 @@
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version> <version>2.19.1</version>
<configuration> <configuration>
<argLine>-Xmx768m</argLine> <argLine>-Xmx768m -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider</argLine>
<excludes> <excludes>
<exclude>org/sejda/sambox/rendering/TestPDFToImage.java</exclude> <exclude>org/sejda/sambox/rendering/TestPDFToImage.java</exclude>
</excludes> </excludes>
...@@ -200,6 +241,187 @@ ...@@ -200,6 +241,187 @@
<reuseForks>false</reuseForks> <reuseForks>false</reuseForks>
</configuration> </configuration>
</plugin> </plugin>
<!-- PDFBOX-3974: download test files from JIRA and keep them in repository cache -->
<plugin>
<groupId>com.googlecode.maven-download-plugin</groupId>
<artifactId>download-maven-plugin</artifactId>
<version>1.3.0</version>
<executions>
<execution>
<id>PDFBOX-3703</id>
<phase>generate-test-resources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>https://issues.apache.org/jira/secure/attachment/12854913/966635-p12.pdf</url>
<outputDirectory>${project.build.directory}/pdfs</outputDirectory>
<outputFileName>PDFBOX-3703-966635-p12.pdf</outputFileName>
<sha512>28fcb3be0bd3aa983a05107912b7c75ec8203b1ab14e7e76fa2b542d9d2dec9c96921d4220610dff96a299d935d9fffb3be2b552421b516a93344b14aed0ce0d</sha512>
</configuration>
</execution>
<execution>
<id>PDFBOX-3747</id>
<phase>generate-test-resources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>https://github.com/jondot/dotfiles/blob/master/.fonts/calibri.ttf?raw=true</url>
<outputDirectory>${project.build.directory}/fonts</outputDirectory>
<outputFileName>PDFBOX-3747-calibri.ttf</outputFileName>
<sha512>b7eb8e6f2a4549eb68280d0d8834b2a14f711f2d15ffe1420fde654f05dd939181c617bf51e11c44aededaa729966b49288b0a07a35b79aa73a08b8c48b72de0</sha512>
</configuration>
</execution>
<execution>
<id>PDFBOX-3948</id>
<phase>generate-test-resources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>https://issues.apache.org/jira/secure/attachment/12890034/EUWO6SQS5TM4VGOMRD3FLXZHU35V2CP2.pdf</url>
<outputDirectory>${project.build.directory}/pdfs</outputDirectory>
<outputFileName>PDFBOX-3948-EUWO6SQS5TM4VGOMRD3FLXZHU35V2CP2.pdf</outputFileName>
<sha512>f8a9b0b9ea6132f24e54136a40ad99d67df2402f3849a5cb0b7d80cd72298737fe4701e0e77ddd602a06e3ea0a7e107ca40d8d29389eea5834ff37245829c2d2</sha512>
</configuration>
</execution>
<execution>
<id>PDFBOX-3949</id>
<phase>generate-test-resources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>https://issues.apache.org/jira/secure/attachment/12890037/MKFYUGZWS3OPXLLVU2Z4LWCTVA5WNOGF.pdf</url>
<outputDirectory>${project.build.directory}/pdfs</outputDirectory>
<outputFileName>PDFBOX-3949-MKFYUGZWS3OPXLLVU2Z4LWCTVA5WNOGF.pdf</outputFileName>
<sha512>f450fb40ed5589ce0f390eb110d78bc721b766c34b753770b0cb00b2e40ffe15878f54df2423ab99d7df80dd91512858bf56a7cdc392d5c179b4440176fdd2fb</sha512>
</configuration>
</execution>
<execution>
<id>PDFBOX-3950</id>
<phase>generate-test-resources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>https://issues.apache.org/jira/secure/attachment/12890042/23EGDHXSBBYQLKYOKGZUOVYVNE675PRD.pdf</url>
<outputDirectory>${project.build.directory}/pdfs</outputDirectory>
<outputFileName>PDFBOX-3950-23EGDHXSBBYQLKYOKGZUOVYVNE675PRD.pdf</outputFileName>
<sha512>ee1d464c3ed2ad91a4cafbc474b38e5c961282f53ef599d6d10e02058da5a67064550ddc54774dfa843a8b45f34b7e6e8ab4f9a445ba459fdcd858e8dce65b25</sha512>
</configuration>
</execution>
<execution>
<id>PDFBOX-3951</id>
<phase>generate-test-resources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>https://issues.apache.org/jira/secure/attachment/12890047/FIHUZWDDL2VGPOE34N6YHWSIGSH5LVGZ.pdf</url>
<outputDirectory>${project.build.directory}/pdfs</outputDirectory>
<outputFileName>PDFBOX-3951-FIHUZWDDL2VGPOE34N6YHWSIGSH5LVGZ.pdf</outputFileName>
<sha512>2c0b91beb4a2b098738512fefdd40135bf66286cd350ac4e155a5a0150d649acb1da819c817ee9822e8686f526af6b7862fc63a0dae6dc7f1407c7f8b271c65e</sha512>
</configuration>
</execution>
<execution>
<id>PDFBOX-3964</id>
<phase>generate-test-resources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>https://issues.apache.org/jira/secure/attachment/12892097/c687766d68ac766be3f02aaec5e0d713_2.pdf</url>
<outputDirectory>${project.build.directory}/pdfs</outputDirectory>
<outputFileName>PDFBOX-3964-c687766d68ac766be3f02aaec5e0d713_2.pdf</outputFileName>
<sha512>0457fd291a7f83f531fef205128929c8fa8147dd781ea7b7cd49d4d1287941989e72739329a7b172c6f53df0b54d991b514b9baa6145effa8ec7705ef273877b</sha512>
</configuration>
</execution>
<execution>
<id>PDFBOX-4022</id>
<phase>generate-test-resources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>https://issues.apache.org/jira/secure/attachment/12899008/selection.pdf</url>
<outputDirectory>${project.build.directory}/pdfs</outputDirectory>
<outputFileName>PDFBOX-4022-selection.pdf</outputFileName>
<sha512>d08af71bc8e3911ee3ed7c9ce9d4acc0562488981bc83a9c612de9d5f0640fd2d9805f600810f1cad5293fa4acda12444a0dcefa2543125c95d06059feb2c4f0</sha512>
</configuration>
</execution>
<execution>
<id>PDFBOX-4106</id>
<phase>generate-test-resources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>https://ipafont.ipa.go.jp/old/ipafont/ipag00303.php</url>
<outputDirectory>${project.build.directory}/fonts</outputDirectory>
<outputFileName>ipag00303.zip</outputFileName>
<unpack>true</unpack>
<sha512>59535137c649a2f8bdbb463cd716426811a6003a65883ca6e45bb0af1d526b3889af0fba3a353e90bc8d373cd32b90a27ff9ff6916ecbccb42e922c09e9b046a</sha512>
</configuration>
</execution>
<execution>
<id>PDFBOX-4106b</id>
<phase>generate-test-resources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>https://ipafont.ipa.go.jp/old/ipafont/ipagp00303.php</url>
<outputDirectory>${project.build.directory}/fonts</outputDirectory>
<outputFileName>ipagp00303.zip</outputFileName>
<unpack>true</unpack>
<sha512>26d0a9bfba7f5457a98b0bf45a4a6b081bca4140047a0886625691231459f8c81a6cdbe523e9abcbd45fd7caed21d78f1baf3a2cf9167320f6b79be3d697cb5b</sha512>
</configuration>
</execution>
<execution>
<id>PDFBOX-4115</id>
<phase>generate-test-resources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>https://issues.apache.org/jira/secure/attachment/12911053/n019003l.pfb</url>
<outputDirectory>${project.build.directory}/fonts</outputDirectory>
<outputFileName>n019003l.pfb</outputFileName>
<sha512>8eafe21ffa6f3d7d0a50e9f4e5bcdeb727e804b552d74e65b709e778c9ed4605e5aa63743be285f0bc17ad162768583fec4196e1d1146d98f8703359247f22d0</sha512>
</configuration>
</execution>
<execution>
<id>PDFBOX-4197</id>
<phase>generate-test-resources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>https://issues.apache.org/jira/secure/attachment/12919726/sample.pdf</url>
<outputDirectory>${project.build.directory}/pdfs</outputDirectory>
<outputFileName>PDFBOX-4197.pdf</outputFileName>
<sha512>6fefc869dff9db8cd539db177d35beeacc62304173245742eaee8882dab330860a31cbbd4c4ec6cc724603cc453afc07ec61361fbc1e80a47f44b04ccfbaf40d</sha512>
</configuration>
</execution>
<execution>
<id>PDFBOX-4184</id>
<phase>generate-test-resources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>http://www.crh.noaa.gov/Image/gjt/images/ImageGallery/Uncompahgre_small.jpg</url>
<!-- file is also 032163.jpg
from http://downloads.digitalcorpora.org/corpora/files/govdocs1/zipfiles/032.zip -->
<outputDirectory>${project.build.directory}/imgs</outputDirectory>
<outputFileName>PDFBOX-4184-032163.jpg</outputFileName>
<sha512>35241c979d3808ca9d2641b5ec5e40637132b313f75070faca8b8f6d00ddce394070414236db3993f1092fe3bc16995750d528b6d803a7851423c14c308ccdde</sha512>
</configuration>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>
...@@ -222,12 +444,12 @@ ...@@ -222,12 +444,12 @@
<dependency> <dependency>
<groupId>commons-io</groupId> <groupId>commons-io</groupId>
<artifactId>commons-io</artifactId> <artifactId>commons-io</artifactId>
<version>2.5</version> <version>2.6</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.pdfbox</groupId> <groupId>org.apache.pdfbox</groupId>
<artifactId>fontbox</artifactId> <artifactId>fontbox</artifactId>
<version>2.0.8</version> <version>2.0.11</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<artifactId>commons-logging</artifactId> <artifactId>commons-logging</artifactId>
...@@ -239,13 +461,19 @@ ...@@ -239,13 +461,19 @@
<groupId>org.bouncycastle</groupId> <groupId>org.bouncycastle</groupId>
<artifactId>bcmail-jdk15on</artifactId> <artifactId>bcmail-jdk15on</artifactId>
<optional>true</optional> <optional>true</optional>
<version>1.56</version> <version>1.60</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.bouncycastle</groupId> <groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId> <artifactId>bcprov-jdk15on</artifactId>
<optional>true</optional> <optional>true</optional>
<version>1.56</version> <version>1.60</version>
</dependency>
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>jbig2-imageio</artifactId>
<version>3.0.1</version>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ch.qos.logback</groupId> <groupId>ch.qos.logback</groupId>
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
*/ */
package org.sejda.sambox.contentstream; package org.sejda.sambox.contentstream;
import static java.util.Objects.isNull;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import java.awt.geom.GeneralPath; import java.awt.geom.GeneralPath;
...@@ -104,6 +105,22 @@ public abstract class PDFStreamEngine ...@@ -104,6 +105,22 @@ public abstract class PDFStreamEngine
operators.put(op.getName(), op); operators.put(op.getName(), op);
} }
/**
* Adds an operator processor to the engine if there isn't an operator already associated with the PDF operator.
*
* @param op operator processor
* @return true if the operator is added, false if not (there's already an operator associated)
*/
public final boolean addOperatorIfAbsent(OperatorProcessor op)
{
if (isNull(operators.putIfAbsent(op.getName(), op)))
{
op.setContext(this);
return true;
}
return false;
}
/** /**
* Initialises the stream engine for the given page. * Initialises the stream engine for the given page.
*/ */
...@@ -163,7 +180,10 @@ public abstract class PDFStreamEngine ...@@ -163,7 +180,10 @@ public abstract class PDFStreamEngine
throw new IllegalStateException("No current page, call " throw new IllegalStateException("No current page, call "
+ "#processChildStream(PDContentStream, PDPage) instead"); + "#processChildStream(PDContentStream, PDPage) instead");
} }
processStream(form); if (!form.getCOSObject().isEmpty())
{
processStream(form);
}
} }
/** /**
...@@ -431,7 +451,7 @@ public abstract class PDFStreamEngine ...@@ -431,7 +451,7 @@ public abstract class PDFStreamEngine
* @param contentStream the content stream * @param contentStream the content stream
* @throws IOException if there is an exception while processing the stream * @throws IOException if there is an exception while processing the stream
*/ */
protected void processStream(PDContentStream contentStream) throws IOException public void processStream(PDContentStream contentStream) throws IOException
{ {
PDResources parent = pushResources(contentStream); PDResources parent = pushResources(contentStream);
Stack<PDGraphicsState> savedStack = saveGraphicsStack(); Stack<PDGraphicsState> savedStack = saveGraphicsStack();
...@@ -879,7 +899,7 @@ public abstract class PDFStreamEngine ...@@ -879,7 +899,7 @@ public abstract class PDFStreamEngine
protected final Stack<PDGraphicsState> saveGraphicsStack() protected final Stack<PDGraphicsState> saveGraphicsStack()
{ {
Stack<PDGraphicsState> savedStack = graphicsStack; Stack<PDGraphicsState> savedStack = graphicsStack;
graphicsStack = new Stack<PDGraphicsState>(); graphicsStack = new Stack<>();
graphicsStack.add(savedStack.peek().clone()); graphicsStack.add(savedStack.peek().clone());
return savedStack; return savedStack;
} }
...@@ -956,7 +976,7 @@ public abstract class PDFStreamEngine ...@@ -956,7 +976,7 @@ public abstract class PDFStreamEngine
} }
/** /**
* Returns the stream' resources. * @return the stream' resources. This is mainly to be used by the {@link OperatorProcessor} classes
*/ */
public PDResources getResources() public PDResources getResources()
{ {
......
...@@ -22,7 +22,6 @@ import java.io.IOException; ...@@ -22,7 +22,6 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
...@@ -480,7 +479,7 @@ public class COSArray extends COSBase implements List<COSBase> ...@@ -480,7 +479,7 @@ public class COSArray extends COSBase implements List<COSBase>
public List<? extends COSBase> toList() public List<? extends COSBase> toList()
{ {
ArrayList<COSBase> retList = new ArrayList<>(size()); ArrayList<COSBase> retList = new ArrayList<>(size());
Collections.copy(retList, objects); retList.addAll(objects);
return retList; return retList;
} }
......
...@@ -592,7 +592,6 @@ public class COSDictionary extends COSBase ...@@ -592,7 +592,6 @@ public class COSDictionary extends COSBase
/** /**
* Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string.
* Null is returned if the entry does not exist in the dictionary.
* *
* @param key The key to the item in the dictionary. * @param key The key to the item in the dictionary.
* @param defaultValue The value to return if the dictionary item is null. * @param defaultValue The value to return if the dictionary item is null.
...@@ -605,7 +604,6 @@ public class COSDictionary extends COSBase ...@@ -605,7 +604,6 @@ public class COSDictionary extends COSBase
/** /**
* Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string.
* Null is returned if the entry does not exist in the dictionary.
* *
* @param key The key to the item in the dictionary. * @param key The key to the item in the dictionary.
* @param defaultValue The value to return if the dictionary item is null. * @param defaultValue The value to return if the dictionary item is null.
...@@ -643,7 +641,6 @@ public class COSDictionary extends COSBase ...@@ -643,7 +641,6 @@ public class COSDictionary extends COSBase
/** /**
* Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string.
* Null is returned if the entry does not exist in the dictionary.
* *
* @param key The key to the item in the dictionary. * @param key The key to the item in the dictionary.
* @param defaultValue The default value to return. * @param defaultValue The default value to return.
...@@ -656,7 +653,6 @@ public class COSDictionary extends COSBase ...@@ -656,7 +653,6 @@ public class COSDictionary extends COSBase
/** /**
* Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string.
* Null is returned if the entry does not exist in the dictionary.
* *
* @param key The key to the item in the dictionary. * @param key The key to the item in the dictionary.
* @param defaultValue The default value to return. * @param defaultValue The default value to return.
...@@ -709,7 +705,6 @@ public class COSDictionary extends COSBase ...@@ -709,7 +705,6 @@ public class COSDictionary extends COSBase
/** /**
* Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string.
* Null is returned if the entry does not exist in the dictionary.
* *
* @param embedded The embedded dictionary. * @param embedded The embedded dictionary.
* @param key The key to the item in the dictionary. * @param key The key to the item in the dictionary.
...@@ -1167,6 +1162,26 @@ public class COSDictionary extends COSBase ...@@ -1167,6 +1162,26 @@ public class COSDictionary extends COSBase
return getItem(COSName.getPDFName(key)); return getItem(COSName.getPDFName(key));
} }
/**
* This is a special case of getItem that takes multiple keys, it will handle the situation
* where multiple keys could get the same value, ie if either CS or ColorSpace is used to get
* the colorspace. This will get an object from this dictionary.
*
* @param firstKey The first key to try.
* @param secondKey The second key to try.
*
* @return The object that matches the key.
*/
public COSBase getItem(COSName firstKey, COSName secondKey)
{
COSBase retval = getItem(firstKey);
if (retval == null && secondKey != null)
{
retval = getItem(secondKey);
}
return retval;
}
/** /**
* @return names of the entries in this dictionary. The returned set is in the order the entries were added to the * @return names of the entries in this dictionary. The returned set is in the order the entries were added to the
* dictionary. * dictionary.
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
*/ */
package org.sejda.sambox.cos; package org.sejda.sambox.cos;
import static java.util.Objects.nonNull;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static org.sejda.util.RequireUtils.requireNotBlank; import static org.sejda.util.RequireUtils.requireNotBlank;
import static org.sejda.util.RequireUtils.requireNotNullArg; import static org.sejda.util.RequireUtils.requireNotNullArg;
...@@ -83,7 +84,8 @@ public class COSDocument extends COSBase ...@@ -83,7 +84,8 @@ public class COSDocument extends COSBase
*/ */
public boolean isEncrypted() public boolean isEncrypted()
{ {
return trailer.getCOSObject().getDictionaryObject(COSName.ENCRYPT) != null; return nonNull(
trailer.getCOSObject().getDictionaryObject(COSName.ENCRYPT, COSDictionary.class));
} }
/** /**
......
...@@ -34,6 +34,7 @@ public final class COSName extends COSBase implements Comparable<COSName> ...@@ -34,6 +34,7 @@ public final class COSName extends COSBase implements Comparable<COSName>
// A // A
public static final COSName A = newCommonInstance("A"); public static final COSName A = newCommonInstance("A");
public static final COSName AA = newCommonInstance("AA"); public static final COSName AA = newCommonInstance("AA");
public static final COSName AC = newCommonInstance("AC");
public static final COSName ACRO_FORM = newCommonInstance("AcroForm"); public static final COSName ACRO_FORM = newCommonInstance("AcroForm");
public static final COSName ACTUAL_TEXT = newCommonInstance("ActualText"); public static final COSName ACTUAL_TEXT = newCommonInstance("ActualText");
public static final COSName ADBE_PKCS7_DETACHED = newCommonInstance("adbe.pkcs7.detached"); public static final COSName ADBE_PKCS7_DETACHED = newCommonInstance("adbe.pkcs7.detached");
...@@ -124,6 +125,7 @@ public final class COSName extends COSBase implements Comparable<COSName> ...@@ -124,6 +125,7 @@ public final class COSName extends COSBase implements Comparable<COSName>
public static final COSName CMAPNAME = newCommonInstance("CMapName"); public static final COSName CMAPNAME = newCommonInstance("CMapName");
public static final COSName CMYK = newCommonInstance("CMYK"); public static final COSName CMYK = newCommonInstance("CMYK");
public static final COSName CO = newCommonInstance("CO"); public static final COSName CO = newCommonInstance("CO");
public static final COSName COLOR = new COSName("Color");
public static final COSName COLOR_BURN = newCommonInstance("ColorBurn"); public static final COSName COLOR_BURN = newCommonInstance("ColorBurn");
public static final COSName COLOR_DODGE = newCommonInstance("ColorDodge"); public static final COSName COLOR_DODGE = newCommonInstance("ColorDodge");
public static final COSName COLORANTS = newCommonInstance("Colorants"); public static final COSName COLORANTS = newCommonInstance("Colorants");
...@@ -184,6 +186,7 @@ public final class COSName extends COSBase implements Comparable<COSName> ...@@ -184,6 +186,7 @@ public final class COSName extends COSBase implements Comparable<COSName>
public static final COSName DOC_OPEN = newCommonInstance("DocOpen"); public static final COSName DOC_OPEN = newCommonInstance("DocOpen");
public static final COSName DOC_TIME_STAMP = newCommonInstance("DocTimeStamp"); public static final COSName DOC_TIME_STAMP = newCommonInstance("DocTimeStamp");
public static final COSName DOCMDP = newCommonInstance("DocMDP"); public static final COSName DOCMDP = newCommonInstance("DocMDP");
public static final COSName DOCUMENT = new COSName("Document");
public static final COSName DOMAIN = newCommonInstance("Domain"); public static final COSName DOMAIN = newCommonInstance("Domain");
public static final COSName DOS = newCommonInstance("DOS"); public static final COSName DOS = newCommonInstance("DOS");
public static final COSName DP = newCommonInstance("DP"); public static final COSName DP = newCommonInstance("DP");
...@@ -264,6 +267,7 @@ public final class COSName extends COSBase implements Comparable<COSName> ...@@ -264,6 +267,7 @@ public final class COSName extends COSBase implements Comparable<COSName>
public static final COSName HIDE_MENUBAR = newCommonInstance("HideMenubar"); public static final COSName HIDE_MENUBAR = newCommonInstance("HideMenubar");
public static final COSName HIDE_TOOLBAR = newCommonInstance("HideToolbar"); public static final COSName HIDE_TOOLBAR = newCommonInstance("HideToolbar");
public static final COSName HIDE_WINDOWUI = newCommonInstance("HideWindowUI"); public static final COSName HIDE_WINDOWUI = newCommonInstance("HideWindowUI");
public static final COSName HUE = new COSName("Hue");
// I // I
public static final COSName I = newCommonInstance("I"); public static final COSName I = newCommonInstance("I");
public static final COSName IC = newCommonInstance("IC"); public static final COSName IC = newCommonInstance("IC");
...@@ -284,6 +288,9 @@ public final class COSName extends COSBase implements Comparable<COSName> ...@@ -284,6 +288,9 @@ public final class COSName extends COSBase implements Comparable<COSName>
public static final COSName INTERPOLATE = newCommonInstance("Interpolate"); public static final COSName INTERPOLATE = newCommonInstance("Interpolate");
public static final COSName IT = newCommonInstance("IT"); public static final COSName IT = newCommonInstance("IT");
public static final COSName ITALIC_ANGLE = newCommonInstance("ItalicAngle"); public static final COSName ITALIC_ANGLE = newCommonInstance("ItalicAngle");
public static final COSName ISSUER = newCommonInstance("Issuer");
public static final COSName IX = newCommonInstance("IX");
// J // J
public static final COSName JAVA_SCRIPT = newCommonInstance("JavaScript"); public static final COSName JAVA_SCRIPT = newCommonInstance("JavaScript");
public static final COSName JBIG2_DECODE = newCommonInstance("JBIG2Decode"); public static final COSName JBIG2_DECODE = newCommonInstance("JBIG2Decode");
...@@ -334,6 +341,7 @@ public final class COSName extends COSBase implements Comparable<COSName> ...@@ -334,6 +341,7 @@ public final class COSName extends COSBase implements Comparable<COSName>
public static final COSName MCID = newCommonInstance("MCID"); public static final COSName MCID = newCommonInstance("MCID");
public static final COSName MDP = newCommonInstance("MDP"); public static final COSName MDP = newCommonInstance("MDP");
public static final COSName MEDIA_BOX = newCommonInstance("MediaBox"); public static final COSName MEDIA_BOX = newCommonInstance("MediaBox");
public static final COSName MEASURE = new COSName("Measure");
public static final COSName METADATA = newCommonInstance("Metadata"); public static final COSName METADATA = newCommonInstance("Metadata");
public static final COSName MISSING_WIDTH = newCommonInstance("MissingWidth"); public static final COSName MISSING_WIDTH = newCommonInstance("MissingWidth");
public static final COSName MK = newCommonInstance("MK"); public static final COSName MK = newCommonInstance("MK");
...@@ -346,6 +354,7 @@ public final class COSName extends COSBase implements Comparable<COSName> ...@@ -346,6 +354,7 @@ public final class COSName extends COSBase implements Comparable<COSName>
public static final COSName NAME = newCommonInstance("Name"); public static final COSName NAME = newCommonInstance("Name");
public static final COSName NAMES = newCommonInstance("Names"); public static final COSName NAMES = newCommonInstance("Names");
public static final COSName NEED_APPEARANCES = newCommonInstance("NeedAppearances"); public static final COSName NEED_APPEARANCES = newCommonInstance("NeedAppearances");
public static final COSName NEW_WINDOW = new COSName("NewWindow");
public static final COSName NEXT = newCommonInstance("Next"); public static final COSName NEXT = newCommonInstance("Next");
public static final COSName NM = newCommonInstance("NM"); public static final COSName NM = newCommonInstance("NM");
public static final COSName NON_EFONT_NO_WARN = newCommonInstance("NonEFontNoWarn"); public static final COSName NON_EFONT_NO_WARN = newCommonInstance("NonEFontNoWarn");
...@@ -444,6 +453,7 @@ public final class COSName extends COSBase implements Comparable<COSName> ...@@ -444,6 +453,7 @@ public final class COSName extends COSBase implements Comparable<COSName>
// S // S
public static final COSName S = newCommonInstance("S"); public static final COSName S = newCommonInstance("S");
public static final COSName SA = newCommonInstance("SA"); public static final COSName SA = newCommonInstance("SA");
public static final COSName SATURATION = new COSName("Saturation");
public static final COSName SCREEN = newCommonInstance("Screen"); public static final COSName SCREEN = newCommonInstance("Screen");
public static final COSName SE = newCommonInstance("SE"); public static final COSName SE = newCommonInstance("SE");
public static final COSName SEPARATION = newCommonInstance("Separation"); public static final COSName SEPARATION = newCommonInstance("Separation");
...@@ -527,6 +537,8 @@ public final class COSName extends COSBase implements Comparable<COSName> ...@@ -527,6 +537,8 @@ public final class COSName extends COSBase implements Comparable<COSName>
public static final COSName VIEW_AREA = newCommonInstance("ViewArea"); public static final COSName VIEW_AREA = newCommonInstance("ViewArea");
public static final COSName VIEW_CLIP = newCommonInstance("ViewClip"); public static final COSName VIEW_CLIP = newCommonInstance("ViewClip");
public static final COSName VIEWER_PREFERENCES = newCommonInstance("ViewerPreferences"); public static final COSName VIEWER_PREFERENCES = newCommonInstance("ViewerPreferences");
public static final COSName VOLUME = new COSName("Volume");
public static final COSName VP = new COSName("VP");
// W // W
public static final COSName W = newCommonInstance("W"); public static final COSName W = newCommonInstance("W");
public static final COSName W2 = newCommonInstance("W2"); public static final COSName W2 = newCommonInstance("W2");
......
...@@ -401,7 +401,7 @@ public class COSStream extends COSDictionary implements Closeable, Encryptable ...@@ -401,7 +401,7 @@ public class COSStream extends COSDictionary implements Closeable, Encryptable
/** /**
* Sets the function to be used to encrypt this stream. * Sets the function to be used to encrypt this stream.
* *
* @param encrypted * @param encryptor
*/ */
public void setEncryptor(Function<InputStream, InputStream> encryptor) public void setEncryptor(Function<InputStream, InputStream> encryptor)
{ {
...@@ -471,28 +471,39 @@ public class COSStream extends COSDictionary implements Closeable, Encryptable ...@@ -471,28 +471,39 @@ public class COSStream extends COSDictionary implements Closeable, Encryptable
/** /**
* Adds Flate decode filter to the current filters list if possible * Adds Flate decode filter to the current filters list if possible
*
* @true if the FlateDecode filter has been added
*/ */
public void addCompression() throws IOException public boolean addCompression()
{ {
if (canCompress()) if (canCompress())
{ {
COSArray newFilters = new COSArray(COSName.FLATE_DECODE); try
COSBase filters = getFilters();
if (filters instanceof COSName)
{ {
newFilters.add(filters); COSArray newFilters = new COSArray(COSName.FLATE_DECODE);
setFilters(newFilters); COSBase filters = getFilters();
} if (filters instanceof COSName)
else if (filters instanceof COSArray) {
{ newFilters.add(filters);
newFilters.addAll((COSArray) filters); setFilters(newFilters);
setFilters(newFilters); }
else if (filters instanceof COSArray)
{
newFilters.addAll((COSArray) filters);
setFilters(newFilters);
}
else
{
setFilters(COSName.FLATE_DECODE);
}
return true;
} }
else catch (IOException e)
{ {
setFilters(COSName.FLATE_DECODE); LOG.warn("Unable to add FlateDecode filter to the stream", e);
} }
} }
return false;
} }
/** /**
......
...@@ -49,47 +49,57 @@ final class ASCIIHexFilter extends Filter ...@@ -49,47 +49,57 @@ final class ASCIIHexFilter extends Filter
}; };
@Override @Override
public DecodeResult decode(InputStream encoded, OutputStream decoded, public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters,
COSDictionary parameters, int index) throws IOException int index) throws IOException
{ {
// TODO iText and pdfjs both have similar impl which is different from what we have. Maybe we can replace this
// with the algorithm in pdfjs
int value, firstByte, secondByte; int value, firstByte, secondByte;
while ((firstByte = encoded.read()) != -1) try
{ {
// always after first char while ((firstByte = encoded.read()) != -1)
while (isWhitespace(firstByte))
{ {
firstByte = encoded.read(); // always after first char
} while (isWhitespace(firstByte))
if (firstByte == -1 || isEOD(firstByte)) {
{ firstByte = encoded.read();
break; }
} if (firstByte == -1 || isEOD(firstByte))
if (REVERSE_HEX[firstByte] == -1)
{
LOG.error("Invalid hex, int: " + firstByte + " char: " + (char)firstByte);
}
value = REVERSE_HEX[firstByte] * 16;
secondByte = encoded.read();
if (secondByte == -1 || isEOD(secondByte))
{
// second value behaves like 0 in case of EOD
decoded.write(value);
break;
}
if (secondByte >= 0)
{
if (REVERSE_HEX[secondByte] == -1)
{ {
LOG.error("Invalid hex, int: " + secondByte + " char: " + (char)secondByte); break;
} }
value += REVERSE_HEX[secondByte];
if (REVERSE_HEX[firstByte] == -1)
{
LOG.error("Invalid hex, int: " + firstByte + " char: " + (char) firstByte);
}
value = REVERSE_HEX[firstByte] * 16;
secondByte = encoded.read();
if (secondByte == -1 || isEOD(secondByte))
{
// second value behaves like 0 in case of EOD
decoded.write(value);
break;
}
if (secondByte >= 0)
{
if (REVERSE_HEX[secondByte] == -1)
{
LOG.error(
"Invalid hex, int: " + secondByte + " char: " + (char) secondByte);
}
value += REVERSE_HEX[secondByte];
}
decoded.write(value);
} }
decoded.write(value); decoded.flush();
return new DecodeResult(parameters);
}
catch (ArrayIndexOutOfBoundsException e)
{
throw new IOException("Illegal character in ASCIIHexFilter", e);
} }
decoded.flush();
return new DecodeResult(parameters);
} }
// whitespace // whitespace
......
...@@ -279,7 +279,7 @@ final class CCITTFaxDecoderStream extends FilterInputStream ...@@ -279,7 +279,7 @@ final class CCITTFaxDecoderStream extends FilterInputStream
{ {
if (optionByteAligned) if (optionByteAligned)
{ {
bufferPos = -1; // Skip remaining bits and fetch the next byte at row start resetBuffer();
} }
eof: while (true) eof: while (true)
{ {
...@@ -316,7 +316,7 @@ final class CCITTFaxDecoderStream extends FilterInputStream ...@@ -316,7 +316,7 @@ final class CCITTFaxDecoderStream extends FilterInputStream
{ {
if (optionByteAligned) if (optionByteAligned)
{ {
bufferPos = -1; // Skip remaining bits and fetch the next byte at row start resetBuffer();
} }
decode2D(); decode2D();
} }
...@@ -421,30 +421,14 @@ final class CCITTFaxDecoderStream extends FilterInputStream ...@@ -421,30 +421,14 @@ final class CCITTFaxDecoderStream extends FilterInputStream
{ {
return total; return total;
} }
else n = tree.root;
{
n = tree.root;
}
} }
} }
} }
private void resetBuffer() throws IOException private void resetBuffer()
{ {
for (int i = 0; i < decodedRow.length; i++) bufferPos = -1;
{
decodedRow[i] = 0;
}
while (true)
{
if (bufferPos == -1)
{
return;
}
readBit();
}
} }
int buffer = -1; int buffer = -1;
...@@ -688,9 +672,8 @@ final class CCITTFaxDecoderStream extends FilterInputStream ...@@ -688,9 +672,8 @@ final class CCITTFaxDecoderStream extends FilterInputStream
} }
} }
static final short[][] BLACK_CODES = { static final short[][] BLACK_CODES = { { // 2 bits
{ // 2 bits 0x2, 0x3, },
0x2, 0x3, },
{ // 3 bits { // 3 bits
0x2, 0x3, }, 0x2, 0x3, },
{ // 4 bits { // 4 bits
...@@ -718,9 +701,8 @@ final class CCITTFaxDecoderStream extends FilterInputStream ...@@ -718,9 +701,8 @@ final class CCITTFaxDecoderStream extends FilterInputStream
{ // 13 bits { // 13 bits
0x4a, 0x4b, 0x4c, 0x4d, 0x52, 0x53, 0x54, 0x55, 0x5a, 0x5b, 0x64, 0x65, 0x6c, 0x4a, 0x4b, 0x4c, 0x4d, 0x52, 0x53, 0x54, 0x55, 0x5a, 0x5b, 0x64, 0x65, 0x6c,
0x6d, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, } }; 0x6d, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, } };
static final short[][] BLACK_RUN_LENGTHS = { static final short[][] BLACK_RUN_LENGTHS = { { // 2 bits
{ // 2 bits 3, 2, },
3, 2, },
{ // 3 bits { // 3 bits
1, 4, }, 1, 4, },
{ // 4 bits { // 4 bits
...@@ -748,9 +730,8 @@ final class CCITTFaxDecoderStream extends FilterInputStream ...@@ -748,9 +730,8 @@ final class CCITTFaxDecoderStream extends FilterInputStream
640, 704, 768, 832, 1280, 1344, 1408, 1472, 1536, 1600, 1664, 1728, 512, 576, 640, 704, 768, 832, 1280, 1344, 1408, 1472, 1536, 1600, 1664, 1728, 512, 576,
896, 960, 1024, 1088, 1152, 1216, } }; 896, 960, 1024, 1088, 1152, 1216, } };
public static final short[][] WHITE_CODES = { public static final short[][] WHITE_CODES = { { // 4 bits
{ // 4 bits 0x7, 0x8, 0xb, 0xc, 0xe, 0xf, },
0x7, 0x8, 0xb, 0xc, 0xe, 0xf, },
{ // 5 bits { // 5 bits
0x12, 0x13, 0x14, 0x1b, 0x7, 0x8, }, 0x12, 0x13, 0x14, 0x1b, 0x7, 0x8, },
{ // 6 bits { // 6 bits
...@@ -771,9 +752,8 @@ final class CCITTFaxDecoderStream extends FilterInputStream ...@@ -771,9 +752,8 @@ final class CCITTFaxDecoderStream extends FilterInputStream
{ // 12 bits { // 12 bits
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x1c, 0x1d, 0x1e, 0x1f, } }; 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x1c, 0x1d, 0x1e, 0x1f, } };
public static final short[][] WHITE_RUN_LENGTHS = { public static final short[][] WHITE_RUN_LENGTHS = { { // 4 bits
{ // 4 bits 2, 3, 4, 5, 6, 7, },
2, 3, 4, 5, 6, 7, },
{ // 5 bits { // 5 bits
128, 8, 9, 64, 10, 11, }, 128, 8, 9, 64, 10, 11, },
{ // 6 bits { // 6 bits
......
...@@ -38,9 +38,6 @@ final class CCITTFaxFilter extends Filter ...@@ -38,9 +38,6 @@ final class CCITTFaxFilter extends Filter
public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters, public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters,
int index) throws IOException int index) throws IOException
{ {
DecodeResult result = new DecodeResult(new COSDictionary());
result.getParameters().addAll(parameters);
// get decode parameters // get decode parameters
COSDictionary decodeParms = getDecodeParams(parameters, index); COSDictionary decodeParms = getDecodeParams(parameters, index);
...@@ -103,12 +100,6 @@ final class CCITTFaxFilter extends Filter ...@@ -103,12 +100,6 @@ final class CCITTFaxFilter extends Filter
invertBitmap(decompressed); invertBitmap(decompressed);
} }
// repair missing color space
if (!parameters.containsKey(COSName.COLORSPACE))
{
result.getParameters().setItem(COSName.COLORSPACE, COSName.DEVICEGRAY);
}
decoded.write(decompressed); decoded.write(decompressed);
return new DecodeResult(parameters); return new DecodeResult(parameters);
} }
......
...@@ -134,8 +134,7 @@ final class DCTFilter extends Filter ...@@ -134,8 +134,7 @@ final class DCTFilter extends Filter
// already CMYK // already CMYK
break; break;
case 1: case 1:
// TODO YCbCr raster = fromYCbCrtoCMYK(raster);
LOG.warn("YCbCr JPEGs not implemented");
break; break;
case 2: case 2:
raster = fromYCCKtoCMYK(raster); raster = fromYCCKtoCMYK(raster);
...@@ -268,6 +267,44 @@ final class DCTFilter extends Filter ...@@ -268,6 +267,44 @@ final class DCTFilter extends Filter
return writableRaster; return writableRaster;
} }
private WritableRaster fromYCbCrtoCMYK(Raster raster)
{
WritableRaster writableRaster = raster.createCompatibleWritableRaster();
int[] value = new int[4];
for (int y = 0, height = raster.getHeight(); y < height; y++)
{
for (int x = 0, width = raster.getWidth(); x < width; x++)
{
raster.getPixel(x, y, value);
// 4-channels 0..255
float Y = value[0];
float Cb = value[1];
float Cr = value[2];
float K = value[3];
// YCbCr to RGB, see http://www.equasys.de/colorconversion.html
int r = clamp( (1.164f * (Y-16)) + (1.596f * (Cr - 128)) );
int g = clamp( (1.164f * (Y-16)) + (-0.392f * (Cb-128)) + (-0.813f * (Cr-128)));
int b = clamp( (1.164f * (Y-16)) + (2.017f * (Cb-128)));
// naive RGB to CMYK
int cyan = 255 - r;
int magenta = 255 - g;
int yellow = 255 - b;
// update new raster
value[0] = cyan;
value[1] = magenta;
value[2] = yellow;
value[3] = (int)K;
writableRaster.setPixel(x, y, value);
}
}
return writableRaster;
}
// converts from BGR to RGB // converts from BGR to RGB
private WritableRaster fromBGRtoRGB(Raster raster) private WritableRaster fromBGRtoRGB(Raster raster)
{ {
......
...@@ -112,7 +112,7 @@ public abstract class Filter ...@@ -112,7 +112,7 @@ public abstract class Filter
return new COSDictionary(); return new COSDictionary();
} }
} }
if (!(dp instanceof COSArray || dp instanceof COSArray)) if (!(filter instanceof COSArray || dp instanceof COSArray))
{ {
LOG.error("Ignoring invalid DecodeParams. Expected array or dictionary but found {}", LOG.error("Ignoring invalid DecodeParams. Expected array or dictionary but found {}",
dp.getClass().getName()); dp.getClass().getName());
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
*/ */
package org.sejda.sambox.filter; package org.sejda.sambox.filter;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
...@@ -25,9 +24,7 @@ import java.util.zip.Deflater; ...@@ -25,9 +24,7 @@ import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream; import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater; import java.util.zip.Inflater;
import org.sejda.io.FastByteArrayOutputStream;
import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSDictionary;
import org.sejda.sambox.cos.COSName;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -40,39 +37,17 @@ import org.slf4j.LoggerFactory; ...@@ -40,39 +37,17 @@ import org.slf4j.LoggerFactory;
final class FlateFilter extends Filter final class FlateFilter extends Filter
{ {
private static final Logger LOG = LoggerFactory.getLogger(FlateFilter.class); private static final Logger LOG = LoggerFactory.getLogger(FlateFilter.class);
private static final int BUFFER_SIZE = 16348; private static final int BUFFER_SIZE = 0x4000;
@Override @Override
public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters, public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters,
int index) throws IOException int index) throws IOException
{ {
int predictor = -1;
final COSDictionary decodeParams = getDecodeParams(parameters, index); final COSDictionary decodeParams = getDecodeParams(parameters, index);
if (decodeParams != null)
{
predictor = decodeParams.getInt(COSName.PREDICTOR);
}
try try
{ {
if (predictor > 1) decompress(encoded, Predictor.wrapPredictor(decoded, decodeParams));
{
int colors = Math.min(decodeParams.getInt(COSName.COLORS, 1), 32);
int bitsPerPixel = decodeParams.getInt(COSName.BITS_PER_COMPONENT, 8);
int columns = decodeParams.getInt(COSName.COLUMNS, 1);
FastByteArrayOutputStream baos = new FastByteArrayOutputStream();
decompress(encoded, baos);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
Predictor.decodePredictor(predictor, colors, bitsPerPixel, columns, bais, decoded);
decoded.flush();
baos.reset();
bais.reset();
}
else
{
decompress(encoded, decoded);
}
} }
catch (DataFormatException e) catch (DataFormatException e)
{ {
...@@ -154,18 +129,19 @@ final class FlateFilter extends Filter ...@@ -154,18 +129,19 @@ final class FlateFilter extends Filter
} }
compressionLevel = Math.max(-1, Math.min(Deflater.BEST_COMPRESSION, compressionLevel)); compressionLevel = Math.max(-1, Math.min(Deflater.BEST_COMPRESSION, compressionLevel));
Deflater deflater = new Deflater(compressionLevel); Deflater deflater = new Deflater(compressionLevel);
DeflaterOutputStream out = new DeflaterOutputStream(encoded, deflater); try (DeflaterOutputStream out = new DeflaterOutputStream(encoded, deflater))
int amountRead;
int mayRead = input.available();
if (mayRead > 0)
{ {
byte[] buffer = new byte[Math.min(mayRead, BUFFER_SIZE)]; int amountRead;
while ((amountRead = input.read(buffer, 0, Math.min(mayRead, BUFFER_SIZE))) != -1) int mayRead = input.available();
if (mayRead > 0)
{ {
out.write(buffer, 0, amountRead); byte[] buffer = new byte[Math.min(mayRead, BUFFER_SIZE)];
while ((amountRead = input.read(buffer, 0, Math.min(mayRead, BUFFER_SIZE))) != -1)
{
out.write(buffer, 0, amountRead);
}
} }
} }
out.close();
encoded.flush(); encoded.flush();
} }
} }
...@@ -20,6 +20,7 @@ import java.io.IOException; ...@@ -20,6 +20,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSDictionary;
/** /**
...@@ -30,19 +31,12 @@ import org.sejda.sambox.cos.COSDictionary; ...@@ -30,19 +31,12 @@ import org.sejda.sambox.cos.COSDictionary;
*/ */
final class IdentityFilter extends Filter final class IdentityFilter extends Filter
{ {
private static final int BUFFER_SIZE = 1024;
@Override @Override
public DecodeResult decode(InputStream encoded, OutputStream decoded, public DecodeResult decode(InputStream encoded, OutputStream decoded,
COSDictionary parameters, int index) COSDictionary parameters, int index)
throws IOException throws IOException
{ {
byte[] buffer = new byte[BUFFER_SIZE]; IOUtils.copy(encoded, decoded);
int amountRead;
while((amountRead = encoded.read(buffer, 0, BUFFER_SIZE)) != -1)
{
decoded.write(buffer, 0, amountRead);
}
decoded.flush(); decoded.flush();
return new DecodeResult(parameters); return new DecodeResult(parameters);
} }
...@@ -51,12 +45,7 @@ final class IdentityFilter extends Filter ...@@ -51,12 +45,7 @@ final class IdentityFilter extends Filter
public void encode(InputStream input, OutputStream encoded, COSDictionary parameters) public void encode(InputStream input, OutputStream encoded, COSDictionary parameters)
throws IOException throws IOException
{ {
byte[] buffer = new byte[BUFFER_SIZE]; IOUtils.copy(input, encoded);
int amountRead;
while((amountRead = input.read(buffer, 0, BUFFER_SIZE)) != -1)
{
encoded.write(buffer, 0, amountRead);
}
encoded.flush(); encoded.flush();
} }
} }
...@@ -40,8 +40,7 @@ import org.slf4j.LoggerFactory; ...@@ -40,8 +40,7 @@ import org.slf4j.LoggerFactory;
* monochrome (1 bit per pixel) image data (or an approximation of that data). * monochrome (1 bit per pixel) image data (or an approximation of that data).
* *
* Requires a JBIG2 plugin for Java Image I/O to be installed. A known working * Requires a JBIG2 plugin for Java Image I/O to be installed. A known working
* plug-in is <a href="http://code.google.com/p/jbig2-imageio/">jbig2-imageio</a> * plug-in is the Apache PDFBox JBIG2 plugin.
* which is available under the GPL v3 license.
* *
* @author Timo Boehme * @author Timo Boehme
*/ */
...@@ -49,11 +48,29 @@ final class JBIG2Filter extends Filter ...@@ -49,11 +48,29 @@ final class JBIG2Filter extends Filter
{ {
private static final Logger LOG = LoggerFactory.getLogger(JBIG2Filter.class); private static final Logger LOG = LoggerFactory.getLogger(JBIG2Filter.class);
private static boolean levigoLogged = false;
private static synchronized void logLevigoDonated()
{
if (!levigoLogged)
{
LOG.info("The Levigo JBIG2 plugin has been donated to the Apache Foundation");
LOG.info("and an improved version is available for download at "
+ "https://pdfbox.apache.org/download.cgi");
levigoLogged = true;
}
}
@Override @Override
public DecodeResult decode(InputStream encoded, OutputStream decoded, public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary
COSDictionary parameters, int index) throws IOException parameters, int index) throws IOException
{ {
ImageReader reader = findImageReader("JBIG2", "jbig2-imageio is not installed"); ImageReader reader = findImageReader("JBIG2", "jbig2-imageio is not installed");
if (reader.getClass().getName().contains("levigo"))
{
logLevigoDonated();
}
DecodeResult result = new DecodeResult(new COSDictionary()); DecodeResult result = new DecodeResult(new COSDictionary());
result.getParameters().addAll(parameters); result.getParameters().addAll(parameters);
...@@ -127,12 +144,6 @@ final class JBIG2Filter extends Filter ...@@ -127,12 +144,6 @@ final class JBIG2Filter extends Filter
reader.dispose(); reader.dispose();
} }
// repair missing color space
if (!parameters.containsKey(COSName.COLORSPACE))
{
result.getParameters().setName(COSName.COLORSPACE, COSName.DEVICEGRAY.getName());
}
return new DecodeResult(parameters); return new DecodeResult(parameters);
} }
......
...@@ -25,9 +25,9 @@ import java.io.IOException; ...@@ -25,9 +25,9 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader; import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSDictionary;
import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSName;
...@@ -86,7 +86,9 @@ public final class JPXFilter extends Filter ...@@ -86,7 +86,9 @@ public final class JPXFilter extends Filter
ImageInputStream iis = null; ImageInputStream iis = null;
try try
{ {
iis = ImageIO.createImageInputStream(input); // PDFBOX-4121: ImageIO.createImageInputStream() is much slower
iis = new MemoryCacheImageInputStream(input);
reader.setInput(iis, true, true); reader.setInput(iis, true, true);
BufferedImage image; BufferedImage image;
...@@ -116,8 +118,8 @@ public final class JPXFilter extends Filter ...@@ -116,8 +118,8 @@ public final class JPXFilter extends Filter
} }
// override dimensions, see PDFBOX-1735 // override dimensions, see PDFBOX-1735
parameters.setInt(COSName.WIDTH, image.getWidth()); parameters.setInt(COSName.WIDTH, reader.getWidth(0));
parameters.setInt(COSName.HEIGHT, image.getHeight()); parameters.setInt(COSName.HEIGHT, reader.getHeight(0));
// extract embedded color space // extract embedded color space
if (!parameters.containsKey(COSName.COLORSPACE)) if (!parameters.containsKey(COSName.COLORSPACE))
......
...@@ -15,8 +15,6 @@ ...@@ -15,8 +15,6 @@
*/ */
package org.sejda.sambox.filter; package org.sejda.sambox.filter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
...@@ -53,60 +51,39 @@ public class LZWFilter extends Filter ...@@ -53,60 +51,39 @@ public class LZWFilter extends Filter
* The LZW end of data code. * The LZW end of data code.
*/ */
public static final long EOD = 257; public static final long EOD = 257;
//BEWARE: codeTable must be local to each method, because there is only // BEWARE: codeTable must be local to each method, because there is only
// one instance of each filter // one instance of each filter
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public DecodeResult decode(InputStream encoded, OutputStream decoded, public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters,
COSDictionary parameters, int index) throws IOException int index) throws IOException
{ {
int predictor = -1;
int earlyChange = 1;
COSDictionary decodeParams = getDecodeParams(parameters, index); COSDictionary decodeParams = getDecodeParams(parameters, index);
if (decodeParams != null) int earlyChange = decodeParams.getInt(COSName.EARLY_CHANGE, 1);
{
predictor = decodeParams.getInt(COSName.PREDICTOR); if (earlyChange != 0 && earlyChange != 1)
earlyChange = decodeParams.getInt(COSName.EARLY_CHANGE, 1);
if (earlyChange != 0 && earlyChange != 1)
{
earlyChange = 1;
}
}
if (predictor > 1)
{
@SuppressWarnings("null")
int colors = Math.min(decodeParams.getInt(COSName.COLORS, 1), 32);
int bitsPerPixel = decodeParams.getInt(COSName.BITS_PER_COMPONENT, 8);
int columns = decodeParams.getInt(COSName.COLUMNS, 1);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
doLZWDecode(encoded, baos, earlyChange);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
Predictor.decodePredictor(predictor, colors, bitsPerPixel, columns, bais, decoded);
decoded.flush();
baos.reset();
bais.reset();
}
else
{ {
doLZWDecode(encoded, decoded, earlyChange); doLZWDecode(encoded, decoded, earlyChange);
} }
doLZWDecode(encoded, Predictor.wrapPredictor(decoded, decodeParams), earlyChange);
return new DecodeResult(parameters); return new DecodeResult(parameters);
} }
private void doLZWDecode(InputStream encoded, OutputStream decoded, int earlyChange) throws IOException private void doLZWDecode(InputStream encoded, OutputStream decoded, int earlyChange)
throws IOException
{ {
List<byte[]> codeTable = new ArrayList<byte[]>(); List<byte[]> codeTable = new ArrayList<>();
int chunk = 9; int chunk = 9;
final MemoryCacheImageInputStream in = new MemoryCacheImageInputStream(encoded);
long nextCommand; long nextCommand;
long prevCommand = -1; long prevCommand = -1;
try try (MemoryCacheImageInputStream in = new MemoryCacheImageInputStream(encoded))
{ {
while ((nextCommand = in.readBits(chunk)) != EOD) while ((nextCommand = in.readBits(chunk)) != EOD)
{ {
...@@ -141,7 +118,7 @@ public class LZWFilter extends Filter ...@@ -141,7 +118,7 @@ public class LZWFilter extends Filter
decoded.write(newData); decoded.write(newData);
codeTable.add(newData); codeTable.add(newData);
} }
chunk = calculateChunk(codeTable.size(), earlyChange); chunk = calculateChunk(codeTable.size(), earlyChange);
prevCommand = nextCommand; prevCommand = nextCommand;
} }
...@@ -154,8 +131,8 @@ public class LZWFilter extends Filter ...@@ -154,8 +131,8 @@ public class LZWFilter extends Filter
decoded.flush(); decoded.flush();
} }
private void checkIndexBounds(List<byte[]> codeTable, long index, MemoryCacheImageInputStream in) private static void checkIndexBounds(List<byte[]> codeTable, long index,
throws IOException MemoryCacheImageInputStream in) throws IOException
{ {
if (index < 0) if (index < 0)
{ {
...@@ -177,68 +154,69 @@ public class LZWFilter extends Filter ...@@ -177,68 +154,69 @@ public class LZWFilter extends Filter
int chunk = 9; int chunk = 9;
byte[] inputPattern = null; byte[] inputPattern = null;
final MemoryCacheImageOutputStream out = new MemoryCacheImageOutputStream(encoded); try (MemoryCacheImageOutputStream out = new MemoryCacheImageOutputStream(encoded))
out.writeBits(CLEAR_TABLE, chunk);
int foundCode = -1;
int r;
while ((r = rawData.read()) != -1)
{ {
byte by = (byte) r; out.writeBits(CLEAR_TABLE, chunk);
if (inputPattern == null) int foundCode = -1;
int r;
while ((r = rawData.read()) != -1)
{ {
inputPattern = new byte[] { by }; byte by = (byte) r;
foundCode = by & 0xff; if (inputPattern == null)
}
else
{
inputPattern = Arrays.copyOf(inputPattern, inputPattern.length + 1);
inputPattern[inputPattern.length - 1] = by;
int newFoundCode = findPatternCode(codeTable, inputPattern);
if (newFoundCode == -1)
{ {
// use previous
chunk = calculateChunk(codeTable.size() - 1, 1);
out.writeBits(foundCode, chunk);
// create new table entry
codeTable.add(inputPattern);
if (codeTable.size() == 4096)
{
// code table is full
out.writeBits(CLEAR_TABLE, chunk);
codeTable = createCodeTable();
}
inputPattern = new byte[] { by }; inputPattern = new byte[] { by };
foundCode = by & 0xff; foundCode = by & 0xff;
} }
else else
{ {
foundCode = newFoundCode; inputPattern = Arrays.copyOf(inputPattern, inputPattern.length + 1);
inputPattern[inputPattern.length - 1] = by;
int newFoundCode = findPatternCode(codeTable, inputPattern);
if (newFoundCode == -1)
{
// use previous
chunk = calculateChunk(codeTable.size() - 1, 1);
out.writeBits(foundCode, chunk);
// create new table entry
codeTable.add(inputPattern);
if (codeTable.size() == 4096)
{
// code table is full
out.writeBits(CLEAR_TABLE, chunk);
codeTable = createCodeTable();
}
inputPattern = new byte[] { by };
foundCode = by & 0xff;
}
else
{
foundCode = newFoundCode;
}
} }
} }
} if (foundCode != -1)
if (foundCode != -1) {
{ chunk = calculateChunk(codeTable.size() - 1, 1);
chunk = calculateChunk(codeTable.size() - 1, 1); out.writeBits(foundCode, chunk);
out.writeBits(foundCode, chunk); }
}
// PPDFBOX-1977: the decoder wouldn't know that the encoder would output
// an EOD as code, so he would have increased his own code table and
// possibly adjusted the chunk. Therefore, the encoder must behave as
// if the code table had just grown and thus it must be checked it is
// needed to adjust the chunk, based on an increased table size parameter
chunk = calculateChunk(codeTable.size(), 1);
// PPDFBOX-1977: the decoder wouldn't know that the encoder would output out.writeBits(EOD, chunk);
// an EOD as code, so he would have increased his own code table and
// possibly adjusted the chunk. Therefore, the encoder must behave as
// if the code table had just grown and thus it must be checked it is
// needed to adjust the chunk, based on an increased table size parameter
chunk = calculateChunk(codeTable.size(), 1);
out.writeBits(EOD, chunk); // pad with 0
out.writeBits(0, 7);
// pad with 0
out.writeBits(0, 7); // must do or file will be empty :-(
out.flush();
// must do or file will be empty :-( }
out.flush();
out.close();
} }
/** /**
...@@ -246,8 +224,7 @@ public class LZWFilter extends Filter ...@@ -246,8 +224,7 @@ public class LZWFilter extends Filter
* *
* @param codeTable The LZW code table. * @param codeTable The LZW code table.
* @param pattern The pattern to be searched for. * @param pattern The pattern to be searched for.
* @return The index of the longest matching pattern or -1 if nothing is * @return The index of the longest matching pattern or -1 if nothing is found.
* found.
*/ */
private int findPatternCode(List<byte[]> codeTable, byte[] pattern) private int findPatternCode(List<byte[]> codeTable, byte[] pattern)
{ {
...@@ -261,7 +238,7 @@ public class LZWFilter extends Filter ...@@ -261,7 +238,7 @@ public class LZWFilter extends Filter
if (foundCode != -1) if (foundCode != -1)
{ {
// we already found pattern with size > 1 // we already found pattern with size > 1
return foundCode; return foundCode;
} }
else if (pattern.length > 1) else if (pattern.length > 1)
{ {
...@@ -270,7 +247,8 @@ public class LZWFilter extends Filter ...@@ -270,7 +247,8 @@ public class LZWFilter extends Filter
} }
} }
byte[] tryPattern = codeTable.get(i); byte[] tryPattern = codeTable.get(i);
if ((foundCode != -1 || tryPattern.length > foundLen) && Arrays.equals(tryPattern, pattern)) if ((foundCode != -1 || tryPattern.length > foundLen)
&& Arrays.equals(tryPattern, pattern))
{ {
foundCode = i; foundCode = i;
foundLen = tryPattern.length; foundLen = tryPattern.length;
...@@ -280,12 +258,11 @@ public class LZWFilter extends Filter ...@@ -280,12 +258,11 @@ public class LZWFilter extends Filter
} }
/** /**
* Init the code table with 1 byte entries and the EOD and CLEAR_TABLE * Init the code table with 1 byte entries and the EOD and CLEAR_TABLE markers.
* markers.
*/ */
private List<byte[]> createCodeTable() private List<byte[]> createCodeTable()
{ {
List<byte[]> codeTable = new ArrayList<byte[]>(4096); List<byte[]> codeTable = new ArrayList<>(4096);
for (int i = 0; i < 256; ++i) for (int i = 0; i < 256; ++i)
{ {
codeTable.add(new byte[] { (byte) (i & 0xFF) }); codeTable.add(new byte[] { (byte) (i & 0xFF) });
......
This diff is collapsed.
...@@ -47,9 +47,14 @@ final class RunLengthDecodeFilter extends Filter ...@@ -47,9 +47,14 @@ final class RunLengthDecodeFilter extends Filter
{ {
int amountToCopy = dupAmount + 1; int amountToCopy = dupAmount + 1;
int compressedRead; int compressedRead;
while(amountToCopy > 0) while (amountToCopy > 0)
{ {
compressedRead = encoded.read(buffer, 0, amountToCopy); compressedRead = encoded.read(buffer, 0, amountToCopy);
// EOF reached?
if (compressedRead == -1)
{
break;
}
decoded.write(buffer, 0, compressedRead); decoded.write(buffer, 0, compressedRead);
amountToCopy -= compressedRead; amountToCopy -= compressedRead;
} }
...@@ -57,6 +62,11 @@ final class RunLengthDecodeFilter extends Filter ...@@ -57,6 +62,11 @@ final class RunLengthDecodeFilter extends Filter
else else
{ {
int dupByte = encoded.read(); int dupByte = encoded.read();
// EOF reached?
if (dupByte == -1)
{
break;
}
for (int i = 0; i < 257 - dupAmount; i++) for (int i = 0; i < 257 - dupAmount; i++)
{ {
decoded.write(dupByte); decoded.write(dupByte);
......
...@@ -120,10 +120,12 @@ abstract class AbstractXrefTableParser ...@@ -120,10 +120,12 @@ abstract class AbstractXrefTableParser
onEntryFound(inUseEntry(currentObjectNumber, Long.parseLong(splitString[0]), onEntryFound(inUseEntry(currentObjectNumber, Long.parseLong(splitString[0]),
Integer.parseInt(splitString[1]))); Integer.parseInt(splitString[1])));
} }
catch (NumberFormatException e) catch (IllegalArgumentException e)
{ {
throw new IOException("Corrupted xref table entry.", e); throw new IOException(
"Corrupted xref table entry. Invalid xref line: " + currentLine, e);
} }
} }
else if (!"f".equals(entryType)) else if (!"f".equals(entryType))
{ {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment