Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions pdfbox/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,19 @@
<sha512>cd6d92c643108cdcf2b4c6a4ae4cc5a15d848e3335f6c8a135a5438d691eb982d11cfd8f26d11839321e06a364126d9b0c9732b4a19785821b844fe0ec94d525</sha512>
</configuration>
</execution>
<execution>
<id>PDFBOX-5876</id>
<phase>generate-test-resources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>https://issues.apache.org/jira/secure/attachment/13071244/jpeg2000.pdf</url>
<outputDirectory>${project.build.directory}/pdfs</outputDirectory>
<outputFileName>PDFBOX-5876-jpeg2000.pdf</outputFileName>
<sha512>5eb020282f3998c7673983625303fb7634a3ca2a2fc65efc7bd123241a7facae999610bbd38d8a6be8fc26752c7241d806d46252448ba600e2beb118f4567cf3</sha512>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ public final class PDImageXObject extends PDXObject implements PDImage
private boolean jpxValuesInitialized = false;
private BufferedImage jpxSMask = null;

// PDFBOX-5876: upper bound for the subsampling used by initJPXValues method.
private static final int JPX_METADATA_SUBSAMPLING = 8;

/**
* current resource dictionary (has color spaces)
*/
Expand Down Expand Up @@ -739,7 +742,9 @@ private void initJPXValues()
// bits per component
// the colorspace of the image is used if the dictionary doesn't provide any value
PDStream stream = getStream();
try (COSInputStream is = stream.createInputStream())
// PDFBOX-5876: bound the subsampling of this metadata-only read, see field javadoc.
DecodeOptions options = new DecodeOptions(JPX_METADATA_SUBSAMPLING);
try (COSInputStream is = stream.createInputStream(options))
{
DecodeResult decodeResult = is.getDecodeResult();
stream.getCOSObject().addAll(decodeResult.getParameters());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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.rendering;

import java.io.File;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.io.IOUtils;
import org.apache.pdfbox.pdmodel.PDDocument;

/**
* Renders the first page of a PDF at half scale, the same way as reported in PDFBOX-5876. Run in
* its own JVM with a constrained heap by {@link TestQuality#testPDFBox5876()}, since the heap size
* of the JVM already running the test suite can't be changed after the fact.
*/
public final class JPXLowMemoryRenderMain
{
private JPXLowMemoryRenderMain()
{
}

public static void main(String[] args) throws Exception
{
File file = new File(args[0]);
try (PDDocument doc = Loader.loadPDF(file, IOUtils.createTempFileOnlyStreamCache()))
{
PDFRenderer renderer = new PDFRenderer(doc);
renderer.setSubsamplingAllowed(true);
renderer.renderImage(0, 0.5f);
}
}
}
32 changes: 32 additions & 0 deletions pdfbox/src/test/java/org/apache/pdfbox/rendering/TestQuality.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
Expand Down Expand Up @@ -126,4 +128,34 @@ void testPDFBox5403() throws IOException
"expected a dark text pixel but was too light: " + Integer.toHexString(rgb));
}
}

/**
* PDFBOX-5876: rendering a page containing a very large JPEG 2000 (JPX) image at reduced
* scale must not decode the image at full resolution first just to read its width, height
* and color space. Before the fix, {@code PDImageXObject.initJPXValues()} did exactly that,
* on top of the properly subsampled decode done afterwards for the actual rendering, so
* memory usage was driven by the full image size regardless of how small the rendered output
* was. This must run in a separate, heap-constrained JVM, since the heap size of the JVM
* already running the test suite can't be changed after the fact, and the failure (an
* OutOfMemoryError) only reproduces below a certain heap size.
*
* @throws IOException
* @throws InterruptedException
*/
@Test
void testPDFBox5876() throws IOException, InterruptedException
{
File file = new File(TARGET_PDF_DIR, "PDFBOX-5876-jpeg2000.pdf");
String javaBin = System.getProperty("java.home") + File.separator + "bin" +
File.separator + "java";
ProcessBuilder builder = new ProcessBuilder(javaBin, "-Xmx600m",
"-cp", System.getProperty("java.class.path"),
JPXLowMemoryRenderMain.class.getName(), file.getAbsolutePath());
builder.redirectErrorStream(true);
Process process = builder.start();
String output = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
boolean finished = process.waitFor(120, TimeUnit.SECONDS);
Assertions.assertTrue(finished, "subprocess timed out");
Assertions.assertEquals(0, process.exitValue(), "subprocess failed:\n" + output);
}
}