Why does adding w:drawing cause corrupted file - docx4j

I'm trying to add a chart to a docx file using docx4j. I generated what i wanted in Word and with the help of the docx4j webapp i was able to get the corresponding java code. Unfortunately the generated docx file is said to be corrupted by Word. When trying to debug, I realised that if I commented out the line that added the drawing to the run, the file became readable. I can't figure out what's wrong. Below is my code and the link to what i tried to reproduce http://www.filedropper.com/graphique .
I'm using docx4j 8.2 and office 2016
Chart chartPart = new Chart();
Relationship chartRelationship = wordMLPackage.getMainDocumentPart().addTargetPart(chartPart);
chartPart.setJaxbElement(ChartSpace.createChartSpace());
DefaultXmlPart colorPart = new DefaultXmlPart(new PartName("/word/charts/colors1.xml"));
colorPart.setContentType(new ContentType("application/vnd.ms-office.chartcolorstyle+xml"));
colorPart.setRelationshipType("http://schemas.microsoft.com/office/2011/relationships/chartColorStyle");
chartPart.addTargetPart(colorPart);
colorPart.setDocument(new FileInputStream(new File("colors1.xml")));
DefaultXmlPart stylePart = new DefaultXmlPart(new PartName("/word/charts/style1.xml"));
stylePart.setContentType(new ContentType("application/vnd.ms-office.chartstyle+xml"));
stylePart.setRelationshipType("http://schemas.microsoft.com/office/2011/relationships/chartStyle");
chartPart.addTargetPart(stylePart);
stylePart.setDocument(new FileInputStream(new File("style1.xml")));
EmbeddedPackagePart embeddedPackagePart = new EmbeddedPackagePart(
new PartName("/word/embeddings/Microsoft_Excel_Worksheet.xlsx"));
embeddedPackagePart.setContentType(
new ContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
chartPart.addTargetPart(embeddedPackagePart);
embeddedPackagePart.setBinaryData(new java.io.FileInputStream(new File("data.xlsx")));
mainDocumentPart.getContent().add(addGraphic(factory, chartRelationship.getId()));
public static P addGraphic(ObjectFactory factory, String chartId) throws JAXBException {
P p = factory.createP();
// Create object for r
R r = factory.createR();
p.getContent().add(r);
// Create object for drawing (wrapped in JAXBElement)
Drawing drawing = factory.createDrawing();
JAXBElement<org.docx4j.wml.Drawing> drawingWrapped = factory.createRDrawing(drawing);
r.getContent().add(drawingWrapped);
org.docx4j.dml.wordprocessingDrawing.ObjectFactory dmlwordprocessingDrawingObjectFactory = new org.docx4j.dml.wordprocessingDrawing.ObjectFactory();
// Create object for inline
Inline inline = dmlwordprocessingDrawingObjectFactory.createInline();
drawing.getAnchorOrInline().add(inline);
org.docx4j.dml.ObjectFactory dmlObjectFactory = new org.docx4j.dml.ObjectFactory();
// Create object for graphic
Graphic graphic = dmlObjectFactory.createGraphic();
inline.setGraphic(graphic);
// Create object for graphicData
GraphicData graphicdata = dmlObjectFactory.createGraphicData();
graphic.setGraphicData(graphicdata);
graphicdata.setUri("http://schemas.openxmlformats.org/drawingml/2006/chart");
org.docx4j.dml.chart.ObjectFactory dmlchartObjectFactory = new org.docx4j.dml.chart.ObjectFactory();
// Create object for chart (wrapped in JAXBElement)
CTRelId relid = dmlchartObjectFactory.createCTRelId();
JAXBElement<org.docx4j.dml.chart.CTRelId> relidWrapped = dmlchartObjectFactory.createChart(relid);
graphicdata.getAny().add(relidWrapped);
relid.setId(chartId);
// Create object for cNvGraphicFramePr
CTNonVisualGraphicFrameProperties nonvisualgraphicframeproperties = dmlObjectFactory
.createCTNonVisualGraphicFrameProperties();
inline.setCNvGraphicFramePr(nonvisualgraphicframeproperties);
// Create object for extent
CTPositiveSize2D positivesize2d = dmlObjectFactory.createCTPositiveSize2D();
inline.setExtent(positivesize2d);
positivesize2d.setCx(5486400);
positivesize2d.setCy(3200400);
// Create object for effectExtent
CTEffectExtent effectextent = dmlwordprocessingDrawingObjectFactory.createCTEffectExtent();
inline.setEffectExtent(effectextent);
effectextent.setB(0);
effectextent.setL(0);
effectextent.setT(0);
effectextent.setR(0);
// Create object for docPr
CTNonVisualDrawingProps nonvisualdrawingprops = dmlObjectFactory.createCTNonVisualDrawingProps();
inline.setDocPr(nonvisualdrawingprops);
nonvisualdrawingprops.setDescr("");
nonvisualdrawingprops.setName("Graphique 1");
nonvisualdrawingprops.setId(1);
inline.setDistT(Long.valueOf(0));
inline.setDistB(Long.valueOf(0));
inline.setDistL(Long.valueOf(0));
inline.setDistR(Long.valueOf(0));
// Create object for rPr
RPr rpr = factory.createRPr();
r.setRPr(rpr);
// Create object for noProof
BooleanDefaultTrue booleandefaulttrue = factory.createBooleanDefaultTrue();
rpr.setNoProof(booleandefaulttrue);
return p;
}

Related

Revit: Cannot create dimensions when ViewSection is rotated

I'm trying to set dimension to the elements in a AssemblyInstance. The code operates with coordinates from the first element.
AssemblyInstance ass; //is found and is not null
ViewSection vsec = RevitAuxilaries.CreateAssemblyViewSection(uiapp, ass,
AssemblyDetailViewOrientation.ElevationFront, ElementId.InvalidElementId, 25);
//UIApplication,
AssemblyInstance, AssemblyDetailViewOrientation, TemplateId, scale // created
BoundingBoxXYZ bbox1 = ass.get_BoundingBox(uiapp.ActiveUIDocument.ActiveView);
XYZ ptmid = (bbox1.Max + bbox1.Min) * 0.5;
Element cropboxelm = RevitAuxilaries.GetViewCropBox(uiapp, vsec); //finds CropBox element,
//found
BoundingBoxXYZ bcropbox = vsec.CropBox;
XYZ center = new XYZ(ptmid.X, ptmid.Y, 0.5 * (bcropbox.Max.Z + bcropbox.Min.Z));
Line axis = Line.CreateBound(center, center + XYZ.BasisZ);
RevitAuxilaries.RotateElement2(uiapp, cropboxelm, axis, 0.6981); // UIApplication,, Element,
Line, angle// created
double dw = RevitAuxilaries.GetDimensionFromElement(uiapp, fi, Dimensions.enWidth); //found dw
// = 3.937
ptleft = new XYZ(31.501, -23.3878, 32.4803);
ptrght = new XYZ(31.501 + dw * Math.Cos(0.6981), -23.3878 + dw * Math.Sin(0.6981), 32.4803);
Line ln = RevitAuxilaries.CreateLineFromPoints(uiapp, ptleft, ptrght); //created
ReferenceArray refarr = new ReferenceArray();
refarr.Append(ln.GetEndPointReference(0));
refarr.Append(ln.GetEndPointReference(1));
Dimension dim = null;
using (Transaction trans = new Transaction(uiapp.ActiveUIDocument.Document, "CreADim"))
{
trans.Start();
dim = uiapp.ActiveUIDocument.Document.Create.NewDimension(viewsec, line, refarr);
if (!issame)
{
try
{ dim.ValueOverride = Convert.ToInt32(UnitUtils.Convert(dim.Value.Value,
UnitTypeId.Feet, UnitTypeId.Millimeters)).ToString(); }
catch { }
}
trans.Commit();
uiapp.ActiveUIDocument.RefreshActiveView();
}
ERROR: The direction of dimension is invalid
Error in function checkDir, line 939
What is here wrong?
Have you tried to create the exact same dimension in the exact same context manually through the end user interface? Does that complete as expected? If not, what error message does that generate? If yes, you can analyse the resulting model, its elements and their properties using RevitLookup and possibly discover some required settings that can be added to your API approach.

allow arabic text in pdf table using itext7 (xamarin android)

I have to put my list data in a table in a pdf file. My data has some Arabic words. When my pdf is generated, the Arabic words don't appear. I searched and found that I need itext7.pdfcalligraph so I installed it in my app. I found this code too https://itextpdf.com/en/blog/technical-notes/displaying-text-different-languages-single-pdf-document and tried to do something similar to allow Arabic words in my table but I couldn't figure it out.
This is a trial code before I apply it to my real list:
var path2 = global::Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
filePath = System.IO.Path.Combine(path2.ToString(), "myfile2.pdf");
stream = new FileStream(filePath, FileMode.Create);
PdfWriter writer = new PdfWriter(stream);
PdfDocument pdf2 = new iText.Kernel.Pdf.PdfDocument(writer);
Document document = new Document(pdf2, PageSize.A4);
FontSet set = new FontSet();
set.AddFont("ARIAL.TTF");
document.SetFontProvider(new FontProvider(set));
document.SetProperty(Property.FONT, "Arial");
string[] sources = new string[] { "يوم","شهر 2020" };
iText.Layout.Element.Table table = new iText.Layout.Element.Table(2, false);
foreach (string source in sources)
{
Paragraph paragraph = new Paragraph();
Bidi bidi = new Bidi(source, Bidi.DirectionDefaultLeftToRight);
if (bidi.BaseLevel != 0)
{
paragraph.SetTextAlignment(iText.Layout.Properties.TextAlignment.RIGHT);
}
paragraph.Add(source);
table.AddCell(new Cell(1, 1).SetTextAlignment(iText.Layout.Properties.TextAlignment.CENTER).Add(paragraph));
}
document.Add(table);
document.Close();
I updated my code and added the arial.ttf to my assets folder . i'm getting the following exception:
System.InvalidOperationException: 'FontProvider and FontSet are empty. Cannot resolve font family name (see ElementPropertyContainer#setFontFamily) without initialized FontProvider (see RootElement#setFontProvider).'
and I still can't figure it out. any ideas?
thanks in advance
- C #
I have a similar situation for Turkish characters, and I've followed these
steps :
Create a folder under projects root folder which is : /wwwroot/Fonts
Add OpenSans-Regular.ttf under the Fonts folder
Path for font is => ../wwwroot/Fonts/OpenSans-Regular.ttf
Create font like below :
public static PdfFont CreateOpenSansRegularFont()
{
var path = "{Your absolute path for FONT}";
return PdfFontFactory.CreateFont(path, PdfEncodings.IDENTITY_H, true);
}
and use it like :
paragraph.Add(source)
.SetFont(FontFactory.CreateOpenSansRegularFont()); //set font in here
table.AddCell(new Cell(1, 1)
.SetTextAlignment(iText.Layout.Properties.TextAlignment.CENTER)
.Add(paragraph));
This is how I used font factory for Turkish characters ex: "ü,i,ç,ş,ö"
For Xamarin-Android, you could try
string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var path = Path.Combine(documentsPath, "Fonts/Arial.ttf");
Look i fixed it in java,this may help you:
String font = "your Arabic font";
//the magic is in the next 4 lines:
PdfFontFactory.register(font);
FontProgram fontProgram = FontProgramFactory.createFont(font, true);
PdfFont f = PdfFontFactory.createFont(fontProgram, PdfEncodings.IDENTITY_H);
LanguageProcessor languageProcessor = new ArabicLigaturizer();
//and look here how i used setBaseDirection and don't use TextAlignment ,it will work without it
com.itextpdf.kernel.pdf.PdfDocument tempPdfDoc = new com.itextpdf.kernel.pdf.PdfDocument(new PdfReader(pdfFile.getPath()), TempWriter);
com.itextpdf.layout.Document TempDoc = new com.itextpdf.layout.Document(tempPdfDoc);
com.itextpdf.layout.element.Paragraph paragraph0 = new com.itextpdf.layout.element.Paragraph(languageProcessor.process("الاستماره الالكترونية--الاستماره الالكترونية--الاستماره الالكترونية--الاستماره الالكترونية"))
.setFont(f).setBaseDirection(BaseDirection.RIGHT_TO_LEFT)
.setFontSize(15);

How to add a Graph placeholder and Table Placeholder in PDF template using Aspose.dll

How to add a Graph placeholder and Table placeholder in PDF template using Aspose.dll
Please help me in this area.
You can add a graph to a PDF document by using the code below:
// Create Document instance
Document doc = new Document();
// Add page to pages collection of PDF file
Page page = doc.Pages.Add();
// Create Graph instance
Aspose.Pdf.Drawing.Graph graph = new Aspose.Pdf.Drawing.Graph(100, 400);
// Add graph object to paragraphs collection of page instance
page.Paragraphs.Add(graph);
// Create Rectangle instance
Aspose.Pdf.Drawing.Rectangle rect = new Aspose.Pdf.Drawing.Rectangle(100, 100, 200, 120);
// Specify fill color for Graph object
rect.GraphInfo.FillColor = Aspose.Pdf.Color.Red;
// Add rectangle object to shapes collection of Graph object
graph.Shapes.Add(rect);
dataDir = dataDir + "CreateFilledRectangle_out.pdf";
// Save PDF file
doc.Save(dataDir);
And, you can add a table in a PDF document by using the code below:
// Create Document instance
Aspose.Pdf.Document doc = new Aspose.Pdf.Document();
// Add page to pages collection of PDF file
Page page = doc.Pages.Add();
// Initializes a new instance of the Table
Aspose.Pdf.Table table = new Aspose.Pdf.Table();
// Set the table border color as LightGray
table.Border = new Aspose.Pdf.BorderInfo(Aspose.Pdf.BorderSide.All, .5f, Aspose.Pdf.Color.FromRgb(System.Drawing.Color.LightGray));
// Set the border for table cells
table.DefaultCellBorder = new Aspose.Pdf.BorderInfo(Aspose.Pdf.BorderSide.All, .5f, Aspose.Pdf.Color.FromRgb(System.Drawing.Color.LightGray));
// Create a loop to add 10 rows
for (int row_count = 1; row_count < 10; row_count++)
{
// Add row to table
Aspose.Pdf.Row row = table.Rows.Add();
// Add table cells
row.Cells.Add("Column (" + row_count + ", 1)");
row.Cells.Add("Column (" + row_count + ", 2)");
row.Cells.Add("Column (" + row_count + ", 3)");
}
// Add table object to first page of document
doc.Pages[1].Paragraphs.Add(table);
dataDir = dataDir + "document_with_table_out.pdf";
// Save updated document containing table object
doc.Save(dataDir);
You may visit following links for further information on these topics.
Working with Graphs
Working with Tables
P.S: I work with Aspose as Developer Evangelist.

PDFBox inserting data into multi-page doc

I'm trying to create a simple pdf multi-page document with fields in it. To do that I have a template pdf which in the code I clone this template as many times as needed in order to create the document itself.
The problem comes with inserting some data in it. The type of data I try to insert to the document is not supposed to change across the pages. Rather than that, it stays static in all pages, Like the "Pages" digit that represents the number of the pages that this document contains.
Now, Inside my template pdf I have some text fields like, for instance, "Shipper1" and "Pages". I want to be able to insert my data into this text fields so that all the pages in the document will have this values in their "Shipper1" and "Pages" fields.
My code currently does that only on the first page. It shows the data perfectly. On the other hand, when I go to another page, the data isn't shown there. It's just displays an empty field.
Here is the code where I initiate the pdf document:
static void initiatePdf() {
// Initiate a new PDF Box object and get the acro form from it
File file = new File(Constants.Paths.EMPTY_DOC)
PDDocument tempDoc
Evaluator evaluator = new Evaluator(metaHolder)
int numPages = evaluator.getNumOfPagesRequired(objects)
FieldRenamer renamer = new FieldRenamer()
PDResources res = new PDResources()
COSDictionary acroFormDict = new COSDictionary()
List<PDField> fields = []
Closure isFieldExist = {List<PDField> elements, String fieldName ->
elements.findAll{it.getFullyQualifiedName() == fieldName}.size() > 0
}
for(int i = 0; i < numPages; i++) {
tempDoc = new PDDocument().load(file)
PDDocumentCatalog docCatalog = tempDoc.getDocumentCatalog()
PDAcroForm acroForm = docCatalog.acroForm
PDPage page = (PDPage) docCatalog.getPages().get(0)
renamer.setCurrentForm(acroForm)
if(i == 0) {
res = acroForm.getDefaultResources()
acroFormDict.mergeInto(acroForm.getCOSObject())
renamer.renameFields(1)
} else
renamer.renameFields(i*10+1)
List<PDField> newFields = acroForm.fields.findAll { PDField newField ->
isFieldExist(fields, newField.getFullyQualifiedName()) == false
}
fields.addAll(newFields)
document.addPage(page)
}
PDAcroForm acroForm = new PDAcroForm(document, acroFormDict);
acroForm.setFields(fields)
acroForm.setDefaultResources(res);
document.documentCatalog.setAcroForm(acroForm)
}
A couple of things first:
metaHolder instance holds the information about all
the fields that reside inside the acro form. the info is: Field Name, Field Widget Width, Field Font and Font size
evaluator is just and instance of the Evaluator class. Its purpose is to analyze the dynamic data and decide how many pages will take to contain all that text data.
Here is where I try to populate the fields with text:
static void populateData() {
def properties = ["$Constants.Fields.SHIPPER" : "David"]
FieldPopulater populater = new FieldPopulater(document, metaHolder)
populater.populateStaticFields(properties)
}
FieldPopulater class:
package app.components
import app.StringUtils
import app.components.entities.DGObject
import app.components.entities.FieldMeta
import org.apache.pdfbox.pdmodel.PDDocument
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm
import org.apache.pdfbox.pdmodel.interactive.form.PDField
/**
* Created by David on 18/10/2016.
*/
class FieldPopulater {
PDAcroForm acroForm
FormMetaHolder metaHolder
FieldPopulater(PDDocument document, FormMetaHolder metaHolder) {
this.acroForm = document.getDocumentCatalog().acroForm
this.metaHolder = metaHolder
}
void populateStaticFields(properties) {
List<PDField> fields = []
properties.each {fieldName, data ->
FieldMeta fieldMeta = metaHolder.getMetaData(fieldName)
fields = acroForm.fields.findAll { PDField field ->
String currentName = field.getFullyQualifiedName()
char lastChar = currentName[-1]
if(Character.isDigit(lastChar)) {
currentName = currentName.substring(0,currentName.size()-1)
}
currentName == fieldName
}
if(fields.size() > 1) {
int counter = 1
String tempData = data
String currentFitData
while(tempData.isEmpty() != true) {
int maxWords = Utils.getMaxWords(tempData, fieldMeta)
currentFitData = StringUtils.getTextByWords(tempData, maxWords)
tempData = StringUtils.chopTextByWords(tempData, maxWords)
PDField field = fields.find{it.getFullyQualifiedName()[-1] == "$counter"}
field?.setValue(currentFitData)
counter++
}
} else {
PDField tempField = fields[0]
tempField.setValue(data)
}
}
}
}
The result is, in the first page, the field "Shipper" has a value of "David"
In the second page, the field "Shipper" is empty.
Here is an image. First page:
Second page:
What is the problem here?
UPDATE: I tried to add the widgets of every new acro form to the current page so that every field will a few kids widgets that will represent the field, but it still doesn't work.
// All the widgets that are associated with the fields
List<PDAnnotationWidget> widgets = acroForm.fields.collect {PDField field -> field.getWidgets().get(0)}
page.annotations.addAll(widgets)
UPDATE: I also tried to add the current widget of a field to the parent field's collection of widgets. Here is the code:
List<PDAnnotationWidget> widgets = []
// All the widgets that are associated with the fields
acroForm.fields.each {PDField field ->
PDAnnotationWidget widget = field.widgets.get(0)
// Adding the following widget to the page and to the field's list of annotation widgets
widgets.add(widget)
fields.find {it.getFullyQualifiedName() == field.getFullyQualifiedName()}?.widgets.add(widget)
}
page.annotations.addAll(widgets)
What you want is to have sereval visual representations of the same field. This is done by having several annotation widgets for such a field.
In PDF, when a field has only one annotation widget, they share a common dictionary. When it has several, the annotation widgets are in a child list of the field.
When you want several annotation widgets for one field, you need to create the annotation widgets with new PDAnnotationWidget() instead of calling field.getWidgets().get(0) and using that one. These widgets must be added to a list, and this list must be assigned to the field with setWidgets(). And for each widget you must call setRectangle() and setPage() and setParent().
An example for this is in the new CreateMultiWidgetsForm.java example. The setParent() method is not yet available in 2.0.3 (but will be in 2.0.4). In this answer, it is replaced with a call that does the same thing in a less elegant way.
public final class CreateMultiWidgetsForm
{
private CreateMultiWidgetsForm()
{
}
public static void main(String[] args) throws IOException
{
// Create a new document with 2 empty pages.
PDDocument document = new PDDocument();
PDPage page1 = new PDPage(PDRectangle.A4);
document.addPage(page1);
PDPage page2 = new PDPage(PDRectangle.A4);
document.addPage(page2);
// Adobe Acrobat uses Helvetica as a default font and
// stores that under the name '/Helv' in the resources dictionary
PDFont font = PDType1Font.HELVETICA;
PDResources resources = new PDResources();
resources.put(COSName.getPDFName("Helv"), font);
// Add a new AcroForm and add that to the document
PDAcroForm acroForm = new PDAcroForm(document);
document.getDocumentCatalog().setAcroForm(acroForm);
// Add and set the resources and default appearance at the form level
acroForm.setDefaultResources(resources);
// Acrobat sets the font size on the form level to be
// auto sized as default. This is done by setting the font size to '0'
String defaultAppearanceString = "/Helv 0 Tf 0 g";
acroForm.setDefaultAppearance(defaultAppearanceString);
// Add a form field to the form.
PDTextField textBox = new PDTextField(acroForm);
textBox.setPartialName("SampleField");
// Acrobat sets the font size to 12 as default
// This is done by setting the font size to '12' on the
// field level.
// The text color is set to blue in this example.
// To use black, replace "0 0 1 rg" with "0 0 0 rg" or "0 g".
defaultAppearanceString = "/Helv 12 Tf 0 0 1 rg";
textBox.setDefaultAppearance(defaultAppearanceString);
// add the field to the AcroForm
acroForm.getFields().add(textBox);
// Specify 1st annotation associated with the field
PDAnnotationWidget widget1 = new PDAnnotationWidget();
PDRectangle rect = new PDRectangle(50, 750, 250, 50);
widget1.setRectangle(rect);
widget1.setPage(page1);
widget1.getCOSObject().setItem(COSName.PARENT, textBox);
// Specify 2nd annotation associated with the field
PDAnnotationWidget widget2 = new PDAnnotationWidget();
PDRectangle rect2 = new PDRectangle(200, 650, 100, 50);
widget2.setRectangle(rect2);
widget2.setPage(page2);
widget2.getCOSObject().setItem(COSName.PARENT, textBox);
// set green border and yellow background for 1st widget
// if you prefer defaults, just delete this code block
PDAppearanceCharacteristicsDictionary fieldAppearance1
= new PDAppearanceCharacteristicsDictionary(new COSDictionary());
fieldAppearance1.setBorderColour(new PDColor(new float[]{0,1,0}, PDDeviceRGB.INSTANCE));
fieldAppearance1.setBackground(new PDColor(new float[]{1,1,0}, PDDeviceRGB.INSTANCE));
widget1.setAppearanceCharacteristics(fieldAppearance1);
// set red border and green background for 2nd widget
// if you prefer defaults, just delete this code block
PDAppearanceCharacteristicsDictionary fieldAppearance2
= new PDAppearanceCharacteristicsDictionary(new COSDictionary());
fieldAppearance2.setBorderColour(new PDColor(new float[]{1,0,0}, PDDeviceRGB.INSTANCE));
fieldAppearance2.setBackground(new PDColor(new float[]{0,1,0}, PDDeviceRGB.INSTANCE));
widget2.setAppearanceCharacteristics(fieldAppearance2);
List <PDAnnotationWidget> widgets = new ArrayList<PDAnnotationWidget>();
widgets.add(widget1);
widgets.add(widget2);
textBox.setWidgets(widgets);
// make sure the annotations are visible on screen and paper
widget1.setPrinted(true);
widget2.setPrinted(true);
// Add the annotations to the pages
page1.getAnnotations().add(widget1);
page2.getAnnotations().add(widget2);
// set the field value
textBox.setValue("Sample field");
document.save("MultiWidgetsForm.pdf");
document.close();
}
}

Attachments not showing up in pdf document - created using pdfbox

I m trying to attach an swf file to a pdf document. Below is my code (excerpted from the pdfbox-examples). while i can see that the file is attached based on the size of the file - with & without the attachment, I can't see / locate it in the pdf document. I do see textual content correctly displayed. Can someone tell me what I m doing wrong & help me fix the issue?
doc = new PDDocument();
PDPage page = new PDPage();
doc.addPage( page );
PDFont font = PDType1Font.HELVETICA_BOLD;
String inputFileName = "sample.swf";
InputStream fileInputStream = new FileInputStream(new File(inputFileName));
PDEmbeddedFile ef = new PDEmbeddedFile(doc, fileInputStream );
PDPageContentStream contentStream = new PDPageContentStream(doc, page,true,true);
//embedded files are stored in a named tree
PDEmbeddedFilesNameTreeNode efTree = new PDEmbeddedFilesNameTreeNode();
//first create the file specification, which holds the embedded file
PDComplexFileSpecification fs = new PDComplexFileSpecification();
fs.setEmbeddedFile(ef);
//now lets some of the optional parameters
ef.setSubtype( "swf" );
ef.setCreationDate( new GregorianCalendar() );
//now add the entry to the embedded file tree and set in the document.
Map<String, COSObjectable> efMap = new HashMap<String, COSObjectable>();
efMap.put("My first attachment", fs );
efTree.setNames( efMap );
//attachments are stored as part of the "names" dictionary in the document catalog
PDDocumentNameDictionary names = new PDDocumentNameDictionary( doc.getDocumentCatalog() );
names.setEmbeddedFiles( efTree );
doc.getDocumentCatalog().setNames( names );
After struggling with the same thing, I've discovered this is a known issue. Attachments haven't worked for a while I guess.
Here's a link to the issue on the apache forum.
There is a hack suggested here that you can use.
I tried it and it worked!
the other work around i found is after you call setNames on your PDEmbeddedFilesNameTreeNode remove the limits: ((COSDictionary
)efTree.getCOSObject()).removeItem(COSName.LIMITS); ugly hack, but it
works, without having to recompile pdfbox
Attachment works fine with new version of PDFBox 2.0,
public static boolean addAtachement(final String fileName, final String... attachements) {
if (Objects.isNull(fileName)) {
throw new NullPointerException("fileName shouldn't be null");
}
if (Objects.isNull(attachements)) {
throw new NullPointerException("attachements shouldn't be null");
}
Map<String, PDComplexFileSpecification> efMap = new HashMap<String, PDComplexFileSpecification>();
/*
* Load PDF Document.
*/
try (PDDocument doc = PDDocument.load(new File(fileName))) {
/*
* Attachments are stored as part of the "names" dictionary in the
* document catalog
*/
PDDocumentNameDictionary names = new PDDocumentNameDictionary(doc.getDocumentCatalog());
/*
* First we need to get all the existed attachments, after that we
* can add new attachments
*/
PDEmbeddedFilesNameTreeNode efTree = names.getEmbeddedFiles();
if (Objects.isNull(efTree)) {
efTree = new PDEmbeddedFilesNameTreeNode();
}
Map<String, PDComplexFileSpecification> existedNames = efTree.getNames();
if (existedNames == null || existedNames.isEmpty()) {
existedNames = new HashMap<String, PDComplexFileSpecification>();
}
for (String attachement : attachements) {
/*
* Create the file specification, which holds the embedded file
*/
PDComplexFileSpecification fs = new PDComplexFileSpecification();
fs.setFile(attachement);
try (InputStream is = new FileInputStream(attachement)) {
/*
* This represents an embedded file in a file specification
*/
PDEmbeddedFile ef = new PDEmbeddedFile(doc, is);
/* Set some relevant properties of embedded file */
ef.setCreationDate(new GregorianCalendar());
fs.setEmbeddedFile(ef);
/*
* now add the entry to the embedded file tree and set in
* the document.
*/
efMap.put(attachement, fs);
}
}
efTree.setNames(efMap);
names.setEmbeddedFiles(efTree);
doc.getDocumentCatalog().setNames(names);
doc.save(fileName);
return true;
} catch (IOException e) {
System.out.println(e.getMessage());
return false;
}
}
To 'locate' or see an attached file in the PDF, you can't flip through its pages to find any trace of it there (like, an annotation).
In Acrobat Reader 9.x for example, you have to click on the "View Attachments" icon (looking like a paper-clip) on the left sidebar.