openxml-sdk - Creating word 2007 file with settings.xml - c#

I am trying to generate a new Word document using the following code. The Word document gets generated without settings.xml. I need to have settings.xml in the word file. Any help would be appreciated.
public static byte[] GenerateNewDocument()
{
byte[] returnValue = null;
MemoryStream stream = null;
WordprocessingDocument wordDoc = null;
try
{
stream = new System.IO.MemoryStream();
wordDoc = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document);
}
catch
{
if (stream != null)
{
stream.Close();
}
throw;
}
using (wordDoc)
{
wordDoc.AddMainDocumentPart();
MainDocumentPart mainPart = wordDoc.MainDocumentPart;
mainPart.Document = new Document(new Body());
mainPart.Document.Save();
}
returnValue = stream.ToArray();
return returnValue;
}

You need to create your own DocumentSettingsPart and then insert it into the MainDocumentPart. So the settings part may look like this:
<w:settings xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vm" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:sl="http://schemas.openxmlformats.org/schemaLibrary/2006/main">
<w:defaultTabStop w:val="475"/>
<w:compat>
<w:compatSetting w:name="compatibilityMode" w:uri="http://schemas.microsoft.com/office/word" w:val="14"/>
</w:compat>
</w:settings>
And then having that saved somewhere as "settings.xml", you could use code like this:
private static void AddSettingsToMainDocumentPart(MainDocumentPart part)
{
DocumentSettingsPart settingsPart = part.AddNewPart<DocumentSettingsPart>();
FileStream settingsTemplate = new FileStream("settings.xml", FileMode.Open, FileAccess.Read);
settingsPart.FeedData(settingsTemplate);
settingsPart.Settings.Save();
}

Related

Html Text Content to Word using OpenXml

I have a rich text box which contains html formatted text as well as we can insert a copied images. I tried with AlternativeFormatImportPart and AltChunk method. It's generating the document but getting the below error. Please let me know what am I missing here.
MemoryStream ms;// = new MemoryStream(new UTF8Encoding(true).GetPreamble().Concat(Encoding.UTF8.GetBytes(h)).ToArray());
ms = new MemoryStream(HtmlToWord(fileContent));
//MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(h));
// Create alternative format import part.
AlternativeFormatImportPart chunk =
mainDocPart.AddAlternativeFormatImportPart(
"application/xhtml+xml", altChunkId);
chunk.FeedData(ms);
AltChunk altChunk = new AltChunk();
altChunk.Id = altChunkId;
public static byte[] HtmlToWord(String html)
{
const string filename = "test.docx";
if (File.Exists(filename)) File.Delete(filename);
var doc = new Document();
using (MemoryStream generatedDocument = new MemoryStream())
{
using (WordprocessingDocument package = WordprocessingDocument.Create(
generatedDocument, WordprocessingDocumentType.Document))
{
MainDocumentPart mainPart = package.MainDocumentPart;
if (mainPart == null)
{
mainPart = package.AddMainDocumentPart();
new Document(new Body()).Save(mainPart);
}
HtmlConverter converter = new HtmlConverter(mainPart);
converter.ExcludeLinkAnchor = true;
converter.RefreshStyles();
converter.ImageProcessing = ImageProcessing.AutomaticDownload;
//converter.BaseImageUrl = new Uri(domainNameURL + "Images/");
converter.ConsiderDivAsParagraph = false;
Body body = mainPart.Document.Body;
var paragraphs = converter.Parse(html);
for (int i = 0; i < paragraphs.Count; i++)
{
body.Append(paragraphs[i]);
}
mainPart.Document.Save();
}
return generatedDocument.ToArray();
}
}
There are some issues in AlternativeFormatImportPart with MemoryStream, document is not getting formatted well. So followed an alternate approach, using HtmlToWord method saved the html content into word and read the file content using FileStream and feed the AlternativeFormatImportPart.
string docFileName;
HtmlToWord(fileContent, out docFileName);
FileStream fileStream = File.Open(docFileName, FileMode.Open);
// Create alternative format import part.
AlternativeFormatImportPart chunk =mainDocPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.WordprocessingML, altChunkId);
chunk.FeedData(fileStream);
AltChunk altChunk = new AltChunk();
altChunk.Id = altChunkId;

Got a message " MEMORY STREAM IS NOT EXPANDABLE" after using WordprocessingDocument base on Microsoft site on MVC

Currently, I was base on "Search and replace text in a document part (Open XML SDK)" on the Microsoft site. I've realized that the code got an issue after the file has downloaded to my drive.
So I opened that file and got a message
MEMORY STREAM IS NOT EXPANDABLE at sw.Write(docText);
How to fix that?
In GenerateDocxHelper class:
private readonly MemoryStream _mem;
private Dictionary<string, string> _dicData;
public GenerateDocxHelper(string path)
{
_mem = new MemoryStream(System.IO.File.ReadAllBytes(path));
_dicData = new Dictionary<string, string>();
}
public MemoryStream ReplaceTextInWord()
{
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(_mem, true))
{
string docText = null;
using (StreamReader sr = new StreamReader(wordDoc.MainDocumentPart.GetStream()))
{
docText = sr.ReadToEnd();
}
foreach (var data in _dicData)
{
docText = docText.Replace(data.Key, data.Value);
}
using (StreamWriter sw = new StreamWriter(wordDoc.MainDocumentPart.GetStream(FileMode.Create)))
{
sw.Write(docText);
}
}
_mem.Seek(0, SeekOrigin.Begin);
return _mem;
}
You should create the MemoryStream with capacity = 0 which means it is resizeable,
and then add the bytes you have read from the file.
var allBytes = File.ReadAllBytes(path);
//this makes _mem resizeable
_mem = new MemoryStream(0);
_mem.Write(allBytes, 0, allBytes.Length);
Check this answer

Using OpenXML to replace text in the document

I open document and copy to the stream.
How I can replace some text in document before stream?
//wordTemplate - var with path to my word template
byte[] result = null;
byte[] templateBytes = System.IO.File.ReadAllBytes(wordTemplate);
using (MemoryStream templateStream = new MemoryStream())
{
templateStream.Write(templateBytes, 0, (int)templateBytes.Length);
using (WordprocessingDocument doc = WordprocessingDocument.Open(templateStream, true))
{
MainDocumentPart mainPart = doc.MainDocumentPart;
mainPart.Document.Save();
templateStream.Position = 0;
using (MemoryStream memoryStream = new MemoryStream())
{
templateStream.CopyTo(memoryStream);
result = memoryStream.ToArray();
}
}
}
Instead of calling File.ReadAllBytes(), why not call File.ReadAllLines(string) instead? It gives you an array of strings, which you can then can search and replace to your hearts delight.

Editing custom XML part in word document sometimes corrupts document

We have a system that stores some custom templating data in a Word document. Sometimes, updating this data causes Word to complain that the document is corrupted. When that happens, if I unzip the docx file and compare the contents to the previous version, the only difference appears to be the expected change in the customXML\item.xml file. If I re-zip the contents using 7zip, it seems to work OK (Word no longer complains that the document is corrupt).
The (simplified) code:
void CreateOrReplaceCustomXml(string filename, MyCustomData data)
{
using (var doc = WordProcessingDocument.Open(filename, true))
{
var part = GetCustomXmlParts(doc).SingleOrDefault();
if (part == null)
{
part = doc.MainDocumentPart.AddCustomXmlPart(CustomXmlPartType.CustomXml);
}
var serializer = new DataContractSerializer(typeof(MyCustomData));
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, data);
stream.Seek(0, SeekOrigin.Begin);
part.FeedData(stream);
}
}
}
IEnumerable<CustomXmlPart> GetCustomXmlParts(WordProcessingDocument doc)
{
return doc.MainDocumentPart.CustomXmlParts
.Where(part =>
{
using (var stream = doc.Package.GePart(c.Uri).GetStream())
using (var streamReader = new StreamReader(stream))
{
return streamReader.ReadToEnd().Contains("Some.Namespace");
}
});
}
Any suggestions?
Since re-zipping works, it seems the content is well-formed.
So it sounds like the zip process is at fault. So open the corrupted docx in 7-Zip, and take note of the values in the "method" column (especially for customXML\item.xml).
Compare that value to a working docx - is it the same or different? Method "Deflate" works.
I faced the same issue and it turned out it was due to encoding.
Do you already specify the same encoding when serializing/deserializing?
Couple of suggestion
a. Try doc.Package.Flush(); after you write the data back into the custom xml.
b. You may have to delete all custom part and add a new custom part. We are using the following code and it seems working fine.
public static void ReplaceCustomXML(WordprocessingDocument myDoc, string customXML)
{
MainDocumentPart mainPart = myDoc.MainDocumentPart;
mainPart.DeleteParts<CustomXmlPart>(mainPart.CustomXmlParts);
CustomXmlPart customXmlPart = mainPart.AddCustomXmlPart(CustomXmlPartType.CustomXml);
using (StreamWriter ts = new StreamWriter(customXmlPart.GetStream()))
{
ts.Write(customXML);
ts.Flush();
ts.Close();
}
}
public static MemoryStream GetCustomXmlPart(MainDocumentPart mainPart)
{
foreach (CustomXmlPart part in mainPart.CustomXmlParts)
{
using (XmlTextReader reader =
new XmlTextReader(part.GetStream(FileMode.Open, FileAccess.Read)))
{
reader.MoveToContent();
if (reader.Name.Equals("aaaa", StringComparison.OrdinalIgnoreCase))
{
string str = reader.ReadOuterXml();
byte[] byteArray = Encoding.ASCII.GetBytes(str);
MemoryStream stream = new MemoryStream(byteArray);
return stream;
}
}
}
return null; //result;
}
using (WordprocessingDocument myDoc = WordprocessingDocument.Open(ms, true))
{
StreamReader reader = new StreamReader(memStream);
string FullXML = reader.ReadToEnd();
ReplaceCustomXML(myDoc, FullXML);
myDoc.Package.Flush();
//Code to save file
}

XML update attributes: add characters at the end and fail

I'm trying to update the attributes of a xml. My problem is that when I update and later I read my xml I have an exception. I was looking for the problem and I don't know how but at the end of xml appears the last character of my general close tag. Sometimes the three or two characters last or only the character >. This problem don't occur always. Sometimes in the second time, others in fourth time, other in tenth times... I put a snippet bellow. thanks so much and sorry for my English.
PD: I don't use Linq
My XML
// At the end of xml file appears this fragment
<?xml version="1.0"?>
<MYPRINCIPALTAG>
<TAG DATE="01/01/01"></TAG>
</MYPRINCIPALTAG>AG>
More code ;-)
// Read XML
System.IO.FileStream fs = new System.IO.FileStream (path, System.IO.FileMode.Open, System.IO.FileAccess.Read);
XmlTextReader reader = new XmlTextReader (fs);
while (reader.Read ()) {
switch (reader.NodeType) {
case XmlNodeType.Element:
switch (reader.Name) {
case 'TAG':
string pubdate = reader.GetAttribute (0);
break;
}
break;
}
fs.Close();
public static XmlNode OpenXmlNode(string path, ref System.IO.FileStream fs, ref XmlDocument doc) {
fs = new System.IO.FileStream (path, System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite);
doc = new XmlDocument ();
doc.Load (fs);
fs.Flush ();
fs.Close ();
return doc.DocumentElement;
}
public static void CloseXmlNode(string path, ref System.IO.FileStream fs, ref XmlDocument doc) {
fs = new System.IO.FileStream (path, System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite);
doc.Save (fs);
fs.Flush ();
fs.Close ();
}
public static Boolean UpdateXML(string path, string id_tag) {
try {
System.IO.FileStream fs = null;
XmlDocument doc = new XmlDocument ();
XmlNode element = OpenXmlNode (path, ref fs, ref doc);
// Change Date
element.ChildNodes[0].Attributes.GetNamedItem ("date").Value = DateTime.Now.ToString ("dd/MM/yy");
for (int count = 1; count < doc.DocumentElement.ChildNodes.Count; count++) {
if ((doc.DocumentElement.ChildNodes[count].Attributes.GetNamedItem ("ID").Value).Equals (id_tag)) {
for (int i = 0; i < doc.DocumentElement.ChildNodes[count].ChildNodes[0].ChildNodes.Count; i++) {
doc.DocumentElement.ChildNodes[count].ChildNodes[0].ChildNodes[i].Attributes.GetNamedItem ("STATE").Value = "ok";
}
break;
}
}
CloseXmlNode (path, ref fs, ref doc);
return true;
} catch (Exception e) {
return false;
}
}
Try doing a fs.flush(); before fs.Close();
Like this
doc.Save(fs);
fs.Flush();
fs.Close();
Better still. The way that you have written your code (with static functions), you don't seem to be benefitting from using filestreams (IMO). I would change the functions OpenXmlNode() and CloseXmlNode() as follows:
public static XmlNode OpenXmlNode(string path, ref XmlDocument doc)
{
doc = new XmlDocument ();
doc.Load (path);
return doc.DocumentElement;
}
public static void CloseXmlNode(string path, ref XmlDocument doc)
{
doc.Save (path);
}

Categories

Resources