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();
Related
i am trying to create pdf using pdfbox. i am storing EditText data as html in Sqlite DB.
now i am retrieving data from sqliteDB and creating pdf of that. this data is having marathi language as well as english language.
i am using NotoSerifDevanagari-Bold font and have added it to assets folder. from there i am accessing this font into code. but i am getting error. please find my code and error below.
AssetManager assetManager;
PDFBoxResourceLoader.init(getApplicationContext());
File FilePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
assetManager = getAssets();
PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);
PDFont font = PDType0Font.load(document, assetManager.open("notoserifdevanagaribold.ttf"));
PDPageContentStream contentStream;
// Define a content stream for adding to the PDF
contentStream = new PDPageContentStream(document, page);
Cursor data = mDatabaseHelper.getDataByDeckname(deckname);
StringBuilder builder=new StringBuilder();
while (data.moveToNext()) {
String front_page_desc = data.getString(3);
String back_page_desc = data.getString(4);
contentStream.beginText();
contentStream.setNonStrokingColor(15, 38, 192);
contentStream.setFont(font, 12);
contentStream.newLineAtOffset(100, 700);
contentStream.showText(Html.fromHtml(front_page_desc).toString());
contentStream.endText();
contentStream.beginText();
contentStream.setNonStrokingColor(15, 38, 192);
contentStream.setFont(font, 12);
contentStream.newLineAtOffset(100, 700);
contentStream.showText(Html.fromHtml(back_page_desc).toString());
contentStream.endText();
}
contentStream.close();
String path = FilePath.getAbsolutePath() + "/temp.pdf";
document.save(path);
document.close();
ERROR
W/System.err: java.lang.IllegalArgumentException: No glyph for U+000A in font NotoSerifDevanagari-Bold
I tried so many examples for above error but i am not able to fix the issue. this error i am getting on contentStream.showText(Html.fromHtml(front_page_desc).toString()); line. can someone please help me on above.
As per this link U+000A is the Unicode for new line. Any font will fail if you try to render it.
In order to avoid such error you can try something like this:
String[] lines = text.split("\\n");
for (String line : lines) {
if (!line.isBlank()) {
contentStream.showText(line);
// add new line here if you want to
}
}
My use case is to have a button like so on a pdf page (really to add them to existing pages but for now I just want to see it work on anything).
----------
- Back -
----------
And what it does is just closes the current pdf page. The idea is to have multiple tabs opened and each tab is a pdf and then when you hit the "Back" button it closes the current pdf which will then focus back to the previous pdf. This is what I have been trying to use so far.
// Create a new empty document
try {
PDDocument document = new PDDocument();
// Create a new blank page and add it to the document
PDPage blankPage = new PDPage();
document.addPage( blankPage );
PDBorderStyleDictionary borderULine = new PDBorderStyleDictionary();
borderULine.setStyle(PDBorderStyleDictionary.STYLE_UNDERLINE);
PDColor green = new PDColor(new float[] { 0, 1, 0 }, PDDeviceRGB.INSTANCE);
// PDAnnotationTextMarkup txtMark = new PDAnnotationTextMarkup(PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT);
// textWidth = (font.getStringWidth("Click Here") / 1000) * 18;
PDAnnotationLink txtLink = new PDAnnotationLink();
txtLink.setBorderStyle(borderULine);
// add an action
// PDActionURI action = new PDActionURI();
// action.setURI("www.google.com");
PDActionJavaScript action = new PDActionJavaScript("this.closeDoc()");
txtLink.setAction(action);
txtLink.setContents("HI");
txtLink.setColor(green);
PDRectangle position = new PDRectangle();
position.setLowerLeftX(10);
position.setLowerLeftY(20);
position.setUpperRightX(100);
position.setUpperRightY(40);
txtLink.setRectangle(position);
txtLink.setInvisible(false);
blankPage.getAnnotations().add(txtLink);
// Save the newly created document
document.save("C:\\Users\\jsmith\\Desktop\\demo\\BlankPage.pdf");
document.close();
} catch (IOException e) {
e.printStackTrace();
}
And I cant seem to see anything on the pdf page (its just all white), I did get the following code at at least be able to go to a new page instead of the javascript but it was still invisible. I just was able to hover over the bottom left and notice i could click on a link.
PDActionURI action = new PDActionURI();
action.setURI("www.google.com");
Improved answer as discussed in the comments of the OPs own answer, and it also includes the answer from the follow-up question.
PDDocument doc = new PDDocument();
PDPage page = new PDPage();
doc.addPage(page);
COSDictionary acroFormDict = new COSDictionary();
PDAcroForm acroForm = new PDAcroForm(doc, acroFormDict);
doc.getDocumentCatalog().setAcroForm(acroForm);
acroForm.setFields(new ArrayList<>());
PDPushButton button = new PDPushButton(acroForm);
button.setPartialName("Btn1");
PDActionJavaScript actionJavaScript = new PDActionJavaScript("this.closeDoc();");
PDAnnotationAdditionalActions additionalActions = new PDAnnotationAdditionalActions();
additionalActions.setU(actionJavaScript);
// widget
PDAnnotationWidget widget = button.getWidgets().get(0);
widget.setActions(additionalActions);
widget.setRectangle(new PDRectangle(100, 700, 100, 50));
PDColor colourBlack = new PDColor(new float[] { 0, 0, 0 }, PDDeviceRGB.INSTANCE);
PDAppearanceCharacteristicsDictionary fieldAppearance
= new PDAppearanceCharacteristicsDictionary(new COSDictionary());
fieldAppearance.setBorderColour(colourBlack);
widget.setAppearanceCharacteristics(fieldAppearance);
// Create appearance
PDAppearanceDictionary appearanceDictionary = new PDAppearanceDictionary();
PDAppearanceStream appearanceStream = new PDAppearanceStream(doc);
appearanceStream.setResources(new PDResources());
try (PDPageContentStream cs = new PDPageContentStream(doc, appearanceStream))
{
PDRectangle bbox = new PDRectangle(widget.getRectangle().getWidth(), widget.getRectangle().getHeight());
appearanceStream.setBBox(bbox);
cs.setNonStrokingColor(0, 0, 0); // black
cs.addRect(bbox.getLowerLeftX() + 0.5f, bbox.getLowerLeftY() + 0.5f, bbox.getWidth() - 1, bbox.getHeight() - 1);
cs.stroke();
// put some useful text
cs.setFont(PDType1Font.HELVETICA, 20);
cs.beginText();
cs.newLineAtOffset(20, 20);
cs.showText("Close");
cs.endText();
}
appearanceDictionary.setNormalAppearance(appearanceStream);
widget.setAppearance(appearanceDictionary);
page.getAnnotations().add(widget);
acroForm.getFields().add(button);
doc.save("..../Button.pdf");
doc.close();
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();
}
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();
I need to write letters with diacritics to PDF using PDFBox.
PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);
PDPageContentStream contentStream = new PDPageContentStream(document, page);
PDType1Font font = PDType1Font.HELVETICA;
contentStream.setFont(font, 12);
contentStream.beginText();
contentStream.moveTextPositionByAmount(100, 400);
contentStream.drawString("čćšž");
contentStream.endText();
contentStream.close();
document.save("document.pdf");
document.close();
But instead of getting "čćšž" I'm getting garbage in document.
My research didin't get me anywhere. Solution 1 doesn't work, and Solution 2 says it is not possible to load right encoding. Is it really true?