Open Xml: Word Create a new Document and add a paragraph from another document - openxml

I have a document that I am iterating thru getting the paragraphs. For each of these paragraphs I need to create a new document and save it. I can't figure out how to add a Paragraph from the source document to the new one.
foreach (var p in paragraphsFromSourceDocument)
{
using (var memoryStream = new MemoryStream())
{
var doc = WordprocessingDocument.Create(memoryStream, WordprocessingDocumentType.Document);
doc.AddMainDocumentPart();
// Create the Document DOM.
doc.MainDocumentPart.Document = new Document();
doc.MainDocumentPart.Document.Body = new Body();
//Add the paragraph 'p' to the Body here:
// HOW ?????????
doc.MainDocumentPart.Document.Save();
}
}

// Open the file read-only since we don't need to change it.
using (var wordprocessingDocument = WordprocessingDocument.Open(documentFileName, true))
{
paragraphs = wordprocessingDocument.MainDocumentPart.Document.Body
.OfType<Paragraph>().ToList();
styles = wordprocessingDocument.MainDocumentPart.StyleDefinitionsPart;
foreach (var p in paragraphs)
{
using (var memoryStream = new MemoryStream())
{
var doc = WordprocessingDocument.Create(memoryStream, WordprocessingDocumentType.Document);
doc.AddMainDocumentPart().AddPart(styles);
doc.MainDocumentPart.Document = new Document();
doc.MainDocumentPart.Document.Body = new Body();
doc.MainDocumentPart.Document.Body.Append(p.CloneNode(true));
doc.MainDocumentPart.Document.Save();
Console.WriteLine(GetHTMLOfDoc(doc));
}
}
}

Related

How to merge two pdf files with out losing layers using itextsharp in C#?

How to merge two pdf files with layers using itextsharp in C#, I tried but losing layers
// step 1: creation of a document-object
Document document = new Document();
// step 2: we create a writer that listens to the document
PdfCopy writer = new PdfCopy(document, new FileStream(outPutFilePath, FileMode.Create));
if(writer == null)
{
return;
}
// step 3: we open the document
document.Open();
foreach(string fileName in filesPath)
{
// we create a reader for a certain document
PdfReader reader = new PdfReader(fileName);
reader.ConsolidateNamedDestinations();
// step 4: we add content
for(int i = 1; i <= reader.NumberOfPages; i++)
{
PdfImportedPage page = writer.GetImportedPage(reader, i);
page.ContentTagged = true;
writer.AddPage(page);
}
PRAcroForm form = reader.AcroForm;
if(form != null)
{
// writer.CopyDocumentFields(reader);
}
reader.Close();
}
// step 5: we close the document and writer
writer.Close();
document.Close();

iTextSharp PdfStamper.PartialFormFlattening flattening only some, not all, of the fields

The code below is correctly assigning the value "foo" to the named field, but the field is not being "flattened". I must be neglecting a step, but I don't know what it is. Please advise. Thanks.
public byte[] FlattenSpecifiedFormFields(byte[] b, List<string> fieldNames2Flatten)
{
PdfReader reader = new PdfReader(b);
using (var ms = new MemoryStream())
{
var stamper = new iTextSharp.text.pdf.PdfStamper(reader, ms);
foreach (string name in fieldNames2Flatten)
{
stamper.AcroFields.SetField(name, "foo");
stamper.PartialFormFlattening(name);
}
stamper.Close();
return ms.ToArray();
};
}
Even when partially flattening a form, the PdfStamper FormFlattening property must be set to true. I.e.:
var stamper = new PdfStamper(reader, ms);
stamper.FormFlattening = true;
foreach (string name in fieldNames2Flatten)
{
stamper.AcroFields.SetField(name, "foo");
stamper.PartialFormFlattening(name);
}

"Content can not be added to a PdfImportedPage." error

I am trying to download and merges multiple pdf files by using ITextSharp.
It used to working before but I being got an "Content can not be added to a PdfImportedPage." error message on the line:
importedPage = writer.GetImportedPage(reader, currentPageIndex);
The full code is below, any help will be very appreciated.
private string MergeDocuments(IList<string> fileUrls, string fileName)
{
var reportFolder = this.ReportFolder + "\\";
using (MemoryStream output = new MemoryStream())
{
Document document = new Document();
try
{
// Initialize pdf writer
PdfWriter writer = PdfWriter.GetInstance(document, output);
// Open document to write
document.Open();
PdfContentByte content = writer.DirectContent;
PdfImportedPage importedPage;
// Iterate through all pdf documents
foreach (var url in fileUrls)
{
// Create pdf reader
using (PdfReader reader = new PdfReader(new Uri(url)))
{
int numberOfPages = reader.NumberOfPages;
// Iterate through all pages
for (int currentPageIndex = 1; currentPageIndex <= numberOfPages; currentPageIndex++)
{
// Determine page size for the current page
document.SetPageSize( reader.GetPageSizeWithRotation(currentPageIndex) );
// Create page
document.NewPage();
importedPage = writer.GetImportedPage(reader, currentPageIndex);
content.AddTemplate(importedPage, 1f, 0, 0, 1f, 0, 0);
}
}
}
}
catch (Exception exception)
{
throw new Exception("Error occured", exception);
}
File.WriteAllBytes(reportFolder + fileName + ".pdf", output.GetBuffer());
}
return "Reports/" + fileName + ".pdf";
}
When I try the following code, I get a null pointer exception in the addDocument() method:
using (MemoryStream output = new MemoryStream()) {
Document document = new Document();
document.Open();
PdfCopy copy = new PdfSmartCopy(document, output);
foreach (var url in fileUrls) {
using (WebClient client = new WebClient()) {
var byteArray = client.DownloadData(url);
PdfReader reader = new PdfReader(byteArray);
copy.AddDocument(reader);
reader.Close();
}
}
}
I found the problem, the document object should be closed before writing memory stream to file.
Just added document.Close() as below.
document.Close();
File.WriteAllBytes(reportFolder + fileName + ".pdf", output.GetBuffer());

iTextSharp - Create new document as Byte[]

Have a little method which goes to the database and retrieves a pdf document from a varbinary column and then adds data to it. I would like to add code so that if this document (company stationery ) is not found then a new blank document is created and returned. The method could either return a Byte[] or a Stream.
Problem is that the variable "bytes" in the else clause is null.
Any ideas what's wrong?
private Byte[] GetBasePDF(Int32 AttachmentID)
{
Byte[] bytes = null;
DataTable dt = ServiceFactory
.GetService().Attachments_Get(AttachmentID, null, null);
if (dt != null && dt.Rows.Count > 0)
{
bytes = (Byte[])dt.Rows[0]["Data"];
}
else
{
// Create a new blank PDF document and return it as Byte[]
ITST.Document doc =
new ITST.Document(ITST.PageSize.A4, 50f, 50f, 25f, 25f);
MemoryStream ms = new MemoryStream();
PdfCopy copy = new PdfCopy(doc, ms);
ms.Position = 0;
bytes = ms.ToArray();
}
return bytes;
}
You are trying to use PdfCopy but that's intended for existing documents, not new ones. You just need to create a "blank" document using PdfWriter and Document. iText won't let you create a 100% empty document but the code below essentially does that by just adding a space.
private static Byte[] CreateEmptyDocument() {
using (var ms = new System.IO.MemoryStream()) {
using (var doc = new Document()) {
using (var writer = PdfWriter.GetInstance(doc, ms)) {
doc.Open();
doc.Add(new Paragraph(" "));
doc.Close();
}
}
return ms.ToArray();
}
}
I think you may need to use
bytes = ms.GetBuffer();
not
bytes = ms.ToArray();

OpenXML 2 SDK - Word document - Create bulleted list programmatically

Using the OpenXML SDK, 2.0 CTP, I am trying to programmatically create a Word document. In my document I have to insert a bulleted list, an some of the elements of the list must be underlined. How can I do this?
Lists in OpenXML are a little confusing.
There is a NumberingDefinitionsPart that describes all of the lists in the document. It contains information on how the lists should appear (bulleted, numbered, etc.) and also assigns and ID to each one.
Then in the MainDocumentPart, for every item in the list you want to create, you add a new paragraph and assign the ID of the list you want to that paragraph.
So to create a bullet list such as:
Hello,
world!
You would first have to create a NumberingDefinitionsPart:
NumberingDefinitionsPart numberingPart =
mainDocumentPart.AddNewPart<NumberingDefinitionsPart>("someUniqueIdHere");
Numbering element =
new Numbering(
new AbstractNum(
new Level(
new NumberingFormat() { Val = NumberFormatValues.Bullet },
new LevelText() { Val = "·" }
) { LevelIndex = 0 }
) { AbstractNumberId = 1 },
new NumberingInstance(
new AbstractNumId() { Val = 1 }
) { NumberID = 1 });
element.Save(numberingPart);
Then you create the MainDocumentPart as you normally would, except in the paragraph properties, assign the numbering ID:
MainDocumentPart mainDocumentPart =
package.AddMainDocumentPart();
Document element =
new Document(
new Body(
new Paragraph(
new ParagraphProperties(
new NumberingProperties(
new NumberingLevelReference() { Val = 0 },
new NumberingId() { Val = 1 })),
new Run(
new RunProperties(),
new Text("Hello, ") { Space = "preserve" })),
new Paragraph(
new ParagraphProperties(
new NumberingProperties(
new NumberingLevelReference() { Val = 0 },
new NumberingId() { Val = 1 })),
new Run(
new RunProperties(),
new Text("world!") { Space = "preserve" }))));
element.Save(mainDocumentPart);
There is a better explanation of the options available in the OpenXML reference guide in Section 2.9.
I wanted something that would allow me to add more than one bullet list to a document. After banging my head against my desk for a while, I managed to combine a bunch of different posts and examine my document with the Open XML SDK 2.0 Productity Tool and figured some stuff out. The document it produces now passes validation for by version 2.0 and 2.5 of the SDK Productivity tool.
Here is the code; hopefully it saves someone some time and aggravation.
Usage:
const string fileToCreate = "C:\\temp\\bulletTest.docx";
if (File.Exists(fileToCreate))
File.Delete(fileToCreate);
var writer = new SimpleDocumentWriter();
List<string> fruitList = new List<string>() { "Apple", "Banana", "Carrot"};
writer.AddBulletList(fruitList);
writer.AddParagraph("This is a spacing paragraph 1.");
List<string> animalList = new List<string>() { "Dog", "Cat", "Bear" };
writer.AddBulletList(animalList);
writer.AddParagraph("This is a spacing paragraph 2.");
List<string> stuffList = new List<string>() { "Ball", "Wallet", "Phone" };
writer.AddBulletList(stuffList);
writer.AddParagraph("Done.");
writer.SaveToFile(fileToCreate);
Using statements:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
Code
public class SimpleDocumentWriter : IDisposable
{
private MemoryStream _ms;
private WordprocessingDocument _wordprocessingDocument;
public SimpleDocumentWriter()
{
_ms = new MemoryStream();
_wordprocessingDocument = WordprocessingDocument.Create(_ms, WordprocessingDocumentType.Document);
var mainDocumentPart = _wordprocessingDocument.AddMainDocumentPart();
Body body = new Body();
mainDocumentPart.Document = new Document(body);
}
public void AddParagraph(string sentence)
{
List<Run> runList = ListOfStringToRunList(new List<string> { sentence});
AddParagraph(runList);
}
public void AddParagraph(List<string> sentences)
{
List<Run> runList = ListOfStringToRunList(sentences);
AddParagraph(runList);
}
public void AddParagraph(List<Run> runList)
{
var para = new Paragraph();
foreach (Run runItem in runList)
{
para.AppendChild(runItem);
}
Body body = _wordprocessingDocument.MainDocumentPart.Document.Body;
body.AppendChild(para);
}
public void AddBulletList(List<string> sentences)
{
var runList = ListOfStringToRunList(sentences);
AddBulletList(runList);
}
public void AddBulletList(List<Run> runList)
{
// Introduce bulleted numbering in case it will be needed at some point
NumberingDefinitionsPart numberingPart = _wordprocessingDocument.MainDocumentPart.NumberingDefinitionsPart;
if (numberingPart == null)
{
numberingPart = _wordprocessingDocument.MainDocumentPart.AddNewPart<NumberingDefinitionsPart>("NumberingDefinitionsPart001");
Numbering element = new Numbering();
element.Save(numberingPart);
}
// Insert an AbstractNum into the numbering part numbering list. The order seems to matter or it will not pass the
// Open XML SDK Productity Tools validation test. AbstractNum comes first and then NumberingInstance and we want to
// insert this AFTER the last AbstractNum and BEFORE the first NumberingInstance or we will get a validation error.
var abstractNumberId = numberingPart.Numbering.Elements<AbstractNum>().Count() + 1;
var abstractLevel = new Level(new NumberingFormat() {Val = NumberFormatValues.Bullet}, new LevelText() {Val = "·"}) {LevelIndex = 0};
var abstractNum1 = new AbstractNum(abstractLevel) {AbstractNumberId = abstractNumberId};
if (abstractNumberId == 1)
{
numberingPart.Numbering.Append(abstractNum1);
}
else
{
AbstractNum lastAbstractNum = numberingPart.Numbering.Elements<AbstractNum>().Last();
numberingPart.Numbering.InsertAfter(abstractNum1, lastAbstractNum);
}
// Insert an NumberingInstance into the numbering part numbering list. The order seems to matter or it will not pass the
// Open XML SDK Productity Tools validation test. AbstractNum comes first and then NumberingInstance and we want to
// insert this AFTER the last NumberingInstance and AFTER all the AbstractNum entries or we will get a validation error.
var numberId = numberingPart.Numbering.Elements<NumberingInstance>().Count() + 1;
NumberingInstance numberingInstance1 = new NumberingInstance() {NumberID = numberId};
AbstractNumId abstractNumId1 = new AbstractNumId() {Val = abstractNumberId};
numberingInstance1.Append(abstractNumId1);
if (numberId == 1)
{
numberingPart.Numbering.Append(numberingInstance1);
}
else
{
var lastNumberingInstance = numberingPart.Numbering.Elements<NumberingInstance>().Last();
numberingPart.Numbering.InsertAfter(numberingInstance1, lastNumberingInstance);
}
Body body = _wordprocessingDocument.MainDocumentPart.Document.Body;
foreach (Run runItem in runList)
{
// Create items for paragraph properties
var numberingProperties = new NumberingProperties(new NumberingLevelReference() {Val = 0}, new NumberingId() {Val = numberId});
var spacingBetweenLines1 = new SpacingBetweenLines() { After = "0" }; // Get rid of space between bullets
var indentation = new Indentation() { Left = "720", Hanging = "360" }; // correct indentation
ParagraphMarkRunProperties paragraphMarkRunProperties1 = new ParagraphMarkRunProperties();
RunFonts runFonts1 = new RunFonts() { Ascii = "Symbol", HighAnsi = "Symbol" };
paragraphMarkRunProperties1.Append(runFonts1);
// create paragraph properties
var paragraphProperties = new ParagraphProperties(numberingProperties, spacingBetweenLines1, indentation, paragraphMarkRunProperties1);
// Create paragraph
var newPara = new Paragraph(paragraphProperties);
// Add run to the paragraph
newPara.AppendChild(runItem);
// Add one bullet item to the body
body.AppendChild(newPara);
}
}
public void Dispose()
{
CloseAndDisposeOfDocument();
if (_ms != null)
{
_ms.Dispose();
_ms = null;
}
}
public MemoryStream SaveToStream()
{
_ms.Position = 0;
return _ms;
}
public void SaveToFile(string fileName)
{
if (_wordprocessingDocument != null)
{
CloseAndDisposeOfDocument();
}
if (_ms == null)
throw new ArgumentException("This object has already been disposed of so you cannot save it!");
using (var fs = File.Create(fileName))
{
_ms.WriteTo(fs);
}
}
private void CloseAndDisposeOfDocument()
{
if (_wordprocessingDocument != null)
{
_wordprocessingDocument.Close();
_wordprocessingDocument.Dispose();
_wordprocessingDocument = null;
}
}
private static List<Run> ListOfStringToRunList(List<string> sentences)
{
var runList = new List<Run>();
foreach (string item in sentences)
{
var newRun = new Run();
newRun.AppendChild(new Text(item));
runList.Add(newRun);
}
return runList;
}
}
Adam's answer above is correct except it is new NumberingInstance( instead of new Num( as noted in a comment.
Additionally, if you have multiple lists, you should have multiple Numbering elements (each with it's own id eg 1, 2, 3 etc -- one for each list in the document. This doesn't seem to be a problem with bullet lists, but numbered lists will continue using the same numbering sequence (as opposed to starting over again at 1) because it will think that it's the same list. The NumberingId has to be referenced in your paragraph like this:
ParagraphProperties paragraphProperties1 = new ParagraphProperties();
ParagraphStyleId paragraphStyleId1 = new ParagraphStyleId() { Val = "ListParagraph" };
NumberingProperties numberingProperties1 = new NumberingProperties();
NumberingLevelReference numberingLevelReference1 = new NumberingLevelReference() { Val = 0 };
NumberingId numberingId1 = new NumberingId(){ Val = 1 }; //Val is 1, 2, 3 etc based on your numberingid in your numbering element
numberingProperties1.Append(numberingLevelReference1);
numberingProperties1.Append(numberingId1);
paragraphProperties1.Append(paragraphStyleId1);
paragraphProperties1.Append(numberingProperties1);
Children of the Level element will have an effect on the type of bullet, and the indentation.
My bullets were too small until I added this to the Level element:
new NumberingSymbolRunProperties(
new RunFonts() { Hint = FontTypeHintValues.Default, Ascii = "Symbol", HighAnsi = "Symbol" })
Indentation was a problem until I added this element to the Level element as well:
new PreviousParagraphProperties(
new Indentation() { Left = "864", Hanging = "360" })
And if you are like me - creating a document from a template, then you may want to use this code, to handle both situations - when your template does or does not contain any numbering definitions:
// Introduce bulleted numbering in case it will be needed at some point
NumberingDefinitionsPart numberingPart = document.MainDocumentPart.NumberingDefinitionsPart;
if (numberingPart == null)
{
numberingPart = document.MainDocumentPart.AddNewPart<NumberingDefinitionsPart>("NumberingDefinitionsPart001");
}
Not sure if this helps anyone, but here is my snippet for inserting a list of bullets.
Create the word processing document and word document body
WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open($"{tempFolder}{tempFileName}", true);
Body wordDocumentBody = wordprocessingDocument.MainDocumentPart.Document.Body;
Then get the insert index. This is placeholder text inside the word document, so you can insert into the correct place in the word document.
int insertIndex = wordDocumentBody.ToList().IndexOf(wordDocumentBody.Where(p => p.InnerText.Contains("PLACEHOLDER_TEXT")).First());
You can then call this method to insert into the word document in both bullet points for lettering.
public static void InsertBullets(Body wordDocumentBody, int insertIndex, int bulletStyle, List<string> strToAdd)
{
//// Bullet Styles:
// 1 - Standard bullet
// 2 - Numbered
foreach (string item in strToAdd)
{
Paragraph para = wordDocumentBody.InsertAt(new Paragraph(), insertIndex);
ParagraphProperties paragraphProperties = new ParagraphProperties();
paragraphProperties.Append(new ParagraphStyleId() { Val = "ListParagraph" });
paragraphProperties.Append(new NumberingProperties(
new NumberingLevelReference() { Val = 0 },
new NumberingId() { Val = bulletStyle }));
para.Append(paragraphProperties);
para.Append(new Run(
new RunProperties(
new RunStyle() { Val = "ListParagraph" },
new NoProof()),
new Text($"{item}")));
insertIndex++;
}
}
You can then remove the placeholder text after with this.
wordDocumentBody.Elements().ElementAt(wordDocumentBody.ToList().IndexOf(wordDocumentBody.Where(p => p.InnerText.Contains("PLACEHOLDER_TEXT")).First())).Remove();