I am using PDF Render for reading and updating PDF.
I want to add bookmark in that PDF and update it using same API.
Is it possible to do so with PDF Renderer?
Here is some code snippet to update bookmarks in PDF which is not working
File file = new File("C:\\test.pdf");
RandomAccessFile raf = new RandomAccessFile(file, "rw");
FileChannel channel = raf.getChannel();
ByteBuffer buf = channel.map(FileChannel.MapMode.READ_WRITE, 0, channel.size());
PDFFile pdffile = new PDFFile(buf);
OutlineNode rootNode = new OutlineNode("New Bookmark");
PDFPage page = pdffile.getPage(0);
OutlineNode node = pdffile.getOutline();
OutlineNode node2 = (OutlineNode)node.getNextNode();
node2.add(rootNode);
I am using PDFRenderer-0.9.0.jar lib for above example.
If any one worked on PDF Renderer, please suggest me.
You can add a bookmark using this.
For full code and implementation follow the link.
you can use follow the link if you want to access the bookmark later.
import pdftron.Common.PDFNetException;
import pdftron.PDF.*;
import pdftron.SDF.Obj;
import pdftron.SDF.SDFDoc;
public class BookmarkTest {
public static void main(String[] args)
{
PDFNet.initialize();
// Relative path to the folder containing test files.
String input_path = "../../TestFiles/";
String output_path = "../../TestFiles/Output/";
// The following example illustrates how to create and edit the outline tree
// using high-level Bookmark methods.
try
{
PDFDoc doc=new PDFDoc((input_path + "numbered.pdf"));
doc.initSecurityHandler();
// Lets first create the root bookmark items.
Bookmark red = Bookmark.create(doc, "Red");
Bookmark green = Bookmark.create(doc, "Green");
Bookmark blue = Bookmark.create(doc, "Blue");
doc.addRootBookmark(red);
doc.addRootBookmark(green);
doc.addRootBookmark(blue);
// You can also add new root bookmarks using Bookmark.AddNext("...")
blue.addNext("foo");
blue.addNext("bar");
// We can now associate new bookmarks with page destinations:
// The following example creates an 'explicit' destination (see
// section '8.2.1 Destinations' in PDF Reference for more details)
Destination red_dest = Destination.createFit((Page)(doc.getPageIterator().next()));
red.setAction(Action.createGoto(red_dest));
// Create an explicit destination to the first green page in the document
green.setAction(Action.createGoto(
Destination.createFit((Page)(doc.getPage(10))) ));
// The following example creates a 'named' destination (see
// section '8.2.1 Destinations' in PDF Reference for more details)
// Named destinations have certain advantages over explicit destinations.
byte[] key={'b','l','u','e','1'};
Action blue_action = Action.createGoto(key,
Destination.createFit((Page)(doc.getPage(19))) );
blue.setAction(blue_action);
// We can now add children Bookmarks
Bookmark sub_red1 = red.addChild("Red - Page 1");
sub_red1.setAction(Action.createGoto(Destination.createFit((Page)(doc.getPage(1)))));
Bookmark sub_red2 = red.addChild("Red - Page 2");
sub_red2.setAction(Action.createGoto(Destination.createFit((Page)(doc.getPage(2)))));
Bookmark sub_red3 = red.addChild("Red - Page 3");
sub_red3.setAction(Action.createGoto(Destination.createFit((Page)(doc.getPage(3)))));
Bookmark sub_red4 = sub_red3.addChild("Red - Page 4");
sub_red4.setAction(Action.createGoto(Destination.createFit((Page)(doc.getPage(4)))));
Bookmark sub_red5 = sub_red3.addChild("Red - Page 5");
sub_red5.setAction(Action.createGoto(Destination.createFit((Page)(doc.getPage(5)))));
Bookmark sub_red6 = sub_red3.addChild("Red - Page 6");
sub_red6.setAction(Action.createGoto(Destination.createFit((Page)(doc.getPage(6)))));
// Example of how to find and delete a bookmark by title text.
Bookmark foo = doc.getFirstBookmark().find("foo");
if (foo.isValid())
{
foo.delete();
}
else
{
throw new Exception("Foo is not Valid");
}
Bookmark bar = doc.getFirstBookmark().find("bar");
if (bar.isValid())
{
bar.delete();
}
else
{
throw new Exception("Bar is not Valid");
}
// Adding color to Bookmarks. Color and other formatting can help readers
// get around more easily in large PDF documents.
red.setColor(1, 0, 0);
green.setColor(0, 1, 0);
green.setFlags(2); // set bold font
blue.setColor(0, 0, 1);
blue.setFlags(3); // set bold and itallic
doc.save((output_path + "bookmark.pdf"), 0, null);
doc.close();
}
catch(Exception e)
{
System.out.println(e);
}
// The following example illustrates how to traverse the outline tree using
// Bookmark navigation methods: Bookmark.GetNext(), Bookmark.GetPrev(),
// Bookmark.GetFirstChild () and Bookmark.GetLastChild ().
try
{
// Open the document that was saved in the previous code sample
PDFDoc doc=new PDFDoc((output_path + "bookmark.pdf"));
doc.initSecurityHandler();
Bookmark root = doc.getFirstBookmark();
PrintOutlineTree(root);
doc.close();
System.out.println("Done.");
}
catch(Exception e)
{
System.out.println(e);
}
// The following example illustrates how to create a Bookmark to a page
// in a remote document. A remote go-to action is similar to an ordinary
// go-to action, but jumps to a destination in another PDF file instead
// of the current file. See Section 8.5.3 'Remote Go-To Actions' in PDF
// Reference Manual for details.
try
{
// Open the document that was saved in the previous code sample
PDFDoc doc=new PDFDoc((output_path + "bookmark.pdf"));
doc.initSecurityHandler();
// Create file specification (the file reffered to by the remote bookmark)
Obj file_spec = doc.createIndirectDict();
file_spec.putName("Type", "Filespec");
file_spec.putString("F", "bookmark.pdf");
FileSpec spec=new FileSpec(file_spec);
Action goto_remote = Action.createGotoRemote(spec, 5, true);
Bookmark remoteBookmark1 = Bookmark.create(doc, "REMOTE BOOKMARK 1");
remoteBookmark1.setAction(goto_remote);
doc.addRootBookmark(remoteBookmark1);
// Create another remote bootmark, but this time using the low-level SDF/Cos API.
// Create a remote action
Bookmark remoteBookmark2 = Bookmark.create(doc, "REMOTE BOOKMARK 2");
doc.addRootBookmark(remoteBookmark2);
Obj gotoR = remoteBookmark2.getSDFObj().putDict("A");
{
gotoR.putName("S","GoToR"); // Set action type
gotoR.putBool("NewWindow", true);
// Set the file specification
gotoR.put("F", file_spec);
// jump to the first page. Note that pages are indexed from 0.
Obj dest = gotoR.putArray("D"); // Set the destination
dest.pushBackNumber(9);
dest.pushBackName("Fit");
}
doc.save((output_path + "bookmark_remote.pdf"), SDFDoc.e_linearized, null);
doc.close();
}
catch(Exception e)
{
System.out.println(e);
}
PDFNet.terminate();
}
static void PrintIndent(Bookmark item) throws PDFNetException
{
int ident = item.getIndent() - 1;
for (int i=0; i<ident; ++i) System.out.print( " ");
}
// Prints out the outline tree to the standard output
static void PrintOutlineTree(Bookmark item) throws PDFNetException
{
for (; item.isValid(); item=item.getNext())
{
PrintIndent(item);
System.out.print((item.isOpen() ? "- " : "+ ") + item.getTitle() + " ACTION -> ");
// Print Action
Action action = item.getAction();
if (action.isValid())
{
if (action.getType() == Action.e_GoTo)
{
Destination dest = action.getDest();
if (dest.isValid())
{
Page page = dest.getPage();
System.out.println("GoTo Page #" + page.getIndex());
}
}
else
{
System.out.println("Not a 'GoTo' action");
}
}
else
{
System.out.println("NULL");
}
if (item.hasChildren()) // Recursively print children sub-trees
{
PrintOutlineTree(item.getFirstChild());
}
}
}
}
Related
I have a placeholder image in my docx file and I want to replace it with new image. The problem is - the placeholder image has an attribute "in front of text", but the new image has not. As a result the alignment breaks. Here is my code snippet and the docx with placeholder and the resulting docx.
.......
replaceImage(doc, "Рисунок 1", qr, 50, 50);
ByteArrayOutputStream out = new ByteArrayOutputStream();
doc.write(out);
out.close();
return out.toByteArray();
}
}
public XWPFDocument replaceImage(XWPFDocument document, String imageOldName, byte[] newImage, int newImageWidth, int newImageHeight) throws Exception {
try {
int imageParagraphPos = -1;
XWPFParagraph imageParagraph = null;
List<IBodyElement> documentElements = document.getBodyElements();
for (IBodyElement documentElement : documentElements) {
imageParagraphPos++;
if (documentElement instanceof XWPFParagraph) {
imageParagraph = (XWPFParagraph) documentElement;
if (imageParagraph.getCTP() != null && imageParagraph.getCTP().toString().trim().contains(imageOldName)) {
break;
}
}
}
if (imageParagraph == null) {
throw new Exception("Unable to replace image data due to the exception:\n"
+ "'" + imageOldName + "' not found in in document.");
}
ParagraphAlignment oldImageAlignment = imageParagraph.getAlignment();
// remove old image
boolean isDeleted = document.removeBodyElement(imageParagraphPos);
// now add new image
XWPFParagraph newImageParagraph = document.createParagraph();
XWPFRun newImageRun = newImageParagraph.createRun();
newImageParagraph.setAlignment(oldImageAlignment);
try (InputStream is = new ByteArrayInputStream(newImage)) {
newImageRun.addPicture(is, XWPFDocument.PICTURE_TYPE_JPEG, "qr",
Units.toEMU(newImageWidth), Units.toEMU(newImageHeight));
}
// set new image at the old image position
document.setParagraph(newImageParagraph, imageParagraphPos);
// NOW REMOVE REDUNDANT IMAGE FORM THE END OF DOCUMENT
document.removeBodyElement(document.getBodyElements().size() - 1);
return document;
} catch (Exception e) {
throw new Exception("Unable to replace image '" + imageOldName + "' due to the exception:\n" + e);
}
}
The image with placeholder:
enter image description here
The resulting image:
enter image description here
To replace picture templates in Microsoft Word there is no need to delete them.
The storage is as so:
The embedded media is stored as binary file. This is the picture data (XWPFPictureData). In the document a picture element (XWPFPicture) links to that picture data.
The XWPFPicture has settings for position, size and text flow. These dont need to be changed.
The changing is needed in XWPFPictureData. There one can replace the old binary content with the new.
So the need is to find the XWPFPicture in the document. There is a non visual picture name stored while inserting the picture in the document. So if one knows that name, then this could be a criteriea to find the picture.
If found one can get the XWPFPictureData from found XWPFPicture. There is method XWPFPicture.getPictureDatato do so. Then one can replace the old binary content of XWPFPictureData with the new. XWPFPictureData is a package part. So it has PackagePart.getOutputStream to get an output stream to write to.
Following complete example shows that all.
The source.docx needs to have an embedded picture named "QRTemplate.jpg". This is the name of the source file used while inserting the picture into Word document using Word GUI. And there needs to be a file QR.jpg which contains the new content.
The result.docx then has all pictures named "QRTemplate.jpg" replaced with the content of the given file QR.jpg.
import java.io.FileInputStream;
import java.io.OutputStream;
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;
public class WordReplacePictureData {
static XWPFPicture getPictureByName(XWPFRun run, String pictureName) {
if (pictureName == null) return null;
for (XWPFPicture picture : run.getEmbeddedPictures()) {
String nonVisualPictureName = picture.getCTPicture().getNvPicPr().getCNvPr().getName();
if (pictureName.equals(nonVisualPictureName)) {
return picture;
}
}
return null;
}
static void replacePictureData(XWPFPictureData source, String pictureResultPath) {
try ( FileInputStream in = new FileInputStream(pictureResultPath);
OutputStream out = source.getPackagePart().getOutputStream();
) {
byte[] buffer = new byte[2048];
int length;
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
static void replacePicture(XWPFRun run, String pictureName, String pictureResultPath) {
XWPFPicture picture = getPictureByName(run, pictureName);
if (picture != null) {
XWPFPictureData source = picture.getPictureData();
replacePictureData(source, pictureResultPath);
}
}
public static void main(String[] args) throws Exception {
String templatePath = "./source.docx";
String resultPath = "./result.docx";
String pictureTemplateName = "QRTemplate.jpg";
String pictureResultPath = "./QR.jpg";
try ( XWPFDocument document = new XWPFDocument(new FileInputStream(templatePath));
FileOutputStream out = new FileOutputStream(resultPath);
) {
for (IBodyElement bodyElement : document.getBodyElements()) {
if (bodyElement instanceof XWPFParagraph) {
XWPFParagraph paragraph = (XWPFParagraph)bodyElement;
for (XWPFRun run : paragraph.getRuns()) {
replacePicture(run, pictureTemplateName, pictureResultPath);
}
}
}
document.write(out);
}
}
}
I have a dirty workaround. Since the text block on the right side of the image is static, I replaced the text with screen-shot on the original docx. And now, when the placeholder image been substituted by the new image, everything is rendered as expected.
Hello I'm creating javafx app with iText. I have html editor to write text and I want to create pdf from it. Everything works but when I have a really long line that is wrapped in html editor, in pdf it isn't wrapped, its out of page, how can I set wrapping page? here is my code:
PdfWriter writer = null;
try {
writer = new PdfWriter("doc.pdf");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//Initialize PDF document
PdfDocument pdf = new PdfDocument(writer);
// Initialize document
Document document = new Document(pdf, PageSize.A4);
List<IElement> list = null;
try {
list = HtmlConverter.convertToElements(editor.getHtmlText());
} catch (IOException e) {
e.printStackTrace();
}
// add elements to document
for (IElement p : list) {
document.add((IBlockElement) p);
}
// close document
document.close();
I also want to set line spacing for this text
Thank you for help
I don't get any errors for the following code:
public class stack_overflow_0008 extends AbstractSupportTicket{
private static String LONG_PIECE_OF_TEXT =
"Once upon a midnight dreary, while I pondered, weak and weary," +
"Over many a quaint and curious volume of forgotten lore—" +
"While I nodded, nearly napping, suddenly there came a tapping," +
"As of some one gently rapping, rapping at my chamber door." +
"Tis some visitor,” I muttered, “tapping at my chamber door—" +
"Only this and nothing more.";
public static void main(String[] args)
{
PdfWriter writer = null;
try {
writer = new PdfWriter(getOutputFile());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//Initialize PDF document
PdfDocument pdf = new PdfDocument(writer);
// Initialize document
Document document = new Document(pdf, PageSize.A4);
List<IElement> list = null;
try {
list = HtmlConverter.convertToElements("<p>" + LONG_PIECE_OF_TEXT + "</p>");
} catch (IOException e) {
e.printStackTrace();
}
for (IElement p : list) {
document.add((IBlockElement) p);
}
document.close();
}
}
The document is a single (A4) page PDF with one string neatly wrapped.
I think perhaps the content of your string is to blame?
Could you post the HTML you get from this editor object?
Update:
Using the code from this answer on the HTML shared in a new comment to the question, I get the following result:
As you can see, the content is distributed over two lines. No content "falls off the page."
I am using Apache POI XWPF components and java, to extract data from a .xml file into a word document. So far so good, but I am struggling to create a table of contents.
I have to create a table of contents at the start of the method and then I update it at the end to get all the new headers. Currently I use doc.createTOC(), where doc is a variable created from XWPFDocument, to create the table at the start and then I use doc.enforceUpdateFields() to update everything at the end of the document. But when I open the document after I ran the program, the table of contents is empty, but the navigation panel does include some of the headers I specified.
A comment recommended that I include some code. So i started off by create the document from a template:
XWPFDocument doc = new XWPFDocument(new FileInputStream("D://Template.docx"));
I then create a table of contents:
doc.createTOC();
Then throughout the method I add headers to the document:
XWPFParagraph documentControlHeading = doc.createParagraph();
documentControlHeading.setPageBreak(true);
documentControlHeading.setAlignment(ParagraphAlignment.LEFT);
documentControlHeading.setStyle("Tier1Header");
After all the headers are added, I want to update the document so that all the new headers will appear in the table of contents. I do this buy using the following command:
doc.enforceUpdateFields();
Hmmm... I am looking at the createTOC() method code, and it appears that it looks for styles that look like Heading #. So Tier1Header would not be found. Try creating your text first, and use styles like Heading 1 for your headings. Then add the TOC using createTOC(). It should find all the headings when the TOC is created. I do not know if enforceUpdateFields() affects the TOC.
//Your docx template should contain the following or something similar text //which will be searched for and replaced with a WORD TOC.
//${TOC}
public static void main(String[] args) throws IOException, OpenXML4JException {
XWPFDocument docTemplate = null;
try {
File file = new File(PATH_TO_FILE); //"C:\\Reports\\Template.docx";
FileInputStream fis = new FileInputStream(file);
docTemplate = new XWPFDocument(fis);
generateTOC(docTemplate);
saveDocument(docTemplate);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (docTemplate != null) {
docTemplate.close();
}
}
}
private static void saveDocument(XWPFDocument docTemplate) throws FileNotFoundException, IOException {
FileOutputStream outputFile = null;
try {
outputFile = new FileOutputStream(OUTFILENAME);
docTemplate.write(outputFile);
} finally {
if (outputFile != null) {
outputFile.close();
}
}
}
public static void generateTOC(XWPFDocument document) throws InvalidFormatException, FileNotFoundException, IOException {
String findText = "${TOC}";
String replaceText = "";
for (XWPFParagraph p : document.getParagraphs()) {
for (XWPFRun r : p.getRuns()) {
int pos = r.getTextPosition();
String text = r.getText(pos);
if (text != null && text.contains(findText)) {
text = text.replace(findText, replaceText);
r.setText(text, 0);
addField(p, "TOC \\o \"1-3\" \\h \\z \\u");
break;
}
}
}
}
private static void addField(XWPFParagraph paragraph, String fieldName) {
CTSimpleField ctSimpleField = paragraph.getCTP().addNewFldSimple();
// ctSimpleField.setInstr(fieldName + " \\* MERGEFORMAT ");
ctSimpleField.setInstr(fieldName);
ctSimpleField.addNewR().addNewT().setStringValue("<<fieldName>>");
}
This is the code of createTOC(), obtained by inspecting XWPFDocument.class:
public void createTOC() {
CTSdtBlock block = getDocument().getBody().addNewSdt();
TOC toc = new TOC(block);
for (XWPFParagraph par : this.paragraphs) {
String parStyle = par.getStyle();
if ((parStyle != null) && (parStyle.startsWith("Heading"))) try {
int level = Integer.valueOf(parStyle.substring("Heading".length())).intValue();
toc.addRow(level, par.getText(), 1, "112723803");
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
}
As you can see, it adds to the TOC all paragraphs having styles named "HeadingX", with X being a number. But, unfortunately, that's not sufficent. The method, in fact, is bugged/uncomplete in its implementation.
The page number passed to addRow() is always 1, it's not even calculated.
So, at the end, you will have a TOC with all your paragraphs and the trailing dots giving the proper indentation, but the pages will be always equal to "1".
EDIT
...but, there's a solution here.
I am currently modifying the logic for generating a PDF report, using itext package (itext-2.1.7.jar). The report is generated first, then - table of contents (TOC) and then TOC is inserted into the report file, using PdfStamper and it's method - replacePage:
PdfStamper stamper = new PdfStamper(new PdfReader(finalFile.getAbsolutePath()),
new FileOutputStream(reportFile));
stamper.replacePage(new PdfReader(tocFile.getAbsolutePath()), 1, 2);
However, I wanted TOC to also have internal links to point to the right chapter. Using replacePage() method, unfortunately, removes all links and annotations, so I tried another way. I've used a method I've found to merge the report file and the TOC file. It did the trick, meaning that the links were now preserved, but they don't seem to work, the user can click them, but nothing happens. I've placed internal links on other places in the report file, for testing purposes, and they seem to work, but they don't work when placed on the actual TOC. TOC is generated separately, here's how I've created the links (addLeadingDots is a local method, not pertinent to the problem):
Chunk anchor = new Chunk(idx + ". " + chapterName + getLeadingDots(chapterName, pageNumber) + " " + pageNumber, MainReport.FONT12);
String anchorLocation = idx+chapterName.toUpperCase().replaceAll("\\s","");
anchor.setLocalGoto(anchorLocation);
line = new Paragraph();
line.add(anchor);
line.setLeading(6);
table.addCell(line);
And this is how I've created anchor destinations on the chapters in the report file:
Chunk target = new Chunk(title.toUpperCase(), font);
String anchorDestination = title.toUpperCase().replace(".", "").replaceAll("\\s","");
target.setLocalDestination(anchorDestination);
Paragraph p = new Paragraph(target);
p.setAlignment(Paragraph.ALIGN_CENTER);
doc.add(p);
Finally, here's the method that supposed to merge report file and the TOC:
public static void mergePDFs(String originalFilePath, String fileToInsertPath, String outputFile, int location) {
PdfReader originalFileReader = null;
try {
originalFileReader = new PdfReader(originalFilePath);
} catch (IOException ex) {
System.out.println("ITextHelper.addPDFToPDF(): can't read original file: " + ex);
}
PdfReader fileToAddReader = null;
try {
fileToAddReader = new PdfReader(fileToInsertPath);
} catch (IOException ex) {
System.out.println("ITextHelper.addPDFToPDF(): can't read fileToInsert: " + ex);
}
if (originalFileReader != null && fileToAddReader != null) {
int numberOfOriginalPages = originalFileReader.getNumberOfPages();
Document document = new Document();
PdfCopy copy = null;
try {
copy = new PdfCopy(document, new FileOutputStream(outputFile));
document.open();
for (int i = 1; i <= numberOfOriginalPages; i++) {
if (i == location) {
for (int j = 1; j <= fileToAddReader.getNumberOfPages(); j++) {
copy.addPage(copy.getImportedPage(fileToAddReader, j));
}
}
copy.addPage(copy.getImportedPage(originalFileReader, i));
}
document.close();
} catch (DocumentException | FileNotFoundException ex) {
m_logger.error("ITextHelper.addPDFToPDF(): can't read output location: " + ex);
} catch (IOException ex) {
m_logger.error(Level.SEVERE, ex);
}
}
}
So, the main question is, what happens to the links, after both pdf documents are merged and how to make them work? I'm not too experienced with itext, so any help is appreciated.
Thanks for your help
Im new to this PDFBOX. I hve one pdf file, which contain 60 pages. Im using Apache PDFBox-app-1.8.10. jar splitting up the PDF files.
public class SplitDemo {
public static void main(String[] args) throws IOException {
JButton open = new JButton();
JFileChooser fc = new JFileChooser();
fc.setCurrentDirectory(new java.io.File("C:/Users"));
fc.setDialogTitle("Select PDF");
if(fc.showOpenDialog(open)== JFileChooser.APPROVE_OPTION)
{
}
String a = null;
a = fc.getSelectedFile().getAbsolutePath();
PDDocument document = new PDDocument();
document = PDDocument.load(a);
// Create a Splitter object
Splitter splitter = new Splitter();
// We need this as split method returns a list
List<PDDocument> listOfSplitPages;
// We are receiving the split pages as a list of PDFs
listOfSplitPages = splitter.split(document);
// We need an iterator to iterate through them
Iterator<PDDocument> iterator = listOfSplitPages.listIterator();
// I am using variable i to denote page numbers.
int i = 1;
while(iterator.hasNext()){
PDDocument pd = iterator.next();
try{
// Saving each page with its assumed page no.
pd.save("G://PDFCopy/Page " + i++ + ".pdf");
} catch (COSVisitorException anException){
// Something went wrong with a PDF object
System.out.println("Something went wrong with page " + (i-1) + "\n Here is the error message" + anException);
}
}
}
}
**In PDFCopy Folder i hve list of pdf files. How can I convert all pdf to excel format and need to save it in the target folder. i am fully confused in this conversion. **