Merge Multiple Word Documents while Preserving Headers - .Net Office Automation - c#

I want to merge multiple word documents while preserving the header and footer of each document. The below code is merging the documents but merges the header and the footer as well:
public static void Merge(List filesToMerge, string outputFilename)
{
Application wordApplication = null;
Document wordDocument = null;
try
{
// Create a new Microsoft Word application object
wordApplication = new Application();
wordApplication.Visible = false;
wordApplication.ScreenUpdating = false;
// Create a new file based on our template
object defaultTemplate = #"Normal.dotm";
wordDocument = wordApplication.Documents.Add(ref defaultTemplate);
// Make a Word selection object.
Selection selection = wordApplication.Selection;
// Loop thru each of the Word documents
foreach (string file in filesToMerge)
{
// Insert the files to our template
selection.InsertFile(file);
object pageBreak = WdBreakType.wdSectionBreakNextPage;
selection.InsertBreak(ref pageBreak);
}
// Save the document to it's output file.
object outputFile = outputFilename;
wordDocument.SaveAs(ref outputFile);
}
catch (Exception ex)
{
Console.WriteLine("Error while conversion. Details: " + ex);
}
finally
{
MSWordCleanup(wordApplication, wordDocument);
}
}
Is there anyway to do so?

It might be helpful for you to work through these training lessons:
http://office.microsoft.com/en-us/word-help/headers-and-footers-from-basic-to-elaborate-RZ001021662.aspx
While this won't give you the answer in "automation form", it might be enough for you to figure out the answer.

Related

How to add footer that is uneditable using interop in c#

I use this code to add a footer to a word document, but I want to make the footer read-only or uneditable if opened by an application. Is this possible? I searched on the net, but there isn't mention anywhere how to make it uneditable.
Word.Application wordApp = null;
// word document
document = null;
try
{
wordApp = new Word.Application();
//WordApp.Visible = true;
// open
document = wordApp.Application.Documents.Open(wordpath);
foreach (Word.Section wordSection in document.Sections)
{
//Get the footer range and add the footer details.
Word.Range footerRange = wordSection.Footers[Word.WdHeaderFooterIndex.wdHeaderFooterPrimary].Range;
footerRange.Font.ColorIndex = Word.WdColorIndex.wdGray50;
footerRange.Font.Size = 15;
footerRange.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
footerRange.Text = wordHeaderName;
}
// save
document.Save();
}
finally
{
// close excel document & application
if (document != null)
{
try
{
document.Close(Word.WdSaveOptions.wdDoNotSaveChanges);
}
catch { }
}
if (wordApp != null)
{
try
{
wordApp.Quit();
}
catch { }
}
}
Recent versions of Word (since 2007, as I recall) have a protection possiblity that allows you to block the entire document for editing - you can then explicitly allow editing for various ranges. The tricky part is that these ranges must have content in order for the permission to take (and remain) in effect.
This means you might need to put some "hidden text" in a header, for example, that should be editable. And if any header or footer is locked the option to unlink from previous is not available. Any missing functionality could be provided with code tools that unprotects, does whatever needs to be done, then reprotects.
On the assumption that all "parts" of the document already exist when you open the document and create a footer, change your code to something like the following.
Notes:
there might be syntax "blips" as I'm converting this from VBA
You should probably read up on document Protection in both the object
model as well as the general Word help, especially IRM possibilities.
You can assign a password to the protection.
Code snippet:
Word.Application wordApp = null;
// word document
document = null;
try
{
wordApp = new Word.Application();
//WordApp.Visible = true;
// open
document = wordApp.Application.Documents.Open(wordpath);
Word.Range permittedRange = document.Content;
permittedRange.Editors.Add(Word.WdEditorType.wdEditorEveryone);
foreach (Word.Section wordSection in document.Sections)
{
//Allow editing the headers?
permittedRange = wordSection.Headers[Word.WdHeaderFooterIndex.wdHeaderFooterPrimary].Range;
permittedRange.Editors.Add(Word.WdEditorType.wdEditorEveryone);
//Get the footer range and add the footer details.
Word.Range footerRange = wordSection.Footers[Word.WdHeaderFooterIndex.wdHeaderFooterPrimary].Range;
footerRange.Font.ColorIndex = Word.WdColorIndex.wdGray50;
footerRange.Font.Size = 15;
footerRange.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
footerRange.Text = wordHeaderName;
}
document.Protect(Word.WdProtectionType.wdAllowOnlyReading, ref missing, ref missing, ref missing, ref missing);
// save
document.Save();

Merged document using AltChunks has innertext empty

I am trying to merge multiple documents into a single one and then open the result document and process it further.
The "ChunkId" is a property that is increased every time this method is called in order to get a unique id. I followed the example from this site.
This is the code used to merge multiple documents (using altchunks):
`
private void MergeDocument(string mergePath, bool appendPageBreak)
{
if (!File.Exists(mergePath))
{
Log.Warn(string.Format("Document: \"{0}\" was not found.", mergePath));
return;
}
ChunkId++;
var altChunkId = "AltChunkId" + ChunkId;
var mainDocPart = DestinationDocument.MainDocumentPart;
if (mainDocPart == null)
{
DestinationDocument.AddMainDocumentPart();
mainDocPart = DestinationDocument.MainDocumentPart;
if (mainDocPart.Document == null)
mainDocPart.Document = new Document { Body = new Body() };
}
try
{
var chunk = mainDocPart.AddAlternativeFormatImportPart(
AlternativeFormatImportPartType.WordprocessingML, altChunkId);
if (chunk != null)
using (var ms = new FileStream(mergePath, FileMode.Open))
{
chunk.FeedData(ms);
}
else
{
Log.Error(string.Format("Merge - Failed to create chunk document based on \"{0}\".", mergePath));
return; // failed to create chunk document, return from merge method
}
}
catch (Exception e)
{
Log.Error(string.Format("Merge - Failed to insert chunk document based on \"{0}\".", mergePath));
return; // failed to create chunk document, return from merge method
}
var altChunk = new AltChunk { Id = altChunkId };
//append the page break
if (appendPageBreak)
try
{
AppendPageBreak(mainDocPart);
Log.Info(string.Format("Successfully appended page break."));
}
catch (Exception ex)
{
Log.Error(string.Format("Eror appending page break. Message: \"{0}\".", ex.Message));
return; // return if page break insertion failed
}
// insert the document
var last = mainDocPart.Document
.Body
.Elements()
.LastOrDefault(e => e is Paragraph || e is AltChunk);
try
{
if (last == null)
mainDocPart.Document.Body.InsertAt(altChunk, 0);
else
last.InsertAfterSelf(altChunk);
Log.Info(string.Format("Successfully inserted new doc \"{0}\" into destination.", mergePath));
}
catch (Exception ex)
{
Log.Error(string.Format("Error merging document \"{0}\". Message: \"{1}\".", mergePath, ex.Message));
return; // return if the merge was not successfull
}
try
{
mainDocPart.Document.Save();
}
catch (Exception ex)
{
Log.Error(string.Format("Error saving document \"{0}\". Message: \"{1}\".", mergePath, ex.Message));
}
}`
If I open the merged document with Word I can see its content (tables, text, paragraphs..), but if I open if from code again it says that inner text is "" (empty string). I need that inner text to reflect what the document contains because I have to replace some placeholders like "##name##" with another text and I can't if the inner text is empty.
This is the innerxml of the merged document,
This is how I open the merged document:
DestinationDocument = WordprocessingDocument.Open(Path.GetFullPath(destinationPath), true);
How can I read the inner text of the document? Or how can I merge these documents into a single one so that this problem would not occur anymore?
When documents merged with AltChunks it is like embedded attachments to the original word document. The client (MS Word) handles the rendering of the altchunk sections. Hence the resulting document won't have the openxml markup of the merged documents.
If you want to use the resulting document for further programmatic post-processing use Openxml Power Tools. pelase refer to my answer here
Openxml powertools - https://github.com/OfficeDev/Open-Xml-PowerTools
The problem is that the documents are not really merged (per se), the altChunk element only defines a place where the alternative content should be placed in the document and it has a reference to that alternative content.
When you open this document in MS Word then it will actually merge all those alternative contents automatically for you. So when you resave that document with MS Word you'll no longer have altChunk elements.
Nevertheless what you can do is actually manipulate with those altChunk DOCX files (the child DOCX documents) just like you do with the main DOCX file (the parent document).
For example:
string destinationPath = "Sample.docx";
string search = "##name##";
string replace ="John Doe";
using (var parent = WordprocessingDocument.Open(Path.GetFullPath(destinationPath), true))
{
foreach (var altChunk in parent.MainDocumentPart.GetPartsOfType<AlternativeFormatImportPart>())
{
if (Path.GetExtension(altChunk.Uri.OriginalString) != ".docx")
continue;
using (var child = WordprocessingDocument.Open(altChunk.GetStream(), true))
{
var foundText = child.MainDocumentPart.Document.Body
.Descendants<Text>()
.Where(t => t.Text.Contains(search))
.FirstOrDefault();
if (foundText != null)
{
foundText.Text = foundText.Text.Replace(search, replace);
break;
}
}
}
}
Alternatively you'll need to use some approach to merge those documents for real. One solution is mentioned by Flowerking, another one that you could try is with GemBox.Document library. It will merge those alternative contents for you on loading (just as MS Word does when opening).
For example:
string destinationPath = "Sample.docx";
string search = "##name##";
string replace = "John Doe";
DocumentModel document = DocumentModel.Load(destinationPath);
ContentRange foundText = document.Content.Find(search).FirstOrDefault();
if (foundText != null)
foundText.LoadText(replace);
document.Save(destinationPath);

Merging Word documents using WordProcessingDocument

I am currently working on a program in which a user should be able to merge several Word documents into one, without losing any formatting, headers and so on. The documents should simply stack up, one after another, without any changes.
Here is my current code:
public virtual Byte[] MergeWordFiles(IEnumerable<SendData> sourceFiles)
{
int f = 0;
// If only one Word document then skip merge.
if (sourceFiles.Count() == 1)
{
return sourceFiles.First().File;
}
else
{
MemoryStream destinationFile = new MemoryStream();
// Add first file
var firstFile = sourceFiles.First().File;
destinationFile.Write(firstFile, 0, firstFile.Length);
destinationFile.Position = 0;
int pointer = 1;
byte[] ret;
// Add the rest of the files
try
{
using (WordprocessingDocument mainDocument = WordprocessingDocument.Open(destinationFile, true))
{
XElement newBody = XElement.Parse(mainDocument.MainDocumentPart.Document.Body.OuterXml);
for (pointer = 1; pointer < sourceFiles.Count(); pointer++)
{
WordprocessingDocument tempDocument = WordprocessingDocument.Open(new MemoryStream(sourceFiles.ElementAt(pointer).File), true);
XElement tempBody = XElement.Parse(tempDocument.MainDocumentPart.Document.Body.OuterXml);
newBody.Add(XElement.Parse(new DocumentFormat.OpenXml.Wordprocessing.Paragraph(new Run(new Break { Type = BreakValues.Page })).OuterXml));
newBody.Add(tempBody);
mainDocument.MainDocumentPart.Document.Body = new Body(newBody.ToString());
mainDocument.MainDocumentPart.Document.Save();
mainDocument.Package.Flush();
}
}
}
catch (OpenXmlPackageException oxmle)
{
throw new Exception(string.Format(CultureInfo.CurrentCulture, "Error while merging files. Document index {0}", pointer), oxmle);
}
catch (Exception e)
{
throw new Exception(string.Format(CultureInfo.CurrentCulture, "Error while merging files. Document index {0}", pointer), e);
}
finally
{
ret = destinationFile.ToArray();
destinationFile.Close();
destinationFile.Dispose();
}
return ret;
}
}
The problem here is that the formatting is copied from the first document and applied to all the rest, meaning that for instance a different header in the second document will be ignored. How do I prevent this?
I have been looking in to breaking the document in to sections using SectionMarkValues.NextPage, as well as using altChunk.
The problem with the latter is altChunk does not seem to be able to handle a MemoryStream into its "FeedData" method.
DocIO is a .NET library that can read, write, merge and render Word 2003/2007/2010/2013/2016 files. The whole suite of controls is available for free (commercial applications also) through the community license program if you qualify. The community license is the full product with no limitations or watermarks.
Step 1: Create a console application
Step 2: Add reference to Syncfusion.DocIO.Base, Syncfusion.Compression.Base and Syncfusion.OfficeChart.Base; You can add these reference to your project using NuGet also.
Step 3: Copy & paste the following code snippet.
This code snippet will produce the document as per your requirement; each input Word document will get merged with its original formatting, styles and headers/footer.
using Syncfusion.DocIO.DLS;
using Syncfusion.DocIO;
using System.IO;
namespace DocIO_MergeDocument
{
class Program
{
static void Main(string[] args)
{
//Boolean to indicate whether any of the input document has different odd and even headers as true
bool isDifferentOddAndEvenPagesEnabled = false;
// Creating a new document.
using (WordDocument mergedDocument = new WordDocument())
{
//Get the files from input directory
DirectoryInfo dirInfo = new DirectoryInfo(System.Environment.CurrentDirectory + #"\..\..\Data");
FileInfo[] fileInfo = dirInfo.GetFiles();
for (int i = 0; i < fileInfo.Length; i++)
{
if (fileInfo[i].Extension == ".doc" || fileInfo[i].Extension == ".docx")
{
using (WordDocument sourceDocument = new WordDocument(fileInfo[i].FullName))
{
//Check whether the document has different odd and even header/footer
if (!isDifferentOddAndEvenPagesEnabled)
{
foreach (WSection section in sourceDocument.Sections)
{
isDifferentOddAndEvenPagesEnabled = section.PageSetup.DifferentOddAndEvenPages;
if (isDifferentOddAndEvenPagesEnabled)
break;
}
}
//Sets the breakcode of First section of source document as NoBreak to avoid imported from a new page
sourceDocument.Sections[0].BreakCode = SectionBreakCode.EvenPage;
//Imports the contents of source document at the end of merged document
mergedDocument.ImportContent(sourceDocument, ImportOptions.KeepSourceFormatting);
}
}
}
//if any of the input document has different odd and even headers as true then
//Copy the content of the odd header/foort and add the copied content into the even header/footer
if (isDifferentOddAndEvenPagesEnabled)
{
foreach (WSection section in mergedDocument.Sections)
{
section.PageSetup.DifferentOddAndEvenPages = true;
if (section.HeadersFooters.OddHeader.Count > 0 && section.HeadersFooters.EvenHeader.Count == 0)
{
for (int i = 0; i < section.HeadersFooters.OddHeader.Count; i++)
section.HeadersFooters.EvenHeader.ChildEntities.Add(section.HeadersFooters.OddHeader.ChildEntities[i].Clone());
}
if (section.HeadersFooters.OddFooter.Count > 0 && section.HeadersFooters.EvenFooter.Count == 0)
{
for (int i = 0; i < section.HeadersFooters.OddFooter.Count; i++)
section.HeadersFooters.EvenFooter.ChildEntities.Add(section.HeadersFooters.OddFooter.ChildEntities[i].Clone());
}
}
}
//If there is no document to merge then add empty section with empty paragraph
if (mergedDocument.Sections.Count == 0)
mergedDocument.EnsureMinimal();
//Saves the document in the given name and format
mergedDocument.Save("result.docx", FormatType.Docx);
}
}
}
}
Downloadable Demo
Note: There is a Word document (not section) level settings for
applying different header/footer for odd and even pages. Each input
document can have different values for this property. if any of the
input document has different odd and even header/footer as true, it
will affect the visual appearance of header/footer in the resultant
document. Hence, if any of the input document has different odd and
even header/footer, then the resultant Word document will have been
replaced with the odd header/footer contents.
For further information about DocIO, please refer our help documentation
Note: I work for Syncfusion

Blank page removal in a word document using microsoft word.interop

I have created a word document which generates dynamic contents using word.interop. It has some page breaks used in between. what is the problem which I am facing is, this page breaks creates blank pages which I don't want to show to users.
In some cases I need those page breaks there in order to maintain the page layout, so I can't think about removing those page breaks. but what I want is another solution like, in case if there no contents in a particular page other than a page break, remove that page.
How can I accomplish this, please help..
Thanks in advance..
Ahm quite easy: just look on the page and if the only paragraph there contains only a pagebreak - delete the page (or more accurate: save the empty pages to a list an delete in one stepat the end of the story):
I cannot provide code for you, because there is no sample code.
private bool RemoveBlankPage()
{
Word.Application wordapp = null;
Word.Document doc = null;
Word.Paragraphs paragraphs=null;
try
{
// Start Word APllication and set it be invisible
wordapp = new Word.Application();
wordapp.Visible = false;
doc = wordapp.Documents.Open(wordPath);
paragraphs = doc.Paragraphs;
foreach (Word.Paragraph paragraph in paragraphs)
{
if (paragraph.Range.Text.Trim() == string.Empty)
{
paragraph.Range.Select();
wordapp.Selection.Delete();
}
}
// Save the document and close document
doc.Save();
((Word._Document)doc).Close();
// Quit the word application
((Word._Application)wordapp).Quit();
}
catch(Exception ex)
{
MessageBox.Show("Exception Occur, error message is: "+ex.Message);
return false;
}
finally
{
// Clean up the unmanaged Word COM resources by explicitly
// call Marshal.FinalReleaseComObject on all accessor objects
if (paragraphs != null)
{
Marshal.FinalReleaseComObject(paragraphs);
paragraphs = null;
}
if (doc != null)
{
Marshal.FinalReleaseComObject(doc);
doc = null;
}
if (wordapp != null)
{
Marshal.FinalReleaseComObject(wordapp);
wordapp = null;
}
}
return true;
}
This link helped me complete my project
https://code.msdn.microsoft.com/office/How-to-remove-blank-pages-e200755d

how can I put a content in a mergefield in docx

I'm developing a web application with asp.net and I have a file called Template.docx that works like a template to generate other reports. Inside this Template.docx I have some MergeFields (Title, CustomerName, Content, Footer, etc) to replace for some dynamic content in C#.
I would like to know, how can I put a content in a mergefield in docx ?
I don't know if MergeFields is the right way to do this or if there is another way. If you can suggest me, I appreciate!
PS: I have openxml referenced in my web application.
Edits:
private MemoryStream LoadFileIntoStream(string fileName)
{
MemoryStream memoryStream = new MemoryStream();
using (FileStream fileStream = File.OpenRead(fileName))
{
memoryStream.SetLength(fileStream.Length);
fileStream.Read(memoryStream.GetBuffer(), 0, (int) fileStream.Length);
memoryStream.Flush();
fileStream.Close();
}
return memoryStream;
}
public MemoryStream GenerateWord()
{
string templateDoc = "C:\\temp\\template.docx";
string reportFileName = "C:\\temp\\result.docx";
var reportStream = LoadFileIntoStream(templateDoc);
// Copy a new file name from template file
//File.Copy(templateDoc, reportFileName, true);
// Open the new Package
Package pkg = Package.Open(reportStream, FileMode.Open, FileAccess.ReadWrite);
// Specify the URI of the part to be read
Uri uri = new Uri("/word/document.xml", UriKind.Relative);
PackagePart part = pkg.GetPart(uri);
XmlDocument xmlMainXMLDoc = new XmlDocument();
xmlMainXMLDoc.Load(part.GetStream(FileMode.Open, FileAccess.Read));
// replace some keys inside xml (it will come from database, it's just a test)
xmlMainXMLDoc.InnerXml = xmlMainXMLDoc.InnerXml.Replace("field_customer", "My Customer Name");
xmlMainXMLDoc.InnerXml = xmlMainXMLDoc.InnerXml.Replace("field_title", "Report of Documents");
xmlMainXMLDoc.InnerXml = xmlMainXMLDoc.InnerXml.Replace("field_content", "Content of Document");
// Open the stream to write document
StreamWriter partWrt = new StreamWriter(part.GetStream(FileMode.Open, FileAccess.Write));
//doc.Save(partWrt);
xmlMainXMLDoc.Save(partWrt);
partWrt.Flush();
partWrt.Close();
reportStream.Flush();
pkg.Close();
return reportStream;
}
PS: When I convert MemoryStream to a file, I got a corrupted file. Thanks!
I know this is an old post, but I could not get the accepted answer to work for me. The project linked would not even compile (which someone has already commented in that link). Also, it seems to use other Nuget packages like WPFToolkit.
So I'm adding my answer here in case someone finds it useful. This only uses the OpenXML SDK 2.5 and also the WindowsBase v4. This works on MS Word 2010 and later.
string sourceFile = #"C:\Template.docx";
string targetFile = #"C:\Result.docx";
File.Copy(sourceFile, targetFile, true);
using (WordprocessingDocument document = WordprocessingDocument.Open(targetFile, true))
{
// If your sourceFile is a different type (e.g., .DOTX), you will need to change the target type like so:
document.ChangeDocumentType(WordprocessingDocumentType.Document);
// Get the MainPart of the document
MainDocumentPart mainPart = document.MainDocumentPart;
var mergeFields = mainPart.RootElement.Descendants<FieldCode>();
var mergeFieldName = "SenderFullName";
var replacementText = "John Smith";
ReplaceMergeFieldWithText(mergeFields, mergeFieldName, replacementText);
// Save the document
mainPart.Document.Save();
}
private void ReplaceMergeFieldWithText(IEnumerable<FieldCode> fields, string mergeFieldName, string replacementText)
{
var field = fields
.Where(f => f.InnerText.Contains(mergeFieldName))
.FirstOrDefault();
if (field != null)
{
// Get the Run that contains our FieldCode
// Then get the parent container of this Run
Run rFldCode = (Run)field.Parent;
// Get the three (3) other Runs that make up our merge field
Run rBegin = rFldCode.PreviousSibling<Run>();
Run rSep = rFldCode.NextSibling<Run>();
Run rText = rSep.NextSibling<Run>();
Run rEnd = rText.NextSibling<Run>();
// Get the Run that holds the Text element for our merge field
// Get the Text element and replace the text content
Text t = rText.GetFirstChild<Text>();
t.Text = replacementText;
// Remove all the four (4) Runs for our merge field
rFldCode.Remove();
rBegin.Remove();
rSep.Remove();
rEnd.Remove();
}
}
What the code above does is basically this:
Identify the 4 Runs that make up the merge field named "SenderFullName".
Identify the Run that contains the Text element for our merge field.
Remove the 4 Runs.
Update the text property of the Text element for our merge field.
UPDATE
For anyone interested, here is a simple static class I used to help me with replacing merge fields.
Frank Fajardo's answer was 99% of the way there for me, but it is important to note that MERGEFIELDS can be SimpleFields or FieldCodes.
In the case of SimpleFields, the text runs displayed to the user in the document are children of the SimpleField.
In the case of FieldCodes, the text runs shown to the user are between the runs containing FieldChars with the Separate and the End FieldCharValues. Occasionally, several text containing runs exist between the Separate and End Elements.
The code below deals with these problems. Further details of how to get all the MERGEFIELDS from the document, including the header and footer is available in a GitHub repository at https://github.com/mcshaz/SimPlanner/blob/master/SP.DTOs/Utilities/OpenXmlExtensions.cs
private static Run CreateSimpleTextRun(string text)
{
Run returnVar = new Run();
RunProperties runProp = new RunProperties();
runProp.Append(new NoProof());
returnVar.Append(runProp);
returnVar.Append(new Text() { Text = text });
return returnVar;
}
private static void InsertMergeFieldText(OpenXmlElement field, string replacementText)
{
var sf = field as SimpleField;
if (sf != null)
{
var textChildren = sf.Descendants<Text>();
textChildren.First().Text = replacementText;
foreach (var others in textChildren.Skip(1))
{
others.Remove();
}
}
else
{
var runs = GetAssociatedRuns((FieldCode)field);
var rEnd = runs[runs.Count - 1];
foreach (var r in runs
.SkipWhile(r => !r.ContainsCharType(FieldCharValues.Separate))
.Skip(1)
.TakeWhile(r=>r!= rEnd))
{
r.Remove();
}
rEnd.InsertBeforeSelf(CreateSimpleTextRun(replacementText));
}
}
private static IList<Run> GetAssociatedRuns(FieldCode fieldCode)
{
Run rFieldCode = (Run)fieldCode.Parent;
Run rBegin = rFieldCode.PreviousSibling<Run>();
Run rCurrent = rFieldCode.NextSibling<Run>();
var runs = new List<Run>(new[] { rBegin, rCurrent });
while (!rCurrent.ContainsCharType(FieldCharValues.End))
{
rCurrent = rCurrent.NextSibling<Run>();
runs.Add(rCurrent);
};
return runs;
}
private static bool ContainsCharType(this Run run, FieldCharValues fieldCharType)
{
var fc = run.GetFirstChild<FieldChar>();
return fc == null
? false
: fc.FieldCharType.Value == fieldCharType;
}
You could try http://www.codeproject.com/KB/office/Fill_Mergefields.aspx which uses the Open XML SDK to do this.

Categories

Resources