Javafx-Image To PDF Conversion Displaying Half of the Image Using PDFBox - java

I have successfully converted an Image to Pdf. My issue is that the pdf is displaying half of the width
My Code:
#FXML
private void print() {
try {
WritableImage nodeshot = stackPane.snapshot(new SnapshotParameters(), null);
File file = new File("C:/Users/Andre Kelvin/Desktop/TheNode.png");
ImageIO.write(SwingFXUtils.fromFXImage(nodeshot, null), "png", file);
PDDocument doc = new PDDocument();
PDPage page = new PDPage();
PDImageXObject pdimage;
PDPageContentStream content;
pdimage = PDImageXObject.createFromFile("C:/Users/Andre Kelvin/Desktop/TheNode.png", doc);
content = new PDPageContentStream(doc, page);
content.drawImage(pdimage, 0, 0);
content.close();
doc.addPage(page);
doc.save("C:/Users/Andre Kelvin/Desktop/PDFNode.pdf");
doc.close();
file.delete();
//This Line Automatically Opens the user defualt pdf file viewer
Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + "C:/Users/Andre Kelvin/Desktop/PDFNode.pdf");
} catch (Exception e) {
}
}
I have tried getting the root node width and height by using this line:
content.drawImage(pdimage, 0, 0,(float)stackPane.getPrefWidth(),(float)stackPane.getPrefHeight());
and this:
content.drawImage(pdimage, 0, 0,(float)stackPane.getMaxWidth(),(float)stackPane.getMaxHeight());
it will just display a blank white page.
This is the actual image that is Converted to pdf:
And this is the pdf of the image:

Neither the preferred size properties nor their min/max counterparts allow you to reliably determine the size of a Region. Those are just indicators and the calculated values may not match. Furthermore the Region may be resized to sizes other than the preferred size. Last but not least those properties may contain special values Region.USE_PREF_SIZE(=Double.NEGATIVE_INFINITY) and Region.USE_COMPUTED_SIZE(=-1) and even do so by default.
If you need to get the size of a node, use the boundsInLocal property:
Bounds bounds = stackPane.getBoundsInLocal();
In this case it's simpler to get the size of the snapshot instead though.
Furthermore the page size of the PDPage may not be large enough to contain the whole image. You need to scale the image instead or change the page size of the PDPage.
Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + "C:/Users/Andre Kelvin/Desktop/PDFNode.pdf");
This can be done platform-independent using the HostServices available via the Application instance.
Example
#Override
public void start(Stage primaryStage) {
Button button = new Button("print");
StackPane root = new StackPane(button);
button.setOnAction(evt -> {
try {
WritableImage nodeshot = root.snapshot(new SnapshotParameters(), null);
// store image in-memory
ByteArrayOutputStream output = new ByteArrayOutputStream();
ImageIO.write(SwingFXUtils.fromFXImage(nodeshot, null), "png", output);
output.close();
PDDocument doc = new PDDocument();
PDPage page = new PDPage();
PDImageXObject pdimage;
PDPageContentStream content;
pdimage = PDImageXObject.createFromByteArray(doc, output.toByteArray(), "png");
content = new PDPageContentStream(doc, page);
// fit image to media box of page
PDRectangle box = page.getMediaBox();
double factor = Math.min(box.getWidth() / nodeshot.getWidth(), box.getHeight() / nodeshot.getHeight());
float height = (float) (nodeshot.getHeight() * factor);
// beware of inverted y axis here
content.drawImage(pdimage, 0, box.getHeight() - height, (float) (nodeshot.getWidth() * factor), height);
content.close();
doc.addPage(page);
File outputFile = new File("C:/Users/Andre Kelvin/Desktop/PDFNode.pdf");
doc.save(outputFile);
doc.close();
getHostServices().showDocument(outputFile.toURI().toString());
} catch (Exception e) {
}
});
Scene scene = new Scene(root, 300, 300);
primaryStage.setScene(scene);
primaryStage.show();
}

Related

Page Content not Erasing with Rectangle fill in pdfbox

I am working on a requirement that , I need to mask the certain content based on Keys. With the PDF Rectangle fill i am able to mask well. But the user still able to get the content from masked region by selecting the text. How can i get rid of it.
PDDocument document = new PDDocument();
for (int i=0; i<1; i++) {
PDPage blankPage = new PDPage();
document.addPage( blankPage );
PDPageContentStream contentStream = new PDPageContentStream(document, document.getPage(0));
contentStream.beginText();
contentStream.setFont(PDType1Font.TIMES_ROMAN, 12);
contentStream.newLineAtOffset(25, 500);
String text = "This is the sample document and we are adding content to it.";
contentStream.showText(text);
contentStream.endText();
contentStream.setNonStrokingColor(Color.RED);
contentStream.addRect(25, 500, 100, 100);
contentStream.fill();
contentStream.close();
}
document.save("C:/PdfBox_Examples/my_doc.pdf");
System.out.println("PDF created");
document.close();

pdfbox - pdf increase size after converting to grayscale

I need to convert scanned PDF to grayscale PDF. I found 2 solutions for that.
First one is to just use renderImage
private void convertToGray() throws IOException {
File pdfFile = new File(PATH);
try (PDDocument originalPdf = PDDocument.load(pdfFile);
PDDocument doc = new PDDocument()) {
LOGGER.info("Current heap after loading file: {}", Runtime.getRuntime().totalMemory());
PDFRenderer pdfRenderer = new PDFRenderer(originalPdf);
for (int pageNum = 0; pageNum < originalPdf.getNumberOfPages(); pageNum++) {
// PDImageXObject pdImage = LosslessFactory.createFromImage(doc, bufferedImage);
BufferedImage grayImage = pdfRenderer.renderImageWithDPI(pageNum, 300F, ImageType.GRAY);
PDImageXObject pdImage = JPEGFactory.createFromImage(doc, grayImage);
float pageWight = originalPdf.getPage(pageNum).getMediaBox().getWidth();
float pageHeight = originalPdf.getPage(pageNum).getMediaBox().getHeight();
PDPage page = new PDPage(new PDRectangle(pageWight, pageHeight));
doc.addPage(page);
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page)) {
contentStream.drawImage(pdImage, 0F, 0F, pageWight, pageHeight);
}
}
doc.save(NEW_PATH);
}
}
But this leads to increase size of the file (because some PDFs has less DPI than 300.
Second one is to just replace existing image with gray analog
private void convertByImageToGray() throws IOException {
File pdfFile = new File(PATH);
try (PDDocument document = PDDocument.load(pdfFile)) {
List<COSObject> objects = document.getDocument().getObjectsByType(COSName.IMAGE);
for (COSObject object : objects) {
LOGGER.info("Class: {}; {}", object.getClass(), object.toString());
}
for (int pageNum = 0; pageNum < document.getNumberOfPages(); pageNum++) {
PDPage page = document.getPage(pageNum);
replaceImage(document, page);
}
document.save(NEW_PATH);
}
}
private void replaceImage(PDDocument document, PDPage page) throws IOException {
PDResources resources = page.getResources();
Iterable<COSName> xObjectNames = resources.getXObjectNames();
if (xObjectNames != null) {
for (COSName xObjectName : xObjectNames) {
PDXObject object = resources.getXObject(xObjectName);
if (object instanceof PDImageXObject) {
PDImageXObject img1 = (PDImageXObject) object;
BufferedImage bufferedImage1 = img1.getImage();
BufferedImage grayBufferedImage = convertBufferedImageToGray(bufferedImage1);
// PDImageXObject grayImage = JPEGFactory.createFromImage(document, grayBufferedImage);
PDImageXObject grayImage = LosslessFactory.createFromImage(document, grayBufferedImage);
resources.put(xObjectName, grayImage);
}
}
}
}
private static BufferedImage convertBufferedImageToGray(BufferedImage sourceImg) {
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorConvertOp op = new ColorConvertOp(sourceImg.getColorModel().getColorSpace(), cs, null);
op.filter(sourceImg, sourceImg);
return sourceImg;
}
But still some files increase in size like 3 times (even they were already grayscale; interesting that int this case JPEGFactory produces larger files than LosslessFactory). All images in grayscale PDF have the same size as original ones. And I don't understand why.
Maybe there is a better way to make grayscale PDF with predictable size (except ghostscript)?
UPDATE: I've just realized that the issue is with creating PDF from image. It does not compress as well.
For example, I have dummy 1-page scan file that is less than 1 Mb. But if I get image from it (directly copying via Acrobat Reader to Paint, or via code above) it size is ~8-10 Mb depending on the method. And if I create new PDF from this image it's barely compressed. Here is example code:
File pdfFile = new File(FULL_FILE);
try (PDDocument document = PDDocument.load(pdfFile)) {
PDPage page = new PDPage();
document.addPage(page);
PDImageXObject pdImage = PDImageXObject.createFromFile("example.png", document);
try (PDPageContentStream contents = new PDPageContentStream(document, page)) {
contents.drawImage(pdImage, 0F, 0F);
}
document.save(FULL_FILE_NEW);
}
Yes LosslessFactory produces smaller files compared to JPEGFactory
In the below link there are different methods to try and achieve the same goal. Overall the best quality gray scale image was the one from Option 6, however this was by no means the fastest (I myself used Option 4). Comparisons are also provided for you to choose
This link contains possible ways to convert color images to black. It helped me a lot.
Let me know if it works for you and approve my answer if it helped.

BufferedImage color saturation

I'm writing a simple scanning application using jfreesane and Apache PDFBox.
Here is the scanning code:
InetAddress address = InetAddress.getByName("192.168.0.17");
SaneSession session = SaneSession.withRemoteSane(address);
List<SaneDevice> devices = session.listDevices();
SaneDevice device = devices.get(0);
device.open();
device.getOption("resolution").setIntegerValue(300);
BufferedImage bimg = device.acquireImage();
File file = new File("test_scan.png");
ImageIO.write(bimg, "png", file);
device.close();
And making PDF:
PDDocument document = new PDDocument();
float width = bimg.getWidth();
float height = bimg.getHeight();
PDPage page = new PDPage(new PDRectangle(width, height));
document.addPage(page);
PDImageXObject pdimg = LosslessFactory.createFromImage(document, bimg);
PDPageContentStream stream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true);
stream.drawImage(pdimg, 0, 0);
stream.close();
document.save(filename);
document.close();
And here is the result:
As you can see the PDF image is more "pale" (saturation? - sorry, I'm not good at color theory and don't know how to name it correctly).
What I have found out:
Printing BufferedImage to JLabel using JLabel(new ImageIcon(bimg))
constructor produces the same result as with PDF ("pale" colors)
so I guess PDFBox is not the reason.
Changing scanning resolution -
no effect.
bimg.getTransparency() returns 1 (OPAQUE)
bimg.getType() returns 0 (TYPE_CUSTOM)
PNG file:
http://s000.tinyupload.com/index.php?file_id=95648202713651192395
PDF file
http://s000.tinyupload.com/index.php?file_id=90369236997064329368
There was an issue in JFreeSane with colorspaces, it was fixed in version 0.97:
https://github.com/sjamesr/jfreesane/releases/tag/jfreesane-0.97

How to move image to the top of the PDF page using Apache PDFBox?

I am using PDFBox to generate reports in Java. One of my requirements is to create a PDF document which contains the company logo at the top of the page. I am not able to find the way to accomplish that.
I have the following method in a Java class:
public void createPdf() {
PDDocument document = null;
PDPage page = null;
ServletContext servletContext = (ServletContext) FacesContext
.getCurrentInstance().getExternalContext().getContext();
try {
File f = new File("Afiliado_2.pdf");
if (f.exists() && !f.isDirectory()) {
document = PDDocument.load(new File("Afiliado_2.pdf"));
page = document.getPage(0);
} else {
document = new PDDocument();
page = new PDPage();
document.addPage(page);
}
PDImageXObject pdImage = PDImageXObject.createFromFile(
servletContext.getRealPath("/resources/images/logo.jpg"),
document);
PDPageContentStream contentStream = new PDPageContentStream(
document, page, AppendMode.APPEND, true);
contentStream.drawImage(pdImage, 0, 0);
// Make sure that the content stream is closed:
contentStream.close();
// Save the results and ensure that the document is properly closed:
document.save("Afiliado_2.pdf");
document.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The image is currently appearing in the bottom of the PDF. I know the line I need to modify is contentStream.drawImage(pdImage, 0, 0); but what coordinates do I need to specify so that is appears in the top of the page?
Typically the coordinate system for a page in PDF starts at the lower left corner. So with
contentStream.drawImage(pdImage, 0, 0);
you are drawing your image at that point. You can get the boundaries of your page using
page.getMediaBox();
and use that to position your image e.g.
PDRectangle mediaBox = page.getMediaBox();
// draw with the starting point 1 inch to the left
// and 2 inch from the top of the page
contentStream.drawImage(pdImage, 72, mediaBox.getHeight() - 2 * 72);
where PDF files normally specify 72 points to 1 physical inch.

Using Overlay in PDFBox 2.0

What I am trying to do here is to create text and place it onto a blank page. That page would then be overlayed onto another document and that would then be saved as one document. In 1.8 I was able to create a blank PDPage in a PDF, write text to it as needed, then overlay that PDF with another and then save or view on screen using the code below -
overlayDoc = new PDDocument();
page = new PDPage();
overlayDoc.addPage(page);
overlayObj = new Overlay();
font = PDType1Font.COURIER_OBLIQUE;
try {
contentStream = new PDPageContentStream(overlayDoc, page);
contentStream.setFont(font, 10);
}
catch (Exception e){
System.out.println("content stream failed");
}
After I created the stream, when I needed to write something to the overlay document's contentStream, I would call this method, give it my x, y coords and tell it what text to write (again, this is in my 1.8 version):
protected void writeString(int x, int y, String text) {
if (text == null) return;
try {
contentStream.moveTo(x, y);
contentStream.beginText();
contentStream.drawString(text); // deprecated. Use showText(String text)
contentStream.endText();
}
catch (Exception e){
System.out.println(text + " failed. " + e.toString());
}
}
I would call this method whenever I needed to add text and to wherever I needed to do so. After this, I would close my content stream and then merge the documents together as such:
import org.apache.pdfbox.Overlay;
Overlay overlayObj = new Overlay();
....
PDDocument finalDoc = overlayObj.overlay(overlayDoc, originalDoc);
finalDoc now contains a PDDocument which is my original PDF with text overlayed where needed. I could save it and view it as a BufferedImage on the desktop. The reason I moved to 2.0 was that first off I needed to stay on top of the most recent library and also that I was having issues putting an image onto the page (see here).
The issue I am having in this question is that 2.0 no longer has something similar to the org.apache.pdfbox.Overlay class. To confuse me even more is that there are two Overlay classes in 1.8 (org.apache.pdfbox.Overlay and org.apache.pdfbox.util.Overlay) whereas in 2.0 there is only one. The class I need (org.apache.pdfbox.Overlay), or the methods it offers at least, are not present in 2.0 as far as I can tell. I can only find org.apache.pdfbox.multipdf.Overlay.
Here's some quick code that works, it adds "deprecated" over a document and saves it elsewhere:
PDDocument overlayDoc = new PDDocument();
PDPage page = new PDPage();
overlayDoc.addPage(page);
Overlay overlayObj = new Overlay();
PDFont font = PDType1Font.COURIER_OBLIQUE;
PDPageContentStream contentStream = new PDPageContentStream(overlayDoc, page);
contentStream.setFont(font, 50);
contentStream.setNonStrokingColor(0);
contentStream.beginText();
contentStream.moveTextPositionByAmount(200, 200);
contentStream.drawString("deprecated"); // deprecated. Use showText(String text)
contentStream.endText();
contentStream.close();
PDDocument originalDoc = PDDocument.load(new File("...inputfile.pdf"));
overlayObj.setOverlayPosition(Overlay.Position.FOREGROUND);
overlayObj.setInputPDF(originalDoc);
overlayObj.setAllPagesOverlayPDF(overlayDoc);
Map<Integer, String> ovmap = new HashMap<Integer, String>(); // empty map is a dummy
overlayObj.setOutputFile("... result-with-overlay.pdf");
overlayObj.overlay(ovmap);
overlayDoc.close();
originalDoc.close();
What I did additionally to your version:
declare variables
close the content stream
set a color
set to foreground
set a text position (not a stroke path position)
add an empty map
And of course, I read the OverlayPDF source code, it shows more possibilities what you can do with the class.
Bonus content:
Do the same without using the Overlay class, which allows further manipulation of the document before saving it.
PDFont font = PDType1Font.COURIER_OBLIQUE;
PDDocument originalDoc = PDDocument.load(new File("...inputfile.pdf"));
PDPage page1 = originalDoc.getPage(0);
PDPageContentStream contentStream = new PDPageContentStream(originalDoc, page1, true, true, true);
contentStream.setFont(font, 50);
contentStream.setNonStrokingColor(0);
contentStream.beginText();
contentStream.moveTextPositionByAmount(200, 200);
contentStream.drawString("deprecated"); // deprecated. Use showText(String text)
contentStream.endText();
contentStream.close();
originalDoc.save("....result2.pdf");
originalDoc.close();

Categories