I'm trying to create content controls using the OpenXML SDK so that a different program can then pick up the document and edit it as required.
I manage to create Text Content Controls successfully but picture content controls have proven to be more challenging.
The closest I got was with the following code:
var sdtCBlock = new SdtContentBlock(new Paragraph(new Run()));
var sdtPr = new SdtProperties(
new SdtAlias {Val = "" },
new Tag {Val = ""},
new SdtContentPicture(),
new DataBinding {XPath = ""}
);
wordDoc.MainDocumentPart.Document.Body.AppendChild(new SdtCell(sdtPr, sdtCBlock));
This creates an empty content control that word recognises as a picture:
Empty picture content control
The problem is that the second program will not insert the picture into this content control as is. However, if we open the word file manually and click on the content control to pop up the blue square template then the second program successfully detects and changes the image in the content control.
Content control with blue square template after being manually clicked on
Am I doing something wrong? And how can I use the OpenXml code to generate the placeholder with the template in order to be picked up by the second program?
Related
I am trying to create a form field in a PDF where the user can insert an image file and save the document so that the image is persistent (in a new PDF document, as opposed to altering an existing document). I know this is possible, because I've seen it done in other PDFs, but I can't work out how it's supposed to be done in iText 7 for .NET/C#.
I found this on Google, which seems to at least provide the JavaScript and outline of a solution, but I don't know how to edit the "Layout" of an iText PdfButtonFormField object. I have also tried this answer from the iText website, but it's geared towards adding to an existing document, and I couldn't get it to work anyway (some more elusive System.NullReferenceException errors).
Using the idea of creating a button and replacing the image, so far I have tried:
PdfWriter writer = new PdfWriter("myfile.pdf");
PdfDocument document = new PdfDocument(writer);
PdfPage pdfPage = document.AddNewPage(PageSize.A4);
PdfCanvas canvas = new PdfCanvas(pdfPage);
PdfAcroForm form = canvas.GetForm();
PdfButtonFormField button = PdfFormField.CreateButton(document, new Rectangle(50, 50), 0);
button.SetAction(PdfAction.CreateJavaScript("event.target.buttonImportIcon();"));
form.AddField(button); // <-- Error on this line
document.Close();
writer.Close();
In the hope that the buttonImportIcon() would be enough to override the buttons appearance. But I get a System.NullReferenceException: 'Object reference not set to an instance of an object.' error at the indicated line (unfortunately it is no more specific than that), with a slightly unhelpful stacktrace:
Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
at iText.Forms.PdfAcroForm.AddField(PdfFormField field, PdfPage page)
at iText.Forms.PdfAcroForm.AddField(PdfFormField field)
at ReplaceIcon.Main(String[] args) in ReplaceIcon.cs:line 65
I also tried replacing the CreateButton with CreatePushButton, as in:
PdfButtonFormField button = PdfFormField.CreatePushButton(document, new Rectangle(50, 50), "name", "caption");
Using which the code compiles, and I get a "Select Image" dialogue box when I click on the button in the PDF, but the button remains just a grey square with "caption" written on it, rather than being replaced by the selected image. But I suspect that a generic button is required so you can overwrite the layout (somehow).
If anyone knows how this is supposed to be done, either using this button approach or another way, I would greatly appreciate some pointers. As I said, I am specifically interested in creating these fields in a newly generated PDF document, using iText 7 in a C# program.
But I get a System.NullReferenceException: 'Object reference not set to an instance of an object.'
This is the bug in the Acroform#addField method. NPE is being thrown every time it gets a nameless field as a parameter.
To avoid it, just set the field name before adding to the form (field#setName).
Using which the code compiles, and I get a "Select Image" dialogue box when I click on the button in the PDF, but the button remains just a grey square with "caption" written on it, rather than being replaced by the selected image. But I suspect that a generic button is required so you can overwrite the layout (somehow).
the PdfFormField.CreateButton method does not give you any advantages here. That method in iText creates an empty PdfButtonFormField (appearance and behavior should be defined by the developer after a field creation).
On the other side, CreatePushButton does almost what you need.
The only thing must be adjusted is a layout. By default, the created push button has the "label only" layout.
public void Generate()
{
PdfWriter writer = new PdfWriter("myfile.pdf");
PdfDocument document = new PdfDocument(writer);
PdfAcroForm form = PdfAcroForm.GetAcroForm(document, true);
PdfButtonFormField button = PdfFormField.CreatePushButton(document, new Rectangle(20, 500, 50, 50), "btn",
"load");
button.SetAction(PdfAction.CreateJavaScript("event.target.buttonImportIcon();"));
//change the layout type.
PdfDictionary widget = (PdfDictionary) button.GetKids().Get(0).GetIndirectReference().GetRefersTo();
widget.GetAsDictionary(PdfName.MK).Put(PdfName.TP, new PdfNumber((int) PushButtonLayouts.ICON_ONLY));
form.AddField(button); // <-- Error on this line
document.Close();
}
enum PushButtonLayouts
{
LABEL_ONLY = 0, //No icon; caption only
ICON_ONLY = 1, //No caption; icon only
ICON_TOP_LABEL_BOTTOM = 2, // Caption below the icon
LABEL_TOP_ICON_BOTTOM = 3, // Caption above the icon
ICON_LEFT_LABEL_RIGHT = 4, //Caption to the right of the icon
LABEL_LEFT_ICON_RIGHT = 5, //Caption to the left of the icon
LABEL_OVER = 6 // Caption overlaid directly on the icon
}
I want to use OpenXML to automate Word document creation by taking a given document as a template. This template contains text blocks with a special syntax (namely e.g. «tagname[»...«]tagname») in the form of hidden text (in the font dialog box, the 'Hidden' checkbox in the effects section is activated) so that they don't show up when the document is printed.
Depending on the tagname I want to replace any content which might be already there in the template file between the opening and closing tag with some other content (e.g. in «today[»01/01/2000«]today» the content 01/01/2000 would be substituted with 07/06/2018) preferably preserving the format (e.g. bold, italic or text color).
How can I retrieve the hidden text parts and is there an easy way to replace anything between corresponding tags using C#?
I am not sure what exactly are the text blocks in your template. Instead you can use Content Control from Developer options at the required place, then you can name them from the properties of content control.
today will be name of the Content Control
Once you have all the content control at your template, you can look for specific content control with name and add the new value you want to.
Here is the Snippet to give you an idea.
// Title is name of content control(today), value is what you want to add(1/01/2000)
private static void UpdateControl(WordprocessingDocument document, string title, string value)
{
MainDocumentPart mainPart = document.MainDocumentPart;
var sdtRuns = mainPart.Document.Descendants<SdtRun>()
.Where(run => run.SdtProperties.GetFirstChild<Tag>().Val.Value == title);
foreach (SdtRun sdtRun in sdtRuns)
{
sdtRun.Descendants<Text>().First().Text = value;
}
document.MainDocumentPart.Document.Save();
}
Question Revised and clarified (Thanks to Bruno for point me in the right direction)-
This post was originally made under great sleep deprivation and after reading, I see how it could be confusing. I want to make sure that others have this solution in the future and not have to spend loads of time trying to figure it out.
Here is my question and there is an answer below that solves it.
I have a pdf form and I need to add an image to a specific location where I have put a place holder. How can I do this?
Just for future people who may be looking for the answer on how to best add an image to a pre-defined specific location into a pre-existing .pdf form, here is the answer.
/* This codes works for those who have a pre-created pdf form with a button in the location and size you want your image.
I reccomend acrobat pro, thought it has a monthly cost. There are additional free PDF editors
instantiate a new PdfStamper that will create a new file from my existing form. The Reader reads in your template and the stamper makes a copy
FileToPath and FileFromPath are strings i created above that hold the path
*/
using (PdfStamper stamper = new PdfStamper(new PdfReader(fileFromPath), File.Create(FileToPath)))
{
//get my buttons positions current location and height btn1 is the name of my button that exists in the pdf already
AcroFields.FieldPosition fieldPosition = stamper.AcroFields.GetFieldPositions("btn1")[0];
//create a new button utilze the field position to set it's location. FYI this is a rectangle. I reccomend you read about those
//it will save you tons of time to understand them.
PushbuttonField imageField = new
//btn1Replaced is what I named the new button that will overwrite the old place holder button
PushbuttonField(stamper.Writer, fieldPosition.position, "btn1Replaced");
//Here I set they layout from my old button to my new one
imageField.Layout = PushbuttonField.LAYOUT_ICON_ONLY;
//grab the image you want in your pdf imgPath is a string I wrone above to grab the image
iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance("imgPath");
//set your buttons image property to be your image you just grabbed
imageField.Image = img;
//always scale to the size of the button
imageField.ScaleIcon = PushbuttonField.SCALE_ICON_ALWAYS;
imageField.ProportionalIcon = false;
//make sure your button is read only and then it will not act like a button, it will act like an image.
imageField.Options = BaseField.READ_ONLY;
//Get rid of the old button
stamper.AcroFields.RemoveField("btn1");
//add my button and make sure it is on the correct page
stamper.AddAnnotation(imageField.Field, fieldPosition.page);
stamper.Close();
}
I'm trying to add a rich text content control around the user's selected text in a Word document.
I'm new to VSTO and Content Controls so i'm using the MSDN examples as a baseline. The example shows this, which adds the Content Control at the chosen position:
private void AddRichTextControlAtSelection()
{
word.Document currentDocument = Globals.ThisAddIn.Application.ActiveDocument;
currentDocument.Paragraphs[1].Range.InsertParagraphBefore();
currentDocument.Paragraphs[1].Range.Select();
Document extendedDocument = Globals.Factory.GetVstoObject(currentDocument);
richTextControl1 = extendedDocument.Controls.AddRichTextContentControl("richTextControl1");
richTextControl1.PlaceholderText = "Enter your first name";
}
However i want the Content Control to wrap around the user's selected text. Any help, please?
What you found is one possibility. More efficient and "cleaner" (IMO) would be to use the constructor that accepts a RANGE object and pass the Range. If you want the user's selection then
richTextControl1 = extendedDocument.Controls.AddRichTextContentControl(extendedDocument.Parent.Selection.Range, "richTextControl1");
//the Parent of a Document is the Word.Application
//Selection is a dependent of the Word.Application
Otherwise, building on your code sample:
richTextControl1 = extendedDocument.Controls.AddRichTextContentControl(currentDocument.Paragraphs[1].Range, "richTextControl1");
Note that if you don't need to work with VSTO's extensions of the Content Controls you don't need to go through the GlobalFactory steps, you can simply insert the "interop" versions of the Content Controls.
Simple fix in the end: currentDocument.ActiveWindow.Selection.Range.Select();
I'm trying to set a background to a pdf and managed to set it with an image my pdf has a big table so the pages are added automatically not with the Document.NewPage() method so the image background is set only on the first page. This is the code that adds the background:
Image backImg = Image.GetInstance(#"D:\websites\DIS\bugs\130208\A4.png");
backImg.SetAbsolutePosition(0, 0);
backImg.Alignment = Image.UNDERLYING;
var doc = new Document(pageSize);
PdfWriter pdfWriter = PdfWriter.GetInstance(doc, new FileStream(filePath, FileMode.Create));
doc.Open();
doc.Add(backImg);
...
creating a big table
and not using the doc.NewPage() method. Do I have to loop throw every page and add the background image at the end before closing the doc, but how do I put it in the background not on top of the other elements?
Whenever you want to apply something to every page, you should use page events, more specifically PdfPageEvent.onEndPage(), to do it. You can find samples for its usage by keyword Page events > onEndPage --- these samples are taken from iText in Action 2nd Edition. The samples mainly add footers and headers while you want to add background graphics.
Be aware that you shouldn't add content to the Document instance here but instead directly to the PdfWriter, and as you want the image to be under the page content , not above it, you will need to use PdfWriter.getDirectContentUnder() like in the sample Stationery and not PdfWriter.getDirectContent() like in the other samples.
PS: The analogous samples for .Net can be found here.
PPS: The sample ImageDirect.java / ImageDirect.cs shows how to add an image to some direct content which might be the information missing here.
go for
PdfPageEvent.onStartPage()
. In this event, write your code to insert the image (as you are doing it). What it will do is that as soon as a new page is created, it will add the image to it and then the content over it; giving a watermark effect.