I'm trying to insert a file programmatically (*.zip for example) into an existing docx file.
I looked at the docx open library but it doesn't have the function there.
Also tried using Microsoft.Office.Interop.Word. I created a word document with a table, and I'm trying to insert a file into a cell inside the table.
Word.Application wordApp = new Word.Application();
wordApp.Visible = false;
Word.Document doc = new Word.Document();
doc = wordApp.Documents.Open(Environment.CurrentDirectory + "\\test.docx");
doc.Tables[1].Rows[2].Cells[1].Range.InsertFile((Environment.CurrentDirectory + "\\tttt.zip"));
but it caused an error:
"The file appears to be corrupted"
Can anyone have experience and help with this?
After a lot of trial and error...
The function "Range.InsertFile" doesn't actually inserts a file, it reads and appends the text into the Range.
The solution was simple - Copy paste...
using System.Collections.Specialized;
...
...
//Copy the Filename to Clipboard (setFile function uses StringCollection)
StringCollection collection = new StringCollection();
collection.Add(Environment.CurrentDirectory + "\\MyFile.zip");
Clipboard.SetFileDropList(collection);
//Paste into the selected Range.
range.Paste();
*There is also function "PasteSpecial" which didn't work (Only specific data types are supported).
Related
I have to copy selected text from activedocument to new file (at the end of target file). Both (source and target) are docx files. The source file is opened in Word and the user is working with it.
I would like to copy the selection without opening the target file as Microsoft.Office.Interop.Word.Document and copy-paste (for performance reasons).
I don't know how to change the "Selection" in open document to xml understandable by DocumentOpenXml and how to inject this xml into target file.
using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml.Packaging;
using Range = Microsoft.Office.Interop.Word.Range;
public void RangeToNewDocument(string documentPath, Range range)
{
string selectedXML = range.WordOpenXML; //??????????
using (WordprocessingDocument doc = WordprocessingDocument.Open(documentPath, true))
{
Body body = doc.MainDocumentPart.Document.Body;
//body.Append(selectedXML); ??????
doc.SaveAs(documentPath + ".RangeToNewDocumentTest.docx");
}
}
Many example codes are for "How to add something" but there are new objects (as 'Runs' or all 'Paragraphs') but I couldn't find anything about an existing object.
The only link I found is:
https://learn.microsoft.com/en-us/office/open-xml/how-to-copy-the-contents-of-an-open-xml-package-part-to-a-document-part-in-a-dif
but there replace one ThemePart with another and I have no idea how to adjust it for me.
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();
}
I've found loads of useful documentation around creating an instance of a word doc, inserting all manner of text and formatting but cannot find anywhere something to save a document that hasnt already been created and opened programmatically.
Essentially I want to create a docx file and fill it with text from a rich text box. Using code Ive found at How to Insert text in the end of the document I am able to achieve this if I first create a document. But despite suggestions of using _document.SaveAs() (which doesnt exist - version diff i guess) or .Save() supposedly prompting with a SaveAs dialogue if the file doesnt already exist, I always get a type mismatch error. So this is the working code if i pre-create the file to use:
OpenFileDialog SDO = new OpenFileDialog();
SDO.ShowDialog();
Microsoft.Office.Interop.Word._Application oWord;
object oMissing = Type.Missing;
oWord = new Microsoft.Office.Interop.Word.Application();
oWord.Visible = false;
oWord.Documents.Open(SDO.FileName);
oWord.Selection.TypeText(richTextBox1.Text);
oWord.ActiveDocument.Save();
oWord.Quit();
Now one would assume that removing the lines for the OpenFileDialogue Documents.Open would go some way to saving a new file created in C#, however even with:
Microsoft.Office.Interop.Word._Application oWord;
object oMissing = Type.Missing;
oWord = new Microsoft.Office.Interop.Word.Application();
oWord.Visible = false;
SaveFileDialog SD = new SaveFileDialog();
SD.Filter = "Word File |*.docx";
SD.Title = "Save File";
SD.ShowDialog();
oWord.Documents.Save(SD.FileName,WdNewDocumentType.wdNewXMLDocument);
oWord.Selection.TypeText(richTextBox1.Text);
oWord.ActiveDocument.Save();
oWord.Quit();
Other examples ive seen open the document so that you can save it yourself but i need it saving without any human intervention other than choosing a filename.
Any help appreciated, also the option of third party dlls like spire and gem are precluded so not an option :(
If anyone has a simple example of creating and saving a word doc that didnt exist before the program ran id be much obliged.
The Microsoft MSDN documentation has tons of useful guides and examples.
You are going to want to include:
using Word = Microsoft.Office.Interop.Word;
using Microsoft.Office.Tools.Word;
Then declare your application:
Word.Application app = new Word.Application();
Declare your new document:
Word.Document doc = app.Documents.Add();
Add text to your documnet
There are two ways to save these documents:
Programmatically
Using a save file dialog box
This is the way I do it.
app = new Word.Application();
object oMissing = System.Reflection.Missing.Value;
Word._Document oDoc = app.Documents.Add(ref oMissing, ref oMissing,
ref oMissing, ref oMissing);
.....
app.ActiveDocument.SaveAs2(fileName);
Where filename is my desired file name. When I was originally doing this, I discovered that there were a lot of undocumented (and therefore unsupported) functions. SaveAs2 is one of them! But it does work.
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
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();