When merging the multiple word templates using OpenXML in C#, they won’t include the headers or even headers are not applied to the specific page as what page template header is located. I’ve spent hours and hours and haven’t found any luck resolving. It is pretty easy to merge documents without headers.
Here is the code I tried:
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing
public class OpenXmlMerging
{
public void MergeTemplates(string targetFilePath, List<string> sourceFilePaths)
{
using (WordprocessingDocument targetDoc = WordprocessingDocument.Create(targetFilePath, DocumentFormat.OpenXml.WordprocessingDocumentType.Document))
{
// Create the main document part and add an empty document to it
MainDocumentPart mainPart = targetDoc.AddMainDocumentPart();
mainPart.Document = new Document(new Body());
// Initialize dictionaries to store parts
var headerParts = new Dictionary<string, byte[]>();
var imageParts = new Dictionary<string, (string ContentType, byte[] Data)>();
var styleParts = new Dictionary<string, byte[]>();
var themeParts = new Dictionary<string, byte[]>();
foreach (var sourceFilePath in sourceFilePaths)
{
using (WordprocessingDocument sourceDoc = WordprocessingDocument.Open(sourceFilePath, false))
{
CollectHeaderParts(sourceDoc.MainDocumentPart.HeaderParts, headerParts);
// Collect images, headers, styles, and themes
CollectImageParts(sourceDoc.MainDocumentPart.ImageParts, imageParts);
CollectStyles(sourceDoc.MainDocumentPart.StyleDefinitionsPart, styleParts);
CollectThemes(sourceDoc.MainDocumentPart.ThemePart, themeParts);
// Merge document body
MergeDocumentBodies(targetDoc, sourceDoc);
// Insert a page break if this is not the last template
if (sourceFilePath != sourceFilePaths.Last())
{
//InsertPageBreak(targetDoc);
// InsertSectionBreak(targetDoc);
}
// Handle images
UpdateImageReferences(sourceDoc, targetDoc, imageParts);
}
}
// Add headers, images, styles, and themes to the target document
AddHeaders(targetDoc, headerParts);
AddImages(targetDoc, imageParts);
AddStyles(targetDoc, styleParts);
AddThemes(targetDoc, themeParts);
// Update header references after all headers have been added
// UpdateHeaderReferences(targetDoc);
// Debug header content to verify inclusion
// DebugHeaderReferences(targetDoc);
// DebugHeaderContent(targetDoc);
// Save and close the target document
SaveAndClose(targetDoc);
}
}
private static void CollectHeaderParts(IEnumerable<HeaderPart> headerParts, Dictionary<string, byte[]> headerDict)
{
foreach (var headerPart in headerParts)
{
string uri = headerPart.Uri.ToString();
using (var stream = headerPart.GetStream())
{
headerDict[uri] = ReadStreamToByteArray(stream);
}
}
}
private static void CollectImageParts(IEnumerable<ImagePart> imageParts, Dictionary<string, (string ContentType, byte[] Data)> imageDict)
{
foreach (var imagePart in imageParts)
{
string uri = imagePart.Uri.ToString();
using (var stream = imagePart.GetStream())
{
byte[] data = ReadStreamToByteArray(stream);
imageDict[uri] = (imagePart.ContentType, data);
}
}
}
private static void CollectStyles(StyleDefinitionsPart stylePart, Dictionary<string, byte[]> styleDict)
{
if (stylePart != null)
{
string uri = stylePart.Uri.ToString();
using (var stream = stylePart.GetStream())
{
styleDict[uri] = ReadStreamToByteArray(stream);
}
}
}
private static void CollectThemes(ThemePart themePart, Dictionary<string, byte[]> themeDict)
{
if (themePart != null)
{
string uri = themePart.Uri.ToString();
using (var stream = themePart.GetStream())
{
themeDict[uri] = ReadStreamToByteArray(stream);
}
}
}
private static byte[] ReadStreamToByteArray(Stream stream)
{
using (var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
private static void MergeDocumentBodies(WordprocessingDocument targetDoc, WordprocessingDocument sourceDoc)
{
var sourceBody = sourceDoc.MainDocumentPart.Document.Body;
var targetBody = targetDoc.MainDocumentPart.Document.Body;
foreach (var element in sourceBody.Elements())
{
targetBody.Append(element.CloneNode(true));
}
}
private static void InsertPageBreak(WordprocessingDocument document)
{
var body = document.MainDocumentPart.Document.Body;
var paragraph = new Paragraph(new Run(new Break() { Type = BreakValues.Page }));
body.Append(paragraph);
}
/*
private static void UpdateImageReferences(WordprocessingDocument sourceDoc, WordprocessingDocument targetDoc, Dictionary<string, (string ContentType, byte[] Data)> imageParts)
{
var targetMainPart = targetDoc.MainDocumentPart;
var sourceMainPart = sourceDoc.MainDocumentPart;
foreach (var imagePart in sourceMainPart.ImageParts)
{
string imageUri = imagePart.Uri.ToString();
if (imageParts.ContainsKey(imageUri))
{
// Create a new ImagePart in the target document
var targetImagePart = targetMainPart.AddNewPart<ImagePart>(imageParts[imageUri].ContentType);
using (var stream = new MemoryStream(imageParts[imageUri].Data))
{
targetImagePart.FeedData(stream);
}
// Update document body references to the new ImagePart
var imageReferences = targetDoc.MainDocumentPart.Document.Body.Descendants<DocumentFormat.OpenXml.Drawing.Blip>().ToList();
foreach (var blip in imageReferences)
{
var blipEmbed = blip.Embed;
if (blipEmbed != null && blipEmbed.Value == sourceMainPart.GetIdOfPart(imagePart))
{
blip.Embed = targetMainPart.GetIdOfPart(targetImagePart);
}
}
}
}
}
*/
private static void UpdateImageReferences(WordprocessingDocument sourceDoc, WordprocessingDocument targetDoc, Dictionary<string, (string ContentType, byte[] Data)> imageParts)
{
var targetMainPart = targetDoc.MainDocumentPart;
var sourceMainPart = sourceDoc.MainDocumentPart;
var blips = targetDoc.MainDocumentPart.Document.Body.Descendants<DocumentFormat.OpenXml.Drawing.Blip>().ToList();
foreach (var blip in blips)
{
var embed = blip.Embed?.Value;
if (embed != null)
{
foreach (var imagePart in sourceMainPart.ImageParts)
{
string imageUri = imagePart.Uri.ToString();
if (imageParts.ContainsKey(imageUri))
{
string targetImagePartId = targetMainPart.GetIdOfPart(targetMainPart.AddNewPart<ImagePart>(imageParts[imageUri].ContentType));
using (var stream = new MemoryStream(imageParts[imageUri].Data))
{
targetMainPart.GetPartById(targetImagePartId).FeedData(stream);
}
if (embed == sourceMainPart.GetIdOfPart(imagePart))
{
blip.Embed = targetMainPart.GetIdOfPart(targetMainPart.GetPartById(targetImagePartId));
}
}
}
}
}
}
private static void AddHeaders(WordprocessingDocument targetDoc, Dictionary<string, byte[]> headerParts)
{
var mainPart = targetDoc.MainDocumentPart;
// Helper function to generate unique IDs
string GenerateUniqueId(IEnumerable<string> existingIds)
{
int i = 1;
while (true)
{
var id = $"rId{i}";
if (!existingIds.Contains(id))
{
return id;
}
i++;
}
}
// Get existing header part IDs
var existingHeaderParts = mainPart.HeaderParts.ToList();
var existingHeaderIds = existingHeaderParts.Select(hp => mainPart.GetIdOfPart(hp)).ToList();
var headerPartIdMap = new Dictionary<string, string>();
// Add header parts
foreach (var (headerUri, headerData) in headerParts)
{
if (!headerPartIdMap.ContainsKey(headerUri))
{
var uniqueId = GenerateUniqueId(existingHeaderIds);
// Add new header part
var newHeaderPart = mainPart.AddNewPart<HeaderPart>(uniqueId);
using (var stream = new MemoryStream(headerData))
{
newHeaderPart.FeedData(stream);
}
headerPartIdMap[headerUri] = uniqueId;
existingHeaderIds.Add(uniqueId); // Update the list of existing IDs
}
}
// Update header references in section properties
foreach (var sectionProperties in mainPart.Document.Body.Elements<SectionProperties>())
{
foreach (var (headerUri, headerId) in headerPartIdMap)
{
var headerType = HeaderFooterValues.Default; // Default for all pages if not specified
if (headerUri.Contains("First")) // Customize based on actual header URIs
{
headerType = HeaderFooterValues.First;
}
else if (headerUri.Contains("Even")) // Customize based on actual header URIs
{
headerType = HeaderFooterValues.Even;
}
// Ensure the header reference is added for this section
if (!sectionProperties.Elements<HeaderReference>().Any(hr => hr.Id == headerId))
{
sectionProperties.Append(new HeaderReference()
{
Type = headerType,
Id = headerId
});
}
}
}
// Save the changes
mainPart.Document.Save();
}
private static void UpdateHeaderReferences(WordprocessingDocument targetDoc)
{
var mainPart = targetDoc.MainDocumentPart;
// Map existing header parts by their ID
var headerPartIdMap = mainPart.HeaderParts.ToDictionary(
hp => mainPart.GetIdOfPart(hp),
hp => hp
);
// Update header references in each section
foreach (var sectionProperties in mainPart.Document.Body.Elements<SectionProperties>())
{
var headerReferences = sectionProperties.Elements<HeaderReference>().ToList();
foreach (var (headerId, headerPart) in headerPartIdMap)
{
var headerType = HeaderFooterValues.Default; // Default for all pages if not specified
if (headerPart.Uri.ToString().Contains("First")) // Customize based on actual header URIs
{
headerType = HeaderFooterValues.First;
}
else if (headerPart.Uri.ToString().Contains("Even")) // Customize based on actual header URIs
{
headerType = HeaderFooterValues.Even;
}
// Check if the header reference already exists
if (!headerReferences.Any(hr => hr.Id == headerId))
{
sectionProperties.Append(new HeaderReference()
{
Type = headerType,
Id = headerId
});
}
}
}
// Save the changes
mainPart.Document.Save();
}
/*
private static void UpdateHeaderReferences(WordprocessingDocument targetDoc)
{
var mainPart = targetDoc.MainDocumentPart;
// Create a dictionary to store header references
var headerIdToPartId = new Dictionary<string, string>();
// Gather existing header IDs and their corresponding parts
foreach (var headerPart in mainPart.HeaderParts)
{
var relationshipId = mainPart.GetIdOfPart(headerPart);
headerIdToPartId[relationshipId] = relationshipId;
}
// Update header references in each section
foreach (var section in mainPart.Document.Body.Elements<SectionProperties>())
{
foreach (var headerReference in section.Elements<HeaderReference>())
{
var headerId = headerReference.Id?.Value;
if (headerId != null && headerIdToPartId.ContainsKey(headerId))
{
headerReference.Id = new StringValue(headerId);
}
}
}
// Save the changes
mainPart.Document.Save();
}
*/
private static void InsertSectionBreak(WordprocessingDocument document)
{
var body = document.MainDocumentPart.Document.Body;
// Add a new paragraph to separate sections
var paragraph = new Paragraph(new Run(new Break() { Type = BreakValues.Page }));
body.Append(paragraph);
// Create SectionProperties to define new section properties
var sectionProperties = new SectionProperties(
new PageSize() { Width = 12240, Height = 15840 }, // Example page size
new PageMargin() { Top = 1440, Bottom = 1440, Left = 1440, Right = 1440 } // Example margins
);
// Append SectionProperties to the document body
body.Append(sectionProperties);
}
private static void AddImages(WordprocessingDocument targetDoc, Dictionary<string, (string ContentType, byte[] Data)> imageParts)
{
var existingImageParts = targetDoc.MainDocumentPart.ImageParts.ToList();
var existingImageUris = new HashSet<string>(existingImageParts.Select(ip => ip.Uri.ToString()));
foreach (var (uri, (contentType, data)) in imageParts)
{
if (!existingImageUris.Contains(uri))
{
var newImagePart = targetDoc.MainDocumentPart.AddNewPart<ImagePart>(contentType);
using (var stream = new MemoryStream(data))
{
newImagePart.FeedData(stream);
}
}
}
}
private static void AddStyles(WordprocessingDocument targetDoc, Dictionary<string, byte[]> styleParts)
{
var targetStylesPart = targetDoc.MainDocumentPart.StyleDefinitionsPart;
if (targetStylesPart == null)
{
targetStylesPart = targetDoc.MainDocumentPart.AddNewPart<StyleDefinitionsPart>();
}
foreach (var (uri, data) in styleParts)
{
using (var stream = new MemoryStream(data))
{
targetStylesPart.FeedData(stream);
}
}
}
private static void AddThemes(WordprocessingDocument targetDoc, Dictionary<string, byte[]> themeParts)
{
var targetThemePart = targetDoc.MainDocumentPart.ThemePart;
if (targetThemePart == null)
{
targetThemePart = targetDoc.MainDocumentPart.AddNewPart<ThemePart>();
}
foreach (var (uri, data) in themeParts)
{
using (var stream = new MemoryStream(data))
{
targetThemePart.FeedData(stream);
}
}
}
private static void DebugHeaderReferences(WordprocessingDocument targetDoc)
{
var mainPart = targetDoc.MainDocumentPart;
foreach (var sectionProperties in mainPart.Document.Body.Elements<SectionProperties>())
{
foreach (var headerReference in sectionProperties.Elements<HeaderReference>())
{
Console.WriteLine($"Header Reference ID: {headerReference.Id}, Type: {headerReference.Type}");
}
}
}
private static void DebugHeaderContent(WordprocessingDocument targetDoc)
{
var mainPart = targetDoc.MainDocumentPart;
Console.WriteLine("Number of headers: " + mainPart.HeaderParts.Count());
foreach (var headerPart in mainPart.HeaderParts)
{
Console.WriteLine("Header part URI: " + headerPart.Uri);
using (var stream = headerPart.GetStream())
{
using (var reader = new StreamReader(stream))
{
var headerContent = reader.ReadToEnd();
Console.WriteLine("Header content: " + headerContent);
}
}
}
}
private static void SaveAndClose(WordprocessingDocument document)
{
document.MainDocumentPart.Document.Save();
//document.Close(); // Ensure the document is closed
}
}
New contributor
Asad Moosa is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
1