在PDFBox中替换具有相同资源的图像 [英] Replacing images with same resource in PDFBox

查看:174
本文介绍了在PDFBox中替换具有相同资源的图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a pdf containing 2 blank images. I need to replace both the images with 2 separate images using PDFBox. The problem is, both the blank images appear to have the same resource. So, if I replace one, the other one is replaced with the same image as well.

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

I followed this example and tried overriding the processOperator() method and replaced the images based on the imageHeight. However, it still ends up replacing both the images with the same image. This is my code thus far:

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< 480 ),它为空白图像打印imageHeight为30和470。但是,当我添加if块时,它会将imageHeight打印为480和1500,并且永远不会进入if块内部,因为这两个空白图像最终都会被同一图像替换。

Now, when I remove the if block which checks if (imageHeight < 480), it prints the imageHeight as 30 and 470 for the blank images. However, when I add the if block, it prints the imageHeight as 480 and 1500 and never goes inside the if block because of which both the blank images end up getting replaced by the same image.

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

What's going on here? I'm new to PDFBox, so I am unsure if my code is correct.

推荐答案

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

While first thinking about a generic way to actually replace the existing Image by the new Images, I agree with @TilmanHausherr that a more simple solution would be to simply add an extra content stream with two images in the size / position you need covering the existing Image.

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

This approach is easier to implement (even generically) and less error-prone than actual replacement.

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

In a generic solution we do not have the Image positions beforehand. To determine them, we can use this helper class (which essentially is a rip-off of the PDFBox example 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

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

In contrast to the example class this helper stores the locations in a list instead of printing them.

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

We now can cover existing images using code like this:

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。

This sample covers all images on the left half of their respective page with left.png and all others with right.png.

这篇关于在PDFBox中替换具有相同资源的图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆