在PDFBox中替换具有相同资源的图像

时间:2015-06-23 17:50:20

标签: java pdfbox

我有一张包含2张空白图片的pdf。我需要使用PDFBox将这两个图像替换为2个单独的图像。问题是,两个空白图像似乎都具有相同的资源。因此,如果我替换一个,另一个也替换为相同的图像。

我按照this示例尝试重写processOperator()方法并替换基于imageHeight的图像。但是,它仍然最终用相同的图像替换两个图像。到目前为止,这是我的代码:

protected void processOperator( PDFOperator operator, List arguments ) throws IOException
    {
        String operation = operator.getOperation();
        if( INVOKE_OPERATOR.equals(operation) )
        {
            COSName objectName = (COSName)arguments.get( 0 );
            Map<String, PDXObject> xobjects = getResources().getXObjects();
            PDXObject xobject = (PDXObject)xobjects.get( objectName.getName() );
            if( xobject instanceof PDXObjectImage )
            {
                PDXObjectImage blankImage = (PDXObjectImage)xobject;
                int imageWidth = blankImage.getWidth();
                int imageHeight = blankImage.getHeight();

                System.out.println("Image width >>> "+imageWidth+" height >>>> "+imageHeight);

                // Check if it is blank image 1 based on height
                if(imageHeight < 480){
                    File logo = new File("abc.jpg");
                    BufferedImage bufferedImage = ImageIO.read(logo);
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    ImageIO.write( bufferedImage, "jpg", baos );
                    baos.flush();
                    byte[] logoImageInBytes = baos.toByteArray();
                    baos.close();

                    // label will be used to replace the blank image
                    label = logoImageInBytes;
                }

                BufferedImage img = ImageIO.read(new ByteArrayInputStream(label));

                BufferedImage resizedImage = Scalr.resize(img, Scalr.Method.BALANCED, Scalr.Mode.FIT_EXACT, img.getWidth(), img.getHeight());
                ByteArrayOutputStream baos = new ByteArrayOutputStream();                       
                ImageIO.write(resizedImage, "jpg", baos);                       

                // Replace empty image in template with the image generated from shipping label byte array
                PDXObjectImage validImage = new PDJpeg(doc, new ByteArrayInputStream(baos.toByteArray()));
                blankImage.getCOSStream().replaceWithStream(validImage.getCOSStream());
            }

现在,当我删除检查if(imageHeight&lt; 480)的if块时,它会将imageHeight打印为30和470以显示空白图像。但是,当我添加if块时,它会将imageHeight打印为480和1500,并且永远不会进入if块内部,因为这两个空白图像最终都会被同一图像替换。

这里发生了什么?我是PDFBox的新手,所以我不确定我的代码是否正确。

2 个答案:

答案 0 :(得分:1)

在首次考虑通过新图像实际替换现有图像的通用方法时,我同意@TilmanHausherr认为更简单的解决方案是简单地添加一个额外的内容流,其中包含两个大小的图像/您需要的位置 覆盖现有的图片。

这种方法比实际替换更容易实现(甚至一般)并且更不容易出错。

在通用解决方案中,我们事先没有图像位置。为了确定它们,我们可以使用这个帮助器类(基本上是对PDFBox示例PrintImageLocations的剽窃):

public class ImageLocator extends PDFStreamEngine
{
    private static final String INVOKE_OPERATOR = "Do";

    public ImageLocator() throws IOException
    {
        super(ResourceLoader.loadProperties("org/apache/pdfbox/resources/PDFTextStripper.properties", true));
    }

    public List<ImageLocation> getLocations()
    {
        return new ArrayList<ImageLocation>(locations);
    }

    protected void processOperator(PDFOperator operator, List<COSBase> arguments) throws IOException
    {
        String operation = operator.getOperation();
        if (INVOKE_OPERATOR.equals(operation))
        {
            COSName objectName = (COSName) arguments.get(0);
            Map<String, PDXObject> xobjects = getResources().getXObjects();
            PDXObject xobject = (PDXObject) xobjects.get(objectName.getName());
            if (xobject instanceof PDXObjectImage)
            {
                PDXObjectImage image = (PDXObjectImage) xobject;
                PDPage page = getCurrentPage();
                Matrix matrix = getGraphicsState().getCurrentTransformationMatrix();

                locations.add(new ImageLocation(page, matrix, image));
            }
            else if (xobject instanceof PDXObjectForm)
            {
                // save the graphics state
                getGraphicsStack().push((PDGraphicsState) getGraphicsState().clone());
                PDPage page = getCurrentPage();

                PDXObjectForm form = (PDXObjectForm) xobject;
                COSStream invoke = (COSStream) form.getCOSObject();
                PDResources pdResources = form.getResources();
                if (pdResources == null)
                {
                    pdResources = page.findResources();
                }
                // if there is an optional form matrix, we have to
                // map the form space to the user space
                Matrix matrix = form.getMatrix();
                if (matrix != null)
                {
                    Matrix xobjectCTM = matrix.multiply(getGraphicsState().getCurrentTransformationMatrix());
                    getGraphicsState().setCurrentTransformationMatrix(xobjectCTM);
                }
                processSubStream(page, pdResources, invoke);

                // restore the graphics state
                setGraphicsState((PDGraphicsState) getGraphicsStack().pop());
            }
        }
        else
        {
            super.processOperator(operator, arguments);
        }
    }

    public class ImageLocation
    {
        public ImageLocation(PDPage page, Matrix matrix, PDXObjectImage image)
        {
            this.page = page;
            this.matrix = matrix;
            this.image = image;
        }

        public PDPage getPage()
        {
            return page;
        }

        public Matrix getMatrix()
        {
            return matrix;
        }

        public PDXObjectImage getImage()
        {
            return image;
        }

        final PDPage page;
        final Matrix matrix;
        final PDXObjectImage image;
    }

    final List<ImageLocation> locations = new ArrayList<ImageLocation>();
}

ImageLocator.java

与示例类相比,此帮助程序将位置存储在列表中而不是打印它们。

我们现在可以使用以下代码覆盖现有图像:

try (   InputStream resource = getClass().getResourceAsStream("sample.pdf");
        InputStream left = getClass().getResourceAsStream("left.png");
        InputStream right = getClass().getResourceAsStream("right.png");
        PDDocument document = PDDocument.load(resource) )
{
    if (document.isEncrypted())
    {
        document.decrypt("");
    }

    PDJpeg leftImage = new PDJpeg(document, ImageIO.read(left));
    PDJpeg rightImage = new PDJpeg(document, ImageIO.read(right));

    // Locate images
    ImageLocator locator = new ImageLocator();
    List<?> allPages = document.getDocumentCatalog().getAllPages();
    for (int i = 0; i < allPages.size(); i++)
    {
        PDPage page = (PDPage) allPages.get(i);
        locator.processStream(page, page.findResources(), page.getContents().getStream());
    }

    // cover images
    for (ImageLocation location : locator.getLocations())
    {
        // Decide on a replacement
        PDRectangle cropBox = location.getPage().findCropBox();
        float center = (cropBox.getLowerLeftX() + cropBox.getUpperRightX()) / 2.0f;
        PDJpeg image = location.getMatrix().getXPosition() < center ? leftImage : rightImage;

        AffineTransform transform = location.getMatrix().createAffineTransform();

        PDPageContentStream content = new PDPageContentStream(document, location.getPage(), true, false, true);
        content.drawXObject(image, transform);
        content.close();
    }

    document.save(new File(RESULT_FOLDER, "sample-changed.pdf"));
}

OverwriteImage

此示例使用left.png覆盖其各自页面左半部分的所有图像,使用right.png覆盖所有其他图像。

答案 1 :(得分:0)

我没有实现或示例,但我希望通过以下步骤向您说明您想要的方法:

  1. 由于您需要在pdf中使用2个图像(让我们告诉它们imageA和imageB)而不是1(这是空白的)。您必须将它们都添加到pdf中。
  2. 保存文件临时 - 可选,它可以在不重写pdf的情况下工作
  3. 重新打开文件 - 可选,如果您不需要第2步,则也不需要此步骤
  4. 然后用imageA或imageB
  5. 替换空白图像
  6. 从pdf中删除空白图片
  7. 保存pdf