I was migrating some code (originally using iText) to use PdfBox for PDF merging. All went fine except creating PDF packages or portfolios. I have to admit I was not aware that this existed until now.
this is a snippet of my code (using iText):
PdfStamper stamper = new PdfStamper(reader, out);
stamper.makePackage(PdfName.T);
stamper.close();
I need this but with PdfBox.
I'm looking into API and docs for both and I can not find a solution atm. Any help would be great.
PS. Sorry if I made impression that I need solution in iText, I need it in PdfBox because migration is going from iText to PdfBox.
As far as I know PDFBox does not contain a single, dedicated method for that task. On the other hand it is fairly easy to use existing generic PDFBox methods to implement it.
First of all, the task is effectively defined to do the equivalent to
stamper.makePackage(PdfName.T);
using PDFBox. That method in iText is documented as:
/**
* This is the most simple way to change a PDF into a
* portable collection. Choose one of the following names:
* <ul>
* <li>PdfName.D (detailed view)
* <li>PdfName.T (tiled view)
* <li>PdfName.H (hidden)
* </ul>
* Pass this name as a parameter and your PDF will be
* a portable collection with all the embedded and
* attached files as entries.
* #param initialView can be PdfName.D, PdfName.T or PdfName.H
*/
public void makePackage( final PdfName initialView )
Thus, we need to change a PDF (fairly minimally) to make it a portable collection with a tiled view.
According to section 12.3.5 "Collections" of ISO 32000-1 (I don't have part two yet) this means we have to add a Collection dictionary to the PDF catalog with a View entry with value T. Thus:
PDDocument pdDocument = PDDocument.load(...);
COSDictionary collectionDictionary = new COSDictionary();
collectionDictionary.setName(COSName.TYPE, "Collection");
collectionDictionary.setName("View", "T");
PDDocumentCatalog catalog = pdDocument.getDocumentCatalog();
catalog.getCOSObject().setItem("Collection", collectionDictionary);
pdDocument.save(...);
Related
I have searched and searched and cannot find the answer to my problem. I've tried many different approaches in my code, but I've hit a wall and I'm not sure where to go from here. I seem to be wanting to do the same thing as these two threads:
Trying to insert an image into a pdf in c#
Add image in an existing PDF with itextsharp
They are very similar and the answer is the same. However, when I use that exact code, the result is a PDF without an image. Here is my code:
using (var existingFileStream = new FileStream(fileNameExisting, FileMode.Open))
using (var newFileStream = new FileStream(fileNameNew, FileMode.Create))
{
var pdfReader = new PdfReader(existingFileStream);
var stamper = new PdfStamper(pdfReader, newFileStream, '\0', true);
var form = stamper.AcroFields;
var fieldKeys = form.Fields.Keys;
foreach (var field in form.Fields)
{
if (field.Key == "form1[0].ec_Bldg_Photo_1[0].ImageField2[0]")
{
PushbuttonField imageField = form.GetNewPushbuttonFromField(field.Key);
imageField.Layout = PushbuttonField.LAYOUT_ICON_ONLY;
imageField.IconReference = null;
imageField.ProportionalIcon = true;
imageField.Image = Image.GetInstance(#"PATH_TO_IMAGE\front.jpg");
form.ReplacePushbuttonField(field.Key, imageField.Field);
}
}
stamper.FormFlattening = false;
stamper.Close();
pdfReader.Close();
}
I have tried to rule out all of the obvious things. My path to the image is correct, the field is indeed a PushbuttonField when I read the existing PDF field and get the field type. If I open the PDF in Adobe Reader and click on the placeholder for the image, it allows me to pick a file from my PC. When I place an image in the file, save, and then read in that PDF, I can then change my code to this:
imageField.ProportionalIcon = false;
And now all of sudden the image is stretched on the saved copy. So I see that it is changing this part but this is when I enter the image manually in Adobe Reader. When I read in the field after I set that image in Adobe Reader and it shows correctly, I see a couple interesting things. The field.Image property IS NULL and the field.IconReference is NOT NULL. When I use the original code to try and insert the image, it is reversed, where Image is NOT NULL but IconReference IS NULL
Any help would be greatly appreciated, thank you!!
EDIT 1: Ok so I didn't see it the first time, but I went back and checked more thoroughly and I did find that key. Here it is:
Several things are at play here.
Usage Rights:
The PDF is digitally signed with a private key owned by Adobe.
You can see this using RUPS here (in your screen shot you didn't go deep enough):
This has two implications:
The signature unlocks special permissions in Adobe Reader, such as the permission to save a filled out form locally.
Making any changes to the original PDF breaks the signature and removes the special permissions leading to an ugly error message in Adobe Reader.
This functionality is deprecated in (and even removed from) PDF 2.0. It's old technology that became obsolete with the emergence of PDF viewers other than Adobe Reader.
My suggestion: remove the usage rights to avoid breaking the signature. See the FAQ entry "Why do I get an error saying that "use of extended features is no longer available"?" iText 7 / iText 5
This is the iText 7 code:
public void removeUsageRights(PdfDocument pdfDoc) {
PdfDictionary perms = pdfDoc.getCatalog().getPdfObject().getAsDictionary(PdfName.Perms);
if (perms == null) {
return;
}
perms.remove(new PdfName("UR"));
perms.remove(PdfName.UR3);
if (perms.size() == 0) {
pdfDoc.getCatalog().remove(PdfName.Perms);
}
}
This is the iText 5 code:
PdfReader reader = new PdfReader(old_file);
if (reader.hasUsageRights()) {
reader.removeUsageRights();
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(new_file));
stamper.close();
}
reader.close();
This is the iText 5 answer.
Hybrid Form:
If you click on the /AcroForm entry, you see this:
There is a /Fields array with references to field dictionaries that are also widget annotations. That means that the document has an AcroForm form inside. However, there is also an /XFA entry with a series of XML snippets. That means that the document has an XFA form inside.
In other words: the same form description is added twice inside. You are changing a button in one form (the AcroForm part), but not in the other (the XFA form) and that leads to inconsistencies.
XFA has been deprecated in PDF 2.0 because there weren't many vendors supporting that technology. It's kind of frustrating to be confronted with forms that use deprecated technology.
My suggestion: I would remove the XFA part. See the FAQ entry "Is it safe to remove XFA?" iText 5 / iText 7
In iText 5, removing XFA is done like this:
AcroFields form = stamper.getAcroFields();
form.removeXfa();
Important: my suggestion is to remove all the deprecated functionality from the PDF, but if the government expects that functionality to be present, then you're out of luck. In that case, you will need to use Adobe software to process the form. If that's the case, you could complain to the government that their requirements lead to a de facto vendor lock-in. By the way: iText Software is also a vendor. It's an open source company that offers open source software under the AGPL license. The AGPL license allows free use under certain circumstances (see How do I make sure my software complies with AGPL: How can I use iText for free?) If you don't meet those requirements, you will have to purchase a commercial license for your use of iText.
I want to use OpenXML SDK 2.0 to do the following:
Open A.docx
Modify the document
Save the modified document as B.docx
A & B would be parameters to a method and they could be the same. Assuming they are not the same, A should not be modified at all.
I cannot see a "SaveAs" method, in fact `WordprocessingDocument" class doesn't really seem to support concept of file location.
How should I do this?
I use a memory stream and pass it to the WordprocessingDocument.Open method. After I'm done changing the document, I just write the bytes to the destination:
var source = File.ReadAllBytes(filename);
using (var ms = new MemoryStream()) {
ms.Write(source, 0, source.Length);
/* settings defined elsewhere */
using (var doc = WordprocessingDocument.Open(ms, true, settings)) {
/* do something to the doc */
}
/* used in File.WriteAllBytes elsewhere */
return ms.ToArray();
}
+1 on the answer already given...
Here is an MSDN article that discusses working with in-memory Open XML documents. I think that you will find it relevant.
http://msdn.microsoft.com/en-us/library/office/ee945362.aspx
I have spent days on this one so I have put my hand up. I am implementing tags in my own component and have followed Elin's instructions on the Joomla site to the letter (27 July 2013). I can get the new tags to save in the TAGS table correctly, but not the UCM or TAG MAP tables as all the standard components do.
I have traced the code all the way through, and compared to the com_contacts, and cannot for the life of me see any difference in my component.
Where should I be looking for where the code updates the other two tables? I know this will end in an embarrassing answer but I am happy to look foolish.
My table does not have meta fields, but I have manually fudged the metadata array in the $data array. Any help is appreciated.
Instructions:http://docs.joomla.org/J3.1:Using_Tags_in_an_Extension
After many days of extra frustration I discovered that for my component I had to include the archived information into my table class, that is not supposed to be required any more.
Add a property
/**
* Indicator that the tags have been changed
*
* #var JHelperTags
* #since 3.1
*/
protected $tagsHelper = null;
This property helps to manage change in tags.
Modify your constructor
Follow this example to modify your consructor which provides substantial reduction in duplicate code.
$this->tagsHelper = new JHelperTags();
$this->tagsHelper->typeAlias = 'com_contact.contact';
Modify your store() method
Management of tagging and associated data is largely handled through the store() method. This provides maximum flexibility for the handling of tags across many extensions.
If you don't have a store() method you will need to add one. The assumption is that tables will inherit from JTable.
The handling involves preStoreProcess(), a call to the parent store() method, and then a postStoreProcess().
$this->tagsHelper->preStoreProcess($this);
$result = parent::store($updateNulls);
return $result && $this->tagsHelper->postStoreProcess($this);
Using SSJS I've successfully created a PDF using the simple HTML parser that comes with iText but the simple HTML parser doesn't respect CSS and is very limited. I downloaded the XMLWorker class from the iText site and have tried to use that instead but my knowledge of working out how to call Java packages is too limited. All the examples I can find use Java and refer to the classes directly, eg.
Document newPDF = new Document();
But in SSJS we have to use dot notation, eg.
var newPDF:com.itextpdf.text.Document = new com.itextpdf.text.Document();
This - I think - is where I stumble. My code looks like this:
function createLPO2(pReqDoc:NotesDocument) {
importPackage(com.itextpdf);
//importPackage(com.itextpdf.tool.xml.XMLWorkerHelper);
importPackage(java.io);
var con = facesContext.getExternalContext();
var response:com.ibm.xsp.webapp.XspHttpServletResponse = con.getResponse();
response.setContentType("application/pdf");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", -1);
response.setHeader("Content-Disposition","attachment; filename=\"LPO_" + pReqDoc.getItemValueString("RequisitionNo") + ".pdf\"");
var newPDF:com.itextpdf.text.Document = new com.itextpdf.text.Document();
var writer = com.itextpdf.text.pdf.PdfWriter.getInstance(newPDF,response.getOutputStream());
var xmlWorkerHelper = com.itextpdf.tool.xml.XMLWorkerHelper.getInstance();
var strHTML = getTestHTML(); //this is the HTML used in the examples on the iText site
xmlWorkerHelper.parseXHtml(writer, newPDF, new java.io.StringReader(strHTML));
newPDF.close();
writer.close();
facesContext.responseComplete();
}
If I run this script as it is I get a script error on the Domino console. If I remove the comment on the line importPackage(com.itextpdf.tool.xml.XMLWorkerHelper); it gives a completely different error. I suspect I have to import the XMLWorkerHelper package and not just the com.itextpdf package. I thought if I opened the jar file using a tool like 7-zip I could work out the path, which is how I arrived at com.itextpdf.tool.xml.XMLWorkerHelper
Is this right? If so, why does my script fail?
Rob,
seriously, don't try to do that in SSJS. iText is all Java, if you try to mangle with it in a different language it will stress you out. Create a wrapper class that has a method that takes an OutputStream and whatever data (Document, View etc) you need. Obtain the OutputStream in your SSJS and call the function. Look for the XAgent XSnippet on OpenNTF and my blog series (the last two are missing - bear with me) on PDF creation.
One word of caution: iText is GPL, so you either GPL your software too, buy a commercial iText license or look for alternatives like Apache PDFBox or Apache FOP. Ah the second caution: HTML to PDF is a Pita. You could look at a commercial tool like e.g. from Swing software (or change your approach)
I am building a solution that allows users to pick and chose sections from a Word template, populate those sections with content from a database, and assemble the 1k
new data into a new .docx document
So far, I have successful methodologies for locating content and transplanting that content into a new document. I am using the OpenXML SDK 2.0 to locate content by Styles and Content Controls. I am able to create IEnumerable objects containing elements such as Paragraphs, SdtBlocks, Run, etc.
I need to find an elegant way to serialize these element blocks so I can store them as whole blocks of type VARBINARY in a SQL 2005 database. Can someone please point me to a viable example for serializing these OpenXML parts/elements?
I am working on Excel at the moment but I think your problem is similar in nature.
From the code below I can extract the XML code of the row and then store it.
private string GetContents(uint rowIndex)
{
return GetExistingRow(rowIndex).OuterXml;
}
private Row GetExistingRow(uint rowIndex)
{
return SheetData.
Elements<Row>().
Where(r => r.RowIndex == rowIndex).
FirstOrDefault();
}
please note the SheetData object is extracted as
this.SheetData = WorksheetPart.Worksheet.GetFirstChild<SheetData>()
I hope this helps.