Is there a way to set bounding box of figure in PDFBox - java

I'm trying to create a PDF that's PAC3 compliant and I need to add images to the PDF. I was able add an image to the PDF but when I run the PDF in PAC 3. I get an error because my image doesn't have a bounding box.
PAC3 output:
image of PAC3
Here's my code for adding an image to the pdf document.
PDStructureElement currentElem;
public void drawImage(PDStructureElement parent, float width, float height, float x,float y) throws IOException {
currentElem = addContentToParent(null, StandardStructureTypes.Figure, parent);
currentElem.setAlternateDescription("logo");
PDImageXObject logoImg = PDImageXObject.createFromFile("logo.jpg", this.pdf);
PDPageContentStream contentStream = new PDPageContentStream(this.pdf, this.pdf.getPage(0), PDPageContentStream.AppendMode.APPEND, false);
setNextMarkedContentDictionary();
contentStream.beginMarkedContent(COSName.IMAGE, PDPropertyList.create(currentMarkedContentDictionary));
contentStream.drawImage(logoImg, x, y,45,42);
contentStream.endMarkedContent();
contentStream.close();
addContentToParent(COSName.IMAGE, null, currentElem);
}
My Question:
How do I add a bounding box to a image? Is it even possible with PDFBox?

Originally I found a workaround for passing the PAC 3 tests, I would draw the images after everything was written and for whatever reason that worked on my 13 page document but when I tried to convert a 2 page document the error started to appear again.
Since then I found the proper way of giving an image a bounding box you will need to add this code to the image creation and it will satisfy PAC 3 tests (the proper way)
PDRectangle boundingBox = new PDRectangle(x, y, width, height);
COSDictionary attr = new COSDictionary();
attr.setString(COSName.BBOX, boundingBox.toString());
currentElem.getCOSObject().setItem(COSName.A, attr);

Related

PDFBox 2.0.9 - pdf to image with alpha

I have problem with transferring PDF page to image (png) using PDFBox. I'm trying to transfer to image because then I want to use in PowerPoint presentation. Everything works fine except one case. Graph colors are not passed to image when fill of graph has transparency setup and colors are not from main palette.
Here is the link to pdf
Attaching sample image with pdf on left and output on the right
Here is sample responsible for pdf to img:
public static BufferedImage convertPDF2Image(PDDocument pdf, int page) throws IOException {
BufferedImage image = null;
ImageType imageType = ImageType.ARGB;
// int dpi = 96;
int dpi = 100;
PDFRenderer renderer = new PDFRenderer(pdf);
image = renderer.renderImageWithDPI(page, dpi, imageType);
return image;
}

Why does PDFBox read the image width/height wrong? (always assumes "width" is the bigger one)

I'm using the PDFBox library (see here) to convert an image to PDF. The goal is to have a image scaled to a full A4 page in the PDF file. And it works well, except one thing:
The image height and width seem to be mixed up. The width is always assumed to be the bigger value of them both. I have 2 images: One has the dimensions (according to the Windows file details) 4032x2268 (landscape) and the other one 2268x4032 (portrait).
When i load the images in PDFBox, the width is always 4032 and the height 2268. The goal is to create a landscape PDF for one and a portrait PDF for the other one. This weird "bug" (?) causes the portrait image to convert to a landscape PDF which of course causes the image to be rotated (which is inconventient).
Here's the relevant part of my code:
public byte[] imageToPDF(MultipartFile file) throws IOException {
PDDocument pdf = new PDDocument();
PDImageXObject pdImage = PDImageXObject.createFromByteArray(pdf, file.getBytes(), file.getOriginalFilename());
// scale image to fit the full page
PDPage page;
int imageWidth;
int imageHeight;
if (pdImage.getWidth() > pdImage.getHeight()) {
// landscape pdf
float pageHeight = PDRectangle.A4.getWidth();
float pageWidth = PDRectangle.A4.getHeight();
page = new PDPage(new PDRectangle(pageWidth, pageHeight));
imageWidth = (int)pageWidth;
imageHeight = (int)(((double)imageWidth / (double)pdImage.getWidth()) * (double)pdImage.getHeight());
} else {
// portrait pdf
float pageHeight = PDRectangle.A4.getHeight();
float pageWidth = PDRectangle.A4.getWidth();
page = new PDPage(new PDRectangle(pageWidth, pageHeight));
imageHeight = (int)pageHeight;
imageWidth = (int)(((double)imageHeight / (double)pdImage.getHeight()) * (double)pdImage.getWidth());
}
...
}
pdImage.getWidth() is always greater than pdImage.getHeight(), no matter which of the two images I use. Does anyone have an idea?

PDFBox renderImage produces incorrect image dimensions at specified scale

I am using the very useful PDFBox to build a simple pdf stamping GUI.
I noticed a serious issue with a particular document however.
When I specify a particular scale factor for the rendering, the expected output image size is different.
What is worse? the scaling factor used for the resultant image along the horizontal axis is different from that along the vertical axis.
Here is the code I used:
/**
* #param pdfPath The path to the pdf document
* #param page The pdf page number(is zero based)
*/
public BufferedImage loadPdfImage(String pdfPath, int page) {
File file = new File(pdfPath);
try (PDDocument doc = PDDocument.load(file)) {
pageCount = doc.getNumberOfPages();
PDPage pDPage = doc.getPage(page);
float w = pDPage.getCropBox().getWidth();
float h = pDPage.getCropBox().getHeight();
System.out.println("Pdf opening: width: "+w+", height: "+h);
PDFRenderer renderer = new PDFRenderer(doc);
float dpiRatio = 1.5f;
BufferedImage img = renderer.renderImage(page, dpiRatio);
float dpiXRatio = img.getWidth() / w;
float dpiYRatio = img.getHeight()/ h;
System.out.println("dpiXRatio: "+dpiXRatio+", dpiYRatio: "+dpiYRatio);
return img;
} catch (IOException ex) {
System.out.println( "invalid pdf found. Please check");
}
return null;
}
The code above loads most pdf documents that I have tried it on and converts given pages within them to BufferedImage objects.
For the said document however, it seems to be unable to render the converted image at the supplied scale-factor.
Is there anything wrong with my code? or is it a known bug?
Thanks.
EDIT
I am using PDFBOX v2.0.15
And the page has no rotation.
The error was mine; for the most part.
I had used the MediaBox to compute the scale factors and unfortunately the MediaBox and CropBox of the pdf file in question were not the same.
For example:
cropbox-rect: [8.50394,34.0157,586.496,807.984]
mediabox-rect: [0.0,0.0,595.0,842.0]
After making corrections for these, the scale-factors matched better along both axes, save for the errors due to the fact that the image sizes are integer numbers.
This is negligible enough for me to neglect, though.
When stamping, all I had to do was to make the necessary corrections for the cropbox. For example to draw the image(stamp) at P(x,y), I would do:
x += cropBox.getLowerLeftX();
y += cropBox.getLowerLeftY();
before calling the draw image functionality.
It all came out fine!

Drawing vector images on PDF with PDFBox

I would like to draw a vector image on a PDF with Apache PDFBox.
This is the code I use to draw regular images
PDPage page = (PDPage) document.getDocumentCatalog().getAllPages().get(1);
PDPageContentStream contentStream = new PDPageContentStream(document, page, true, true);
BufferedImage _prevImage = ImageIO.read(new FileInputStream("path/to/image.png"));
PDPixelMap prevImage = new PDPixelMap(document, _prevImage);
contentStream.drawXObject(prevImage, prevX, prevY, imageWidth, imageHeight);
If I use a svg or wmf image instead of png, the resulting PDF document comes corrupted.
The main reason I want the image to be a vector image is that with PNG or JPG the image looks horrible, I think it gets somehow compressed so it looks bad. With vector images this shouldn't happen (well, when I export svg paths as PDF in Inkscape it doesn't happen, vector paths are preserved).
Is there a way to draw a svg or wmf (or other vector) to PDF using Apache PDFBox?
I'm currently using PDFBox 1.8, if that matters.
See the library pdfbox-graphics2d, touted in this Jira.
You can draw the SVG, via Batik or Salamander or whatever, onto the class PdfBoxGraphics2D, which is parallel to iText's template.createGraphics(). See the GitHub page for samples.
PDDocument document = ...;
PDPage page = ...; // page whereon to draw
String svgXML = "<svg>...</svg>";
double leftX = ...;
double bottomY = ...; // PDFBox coordinates are oriented bottom-up!
// I set these to the SVG size, which I calculated via Salamander.
// Maybe it doesn't matter, as long as the SVG fits on the graphic.
float graphicsWidth = ...;
float graphicsHeight = ...;
// Draw the SVG onto temporary graphics.
var graphics = new PdfBoxGraphics2D(document, graphicsWidth, graphicsHeight);
try {
int x = 0;
int y = 0;
drawSVG(svg, graphics, x, y); // with Batik, Salamander, or whatever you like
} finally {
graphics.dispose();
}
// Graphics are not visible till a PDFormXObject is added.
var xform = graphics.getXFormObject();
try (var contentWriter = new PDPageContentStream(document, page, AppendMode.APPEND, false)) { // false = don't compress
// XForm objects have to be placed via transform,
// since they cannot be placed via coordinates like images.
var transform = AffineTransform.getTranslateInstance(leftX, bottomY);
xform.setMatrix(transform);
// Now the graphics become visible.
contentWriter.drawForm(xform);
}
And ... in case you want also to scale the SVG graphics to 25% size:
// Way 1: Scale the SVG beforehand
svgXML = String.format("<svg transform=\"scale(%f)\">%s</svg>", .25, svgXML);
// Way 2: Scale in the transform (before calling xform.setMatrix())
transform.concatenate(AffineTransform.getScaleInstance(.25, .25));
I do this, but not directly.
In first transform your SVG documents in PDF documents with FOP librairy and Batik.
https://xmlgraphics.apache.org/fop/dev/design/svg.html.
In second times, you can use LayerUtility in pdfbox to transform your new pdf document in PDXObjectForm. After that, just needs to include PDXObjectForm in your final pdf documents.
The final working solution for me that loads an SVG file and overlays it on a PDF file (this renders the SVG in a 500x500 box at (0,0) coordinate which is bottom left of the PDF document):
package com.example.svgadder;
import java.io.*;
import java.nio.*;
import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import de.rototor.pdfbox.graphics2d.PdfBoxGraphics2D;
import java.awt.geom.AffineTransform;
import com.kitfox.svg.SVGDiagram;
import com.kitfox.svg.SVGException;
import com.kitfox.svg.SVGUniverse;
public class App
{
public static void main( String[] args ) throws Exception {
App app = new App();
}
public App() throws Exception {
// loading PDF and SVG files
File pdfFile = new File("input.pdf");
File svgFile = new File("input.svg");
PDDocument doc = PDDocument.load(pdfFile);
PDPage page = doc.getPage(0);
SVGUniverse svgUniverse = new SVGUniverse();
SVGDiagram diagram = svgUniverse.getDiagram(svgUniverse.loadSVG(f.toURL()));
PdfBoxGraphics2D graphics = new PdfBoxGraphics2D(doc, 500, 500);
try {
diagram.render(graphics);
} finally {
graphics.dispose();
}
PDFormXObject xform = graphics.getXFormObject();
try (PDPageContentStream contentWriter = new PDPageContentStream(doc, page, AppendMode.APPEND, false)) {
AffineTransform transform = AffineTransform.getTranslateInstance(0, 0);
xform.setMatrix(transform);
contentWriter.drawForm(xform);
}
doc.save("res.pdf");
doc.close();
}
}
Please use svgSalamander from here:
https://github.com/mgarin/svgSalamander
Please use what Coemgenus suggested for scaling your final overlaid SVG. I tried the 2nd option and it works well.
Nirmal

PDFBox draw black image from BufferedImage

I try to draw an image from a bufferedImage into a PDF using PDFBox but fails, and I get black images and Acrobat Reader warns whith errors like "Out of memory" (but PDF is display).
I use a bufferedImage because I need to draw a JavaFX Image object (with came from call to Funciones.crearImagenDesdeTexto(), is a function which converts a text into an Image) into PDF. Rest of images works well without using bufferedimage.
PDPixelMap img = null;
BufferedImage bi;
try {
//If item has id, I try to get image with that id (image it's shows OK on PDF)
img = new PDPixelMap(documento, read(getClass().getResourceAsStream("/com/img/" + item.getId() + ".png")));
}
catch (Exception e) {
//If item has not id or fails load image, I create image on the fly (which contains item name. This not work on PDF, shows black images)
bi = new BufferedImage(alto, ancho, BufferedImage.TYPE_INT_ARGB);
bi.createGraphics().drawImage(SwingFXUtils.fromFXImage(Funciones.crearImagenDesdeTexto(item.getNombre()), null), ancho, alto, null);
img = new PDPixelMap(documento, bi);
}
finally {
contenedor.drawXObject(img, x, y, alto, ancho);
}
NOTE: crearImagenDesdeTexto() returns a JavaFX Image Object that is create on the fly (I try this function in other parts of the program and works well, function is take from other stackOverflow response).
Your code is confusing, you have three "new PDJpeg" and one of them is in a catch (which should just handle the error). And what does "read()" do? Does it pass a stream or a BufferedImage? If it is a stream, then it is wrong, because PDJpeg is for JPEGs, not for PNG.
The second one
img = new PDJpeg(documento, (getClass().getResourceAsStream("/com/img/" + Byte.toString(item.getId()) + ".png")));
is definitively wrong for the same reason: PDJPeg is not for PNG files / streams.
If you want to create an image from a PNG file / stream, use PDPixelMap.
It is possible to create a PDJpeg object from a BufferedImage, but this is recommended only if the image wasn't encoded before. Because if you would read a BufferedImage from a JPEG, and then use PDJPeg for this, you'll have a slight loss of quality as the image is decoded and encoded again (JPEG is a "lossy" compression format).
If my advice doesn't help, please upload the JPEG file and the PDF somewhere.
Also make sure that you're using the latest version, which is 1.8.7.
Update after comments:
the parameters to createGraphics.drawImage() should be 0, 0 and not width, height. The two parameters are a location, not a size.
Finally, I find a solution (thanks also to Tilman Hausherr):
private void dibujarImagen(Item i, int x, int y, int alto, int ancho) throws IOException {
PDPixelMap img = null;
try {
img = new PDPixelMap(documento, read(getClass().getResourceAsStream("/com/img/" + i.getId() + ".png")));
}
catch (IllegalArgumentException e) {
img = new PDPixelMap(documento, SwingFXUtils.fromFXImage(Funciones.crearImagenDesdeTexto(i.getNombre()),null));
}
finally {
contenedor.drawXObject(img, x, y, alto, ancho);
}
}

Categories

Resources