Duplicate Word Document Using OpenXML While Open Original Document - c#

I need to create a same copy of existing word document and open it as another instance while the original first document being opened. The second word document do not save but user may have the option to save it or not.
This need to be done using OpenXML.
I will attached here the current implementation. This implementation is having several issues.
The first document need to close first before use it in WordprocessingDocument using statement.
The second newly created document need to save in local folder.
Code Initiation
var doc = Globals.ThisAddIn.Application.ActiveDocument;
doc.Save();
string fileName = doc.FullName;
doc.Close();
using (WordprocessingDocument document = WordprocessingDocument.Create(fileName, WordprocessingDocumentType.Document))
{
}

Why do you need to use OpenXML ? With Interop you could simply:
Open the existing document
Copy everything within the document range
Create a new document
Paste the other document in the new one
It's done quickly and does the job perfectly

Related

Open XML parts are missing in dynamically created Word document

I'm creating WordprocessingDocuments in C# with the Open XML SDK and then converting them to pdf. Initially, I was using Interop to save the document in PDF format, but now that is not an option. I found that LibreOffice can convert documents calling soffice.exe from cmd, and I had wonderful results with normal documents. Still, then, when I tested LibreOffice converter with my dynamic documents, the converter crashed.
I copied one of these documents and opened it with LibreOffice Writer, its structure was wrong, then I opened the same document with Microsoft Word and its structure was fine. Finally, I saved it with Microsoft Word and opened both documents as ZIP files as below:
This is the good one:
And this is the bad one:
I noticed that when I save the document in Microsoft Word, these Open XML parts (which I called "files" in an earlier version of this question) are appearing. When I open the document previously saved with Microsoft Word in LibreOffice, the document is fine again.
Thus, is there a way to generate these Open XML parts (inside the Word document) without opening Microsoft Word?
I use the following code (to check if it is creating all the files):
using (MemoryStream mem = new MemoryStream())
{
// Create Document
using (WordprocessingDocument wordDocument =
WordprocessingDocument.Create(mem, WordprocessingDocumentType.Document, true))
{
// Add a main document part.
MainDocumentPart mainPart = wordDocument.AddMainDocumentPart();
// Create the document structure and add some text.
mainPart.Document = new Document();
Body docBody = new Body();
// Add your docx content here
CreateParagraph(docBody);
CreateStyledParagraph(docBody);
CreateTable(docBody);
CreateList(docBody);
Paragraph pImg = new Paragraph();
ImagePart imagePart = mainPart.AddImagePart(ImagePartType.Jpeg);
string imgPath = "https://cdn.pixabay.com/photo/2019/11/15/05/23/dog-4627679_960_720.png";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(imgPath);
req.UseDefaultCredentials = true;
req.PreAuthenticate = true;
req.Credentials = CredentialCache.DefaultCredentials;
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
imagePart.FeedData(resp.GetResponseStream());
// 1500000 and 1092000 are img width and height
Run rImg = new Run(DrawingManager(mainPart.GetIdOfPart(imagePart), "PictureName", 1500000, 1092000, string.Empty));
pImg.Append(rImg);
docBody.Append(pImg);
Paragraph pLink = new Paragraph();
// For the mainpart see above
pLink.Append(HyperLinkManager("http://YourLink", "My awesome link", mainPart));
docBody.Append(pLink);
mainPart.Document.Append(docBody);
mainPart.Document.Save();
wordDocument.Close();
}
result = Convert.ToBase64String(mem.ToArray());
}
The code above creates a Word document named Result.docx with the following structure:
But there aren't any other Open XML parts (like app.xml or styles.xml)
You need to make a difference between:
the Open XML standard and its minimum requirements on a WordprocessingDocument and
the "minimum" document created by Microsoft Word or other applications.
As per the standard, the minimum WordprocessingDocument only needs a main document part (MainDocumentPart, document.xml) with the following content:
<w:document xmlns:w="...">
<w:body>
<w:p />
</w:body>
</w:document>
Further parts such as the StyleDefinitionsPart (styles.xml) or the NumberingDefintionsPart (numbering.xml) are only required if you have styles or numbering, in which case you must explicitly create them in your code.
Next, looking at your sample code, it seems you are creating:
paragraphs that reference styles (see CreateStyledParagraph(docBody)), which would have to be defined in the StyleDefinitionsPart (styles.xml); and
numbered lists (e.g., CreateList(docBody)), which would have to be defined in the NumberingDefinitionsPart (numbering.xml).
However, your code neither creates a StyleDefinitionsPart nor a NumberingDefintionsPart, which means your document is likely not a valid Open XML document.
Now, Word is very forgiving and fixes various issues silently, ignoring parts of your Open XML markup (e.g., the styles you might have assigned to your paragraphs).
By contrast, depending on how fault-tolerant LibreOffice is, invalid Open XML markup might lead to a crash. For example, if LibreOffice simply assumes that a StyleDefinitionsPart exists when it finds an element like <w:pStyle w:val="MyStyleName" /> in your w:document and then does not check whether it gets a null reference when asking for the StyleDefinitionsPart, it could crash.
Finally, to add parts to your Word document, you would use the Open XML SDK as follows:
[Fact]
public void CanAddParts()
{
const string path = "Document.docx";
const WordprocessingDocumentType type = WordprocessingDocumentType.Document;
using WordprocessingDocument wordDocument = WordprocessingDocument.Create(path, type);
// Create minimum main document part.
MainDocumentPart mainDocumentPart = wordDocument.AddMainDocumentPart();
mainDocumentPart.Document = new Document(new Body(new Paragraph()));
// Create empty style definitions part.
var styleDefinitionsPart = mainDocumentPart.AddNewPart<StyleDefinitionsPart>();
styleDefinitionsPart.Styles = new Styles();
// Create empty numbering definitions part.
var numberingDefinitionsPart = mainDocumentPart.AddNewPart<NumberingDefinitionsPart>();
numberingDefinitionsPart.Numbering = new Numbering();
}

Is it possible to copy text inside a book mark of a docx using DocumentFormat.OpenXML c# to new document with f=same format?

We are trying to merge and split multiple docx files to one single document and then trying to split that single document to multiple documents.
we have tried with reading paragraphs and reading bookmarks seems promising.
using below code
(WordprocessingDocument wordDocument = WordprocessingDocument.Open(fbookMark, false))
{
foreach (BookmarkStart bookmark in wordDocument.MainDocumentPart.Document.Body.Descendants<BookmarkStart>())
{
Console.WriteLine();
Console.WriteLine(bookmark.Name);
Console.WriteLine(bookmark.Parent.InnerText);
}
}
output reads the book marks and inner text, when we try to copy this to a new document it fails. Is there a way to copy this bookmarked text to a new document, keeping the format and bookmark as is?

How to do a Mail Merge in C# using Interop.Word?

I have a template in Word that would be used to print out invoices.
Now, I would like to know how to create a Word Document programmatically and copy the template content into the new document so that I still have a clean template, then replace placeholders that I have typed by using Mail.Merge. I found similar Mail.Merge questions but most require Spire components and I am not interested since it needs to be paid for. I am only a student. Others though, actually doesn't help that much.
The problems I am facing now are as follows:
Create a Word document
Copy template content into new document
How to add placeholder names into MailMerge since I'm very confused about this.
Do MailMerge
Here is the current code that I have concocted, this is actually the first time I have used Interops
Document document = new Document();
Microsoft.Office.Interop.Word.Application wordApp = new Microsoft.Office.Interop.Word.Application();
document = wordApp.Documents.Open(fileName);
string[] fieldNames = {"date_issued", "month_covered", "tuition", "lunchfee", "discount", "in_no", "student_no", "student_name", "amount_due", "amount_paid", "balance", "penalty", "status"};
string[] data = new string[25];
Range range;
document.MailMerge.Fields.Add(range, fieldNames[0]);
document.MailMerge.Execute();
I'm really confused on this part
document.MailMerge.Fields.Add(range, fieldNames[0]);
and I don't know what range is for
If your template is an actual template and not a plain Word Document (with the extension .dotx or .dot, and not .docx/.doc), you don't need to copy anything. Just create a new document based on the template:
var doc = wordApp.Documents.Add("put template path here");
This will have the contents of your template. If you have used the Word UI to setup a mailmerge in the template (including the location of the data for the merge), that will also be carried over into the new document.
Then you can execute the mailmerge from C#:
doc.MailMerge.Execute();

Create a report from word or pdf documents

My goal is to create report in WPF using dev express. I want to add word or pdf documents into report.
For word documents, I am opening word document into RichEditControl, saving it into memory stream as rtf document and using XRRichText control, I am adding rtf document into report.
public void CreateReport()
{
RichEditControl richEdit = new RichEditControl();
MemoryStream stream = new MemoryStream();
XRRichText rtfDoc = new XRRichText();
richEdit.LoadDocument(#"word document path", DocumentFormat.OpenXml);
richEdit.SaveDocument(stream, DevExpress.XtraRichEdit.DocumentFormat.Rtf);
rtfDoc.LoadFile(stream, XRRichTextStreamType.RtfText);
rtfDoc.WidthF = 550F;
this.Detail.Controls.Add(rtfDoc);
this.RequestParameters = false;
this.CreateDocument();
}
Is this a recommended way to add word document as rtf?
and also, Instead of word document, how can I add pdf document into report?
Thank you!
Check the XtraRichEdit - Document Server (available now in v2011, volume 1) blog post, which may be helpful.

Embed contents of a RTF file into a DOCX file using OpenXML SDK

In our old MSWord-97 based system we use COM to interact with a .doc file, and embed an OLE object, so the embedded document is visible in the parent (not as an icon).
We're replacing this with a system using OpenXML SDK since it requires having Word on our server, which generates .docx files. however we still need to embed the contents of RTF files into the generated DOCX... specifically we replace a bookmark with the contents of the file.
I found a few examples online but they all differ. When I create a simple example in Word and view the XML, there's a lot of stuff to position/display the embedded object's visual representation, while the embedding itself doesn't seem too horrific. What's the easiest way to do this?
You could embed the content of a RTF document into a OpenXML DOCX file
by using the AltChunk anchor for external content. The AltChunk (w:altChunk) element specifies
a location in your OpenXML WordprocessingML document to insert external content such as a RTF document.
The code below uses the AltChunk class in conjunction with the AlternativeFormatImportPart class
to embed the content of a RTF document into a DOCX file after the last paragraph:
using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(#"your_docx_file.docx", true))
{
string altChunkId = "AltChunkId5";
MainDocumentPart mainDocPart = wordDocument.MainDocumentPart;
AlternativeFormatImportPart chunk = mainDocPart.AddAlternativeFormatImportPart(
AlternativeFormatImportPartType.Rtf, altChunkId);
// Read RTF document content.
string rtfDocumentContent = File.ReadAllText("your_rtf_document.rtf", Encoding.ASCII);
using (MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(rtfDocumentContent)))
{
chunk.FeedData(ms);
}
AltChunk altChunk = new AltChunk();
altChunk.Id = altChunkId;
// Embed AltChunk after the last paragraph.
mainDocPart.Document.Body.InsertAfter(
altChunk, mainDocPart.Document.Body.Elements<Paragraph>().Last());
mainDocPart.Document.Save();
}
If you want to embed an Unicode RTF string into a DOCX file then you have to escape the Unicode characters. For an example please refer to the following stackoverflow answer.
When you encounter the error "the file is corrupt" then ensure that you Dispose() or Close() the WordprocessingDocument. If you do not Close() the document then the releationship for the w:altchunk is not stored in the Document.xml.rels file.
This fella seemed to have figured it out with his own question and answer at How can I embed any file type into Microsoft Word using OpenXml 2.0

Categories