combine two word documents into one [duplicate] - c#

I have around 10 word documents which I generate using open xml and other stuff.
Now I would like to create another word document and one by one I would like to join them into this newly created document.
I wish to use open xml, any hint would be appreciable.
Below is my code:
private void CreateSampleWordDocument()
{
//string sourceFile = Path.Combine("D:\\GeneralLetter.dot");
//string destinationFile = Path.Combine("D:\\New.doc");
string sourceFile = Path.Combine("D:\\GeneralWelcomeLetter.docx");
string destinationFile = Path.Combine("D:\\New.docx");
try
{
// Create a copy of the template file and open the copy
//File.Copy(sourceFile, destinationFile, true);
using (WordprocessingDocument document = WordprocessingDocument.Open(destinationFile, true))
{
// Change the document type to Document
document.ChangeDocumentType(DocumentFormat.OpenXml.WordprocessingDocumentType.Document);
//Get the Main Part of the document
MainDocumentPart mainPart = document.MainDocumentPart;
mainPart.Document.Save();
}
}
catch
{
}
}
Update( using AltChunks):
using (WordprocessingDocument myDoc = WordprocessingDocument.Open("D:\\Test.docx", true))
{
string altChunkId = "AltChunkId" + DateTime.Now.Ticks.ToString().Substring(0, 2) ;
MainDocumentPart mainPart = myDoc.MainDocumentPart;
AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(
AlternativeFormatImportPartType.WordprocessingML, altChunkId);
using (FileStream fileStream = File.Open("D:\\Test1.docx", FileMode.Open))
chunk.FeedData(fileStream);
AltChunk altChunk = new AltChunk();
altChunk.Id = altChunkId;
mainPart.Document
.Body
.InsertAfter(altChunk, mainPart.Document.Body.Elements<Paragraph>().Last());
mainPart.Document.Save();
}
Why this code overwrites the content of the last file when I use multiple files?
Update 2:
using (WordprocessingDocument myDoc = WordprocessingDocument.Open("D:\\Test.docx", true))
{
MainDocumentPart mainPart = myDoc.MainDocumentPart;
string altChunkId = "AltChunkId" + DateTime.Now.Ticks.ToString().Substring(0, 3);
AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.WordprocessingML, altChunkId);
using (FileStream fileStream = File.Open("d:\\Test1.docx", FileMode.Open))
{
chunk.FeedData(fileStream);
AltChunk altChunk = new AltChunk();
altChunk.Id = altChunkId;
mainPart.Document
.Body
.InsertAfter(altChunk, mainPart.Document.Body
.Elements<Paragraph>().Last());
mainPart.Document.Save();
}
using (FileStream fileStream = File.Open("d:\\Test2.docx", FileMode.Open))
{
chunk.FeedData(fileStream);
AltChunk altChunk = new AltChunk();
altChunk.Id = altChunkId;
mainPart.Document
.Body
.InsertAfter(altChunk, mainPart.Document.Body
.Elements<Paragraph>().Last());
}
using (FileStream fileStream = File.Open("d:\\Test3.docx", FileMode.Open))
{
chunk.FeedData(fileStream);
AltChunk altChunk = new AltChunk();
altChunk.Id = altChunkId;
mainPart.Document
.Body
.InsertAfter(altChunk, mainPart.Document.Body
.Elements<Paragraph>().Last());
}
}
This code is appending the Test2 data twice, in place of Test1 data as well.
Means I get:
Test
Test2
Test2
instead of :
Test
Test1
Test2

Using openXML SDK only, you can use AltChunk element to merge the multiple document into one.
This link the-easy-way-to-assemble-multiple-word-documents and this one How to Use altChunk for Document Assembly provide some samples.
EDIT 1
Based on your code that uses altchunk in the updated question (update#1), here is the VB.Net code I have tested and that works like a charm for me:
Using myDoc = DocumentFormat.OpenXml.Packaging.WordprocessingDocument.Open("D:\\Test.docx", True)
Dim altChunkId = "AltChunkId" + DateTime.Now.Ticks.ToString().Substring(0, 2)
Dim mainPart = myDoc.MainDocumentPart
Dim chunk = mainPart.AddAlternativeFormatImportPart(
DocumentFormat.OpenXml.Packaging.AlternativeFormatImportPartType.WordprocessingML, altChunkId)
Using fileStream As IO.FileStream = IO.File.Open("D:\\Test1.docx", IO.FileMode.Open)
chunk.FeedData(fileStream)
End Using
Dim altChunk = New DocumentFormat.OpenXml.Wordprocessing.AltChunk()
altChunk.Id = altChunkId
mainPart.Document.Body.InsertAfter(altChunk, mainPart.Document.Body.Elements(Of DocumentFormat.OpenXml.Wordprocessing.Paragraph).Last())
mainPart.Document.Save()
End Using
EDIT 2
The second issue (update#2)
This code is appending the Test2 data twice, in place of Test1 data as
well.
is related to altchunkid.
For each document you want to merge in the main document, you need to:
add an AlternativeFormatImportPart in the mainDocumentPart with an Id which must be unique. This element contains the inserted data
add in the body an Altchunk element in which you set the id to reference the previous AlternativeFormatImportPart.
In your code, you are using the same Id for all the AltChunks. It's why you see many time the same text.
I am not sure the altchunkid will be unique with your code: string altChunkId = "AltChunkId" + DateTime.Now.Ticks.ToString().Substring(0, 2);
If you don't need to set a specific value, I recommend you to not set explicitly the AltChunkId when you add the AlternativeFormatImportPart. Instead, you get the one generated by the SDK like this:
VB.Net
Dim chunk As AlternativeFormatImportPart = mainPart.AddAlternativeFormatImportPart(DocumentFormat.OpenXml.Packaging.AlternativeFormatImportPartType.WordprocessingML)
Dim altchunkid As String = mainPart.GetIdOfPart(chunk)
C#
AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(DocumentFormat.OpenXml.Packaging.AlternativeFormatImportPartType.WordprocessingML);
string altchunkid = mainPart.GetIdOfPart(chunk);

There is a nice wrapper API (Document Builder 2.2) around open xml specially designed to merge documents, with flexibility of choosing the paragraphs to merge etc. You can download it from here (update: moved to github).
The documentation and screen casts on how to use it are here.
Update: Code Sample
var sources = new List<Source>();
//Document Streams (File Streams) of the documents to be merged.
foreach (var stream in documentstreams)
{
var tempms = new MemoryStream();
stream.CopyTo(tempms);
sources.Add(new Source(new WmlDocument(stream.Length.ToString(), tempms), true));
}
var mergedDoc = DocumentBuilder.BuildDocument(sources);
mergedDoc.SaveAs(#"C:\TargetFilePath");
Types Source and WmlDocument are from Document Builder API.
You can even add the file paths directly if you choose to as:
sources.Add(new Source(new WmlDocument(#"C:\FileToBeMerged1.docx"));
sources.Add(new Source(new WmlDocument(#"C:\FileToBeMerged2.docx"));
Found this Nice Comparison between AltChunk and Document Builder approaches to merge documents - helpful to choose based on ones requirements.
You can also use DocX library to merge documents but I prefer Document Builder over this for merging documents.
Hope this helps.

The only thing missing in these answers is the for loop.
For those who just want to copy / paste it:
void MergeInNewFile(string resultFile, IList<string> filenames)
{
using (WordprocessingDocument document = WordprocessingDocument.Create(resultFile, WordprocessingDocumentType.Document))
{
MainDocumentPart mainPart = document.AddMainDocumentPart();
mainPart.Document = new Document(new Body());
foreach (string filename in filenames)
{
AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.WordprocessingML);
string altChunkId = mainPart.GetIdOfPart(chunk);
using (FileStream fileStream = File.Open(filename, FileMode.Open))
{
chunk.FeedData(fileStream);
}
AltChunk altChunk = new AltChunk { Id = altChunkId };
mainPart.Document.Body.AppendChild(altChunk);
}
mainPart.Document.Save();
}
}
All credits go to Chris and yonexbat

Easy to use in C#:
using System;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
namespace WordMergeProject
{
public class Program
{
private static void Main(string[] args)
{
byte[] word1 = File.ReadAllBytes(#"..\..\word1.docx");
byte[] word2 = File.ReadAllBytes(#"..\..\word2.docx");
byte[] result = Merge(word1, word2);
File.WriteAllBytes(#"..\..\word3.docx", result);
}
private static byte[] Merge(byte[] dest, byte[] src)
{
string altChunkId = "AltChunkId" + DateTime.Now.Ticks.ToString();
var memoryStreamDest = new MemoryStream();
memoryStreamDest.Write(dest, 0, dest.Length);
memoryStreamDest.Seek(0, SeekOrigin.Begin);
var memoryStreamSrc = new MemoryStream(src);
using (WordprocessingDocument doc = WordprocessingDocument.Open(memoryStreamDest, true))
{
MainDocumentPart mainPart = doc.MainDocumentPart;
AlternativeFormatImportPart altPart =
mainPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.WordprocessingML, altChunkId);
altPart.FeedData(memoryStreamSrc);
var altChunk = new AltChunk();
altChunk.Id = altChunkId;
OpenXmlElement lastElem = mainPart.Document.Body.Elements<AltChunk>().LastOrDefault();
if(lastElem == null)
{
lastElem = mainPart.Document.Body.Elements<Paragraph>().Last();
}
//Page Brake einfügen
Paragraph pageBreakP = new Paragraph();
Run pageBreakR = new Run();
Break pageBreakBr = new Break() { Type = BreakValues.Page };
pageBreakP.Append(pageBreakR);
pageBreakR.Append(pageBreakBr);
return memoryStreamDest.ToArray();
}
}
}

My solution :
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
namespace TestFusionWord
{
internal class Program
{
public static void MergeDocx(List<string> ListPathFilesToMerge, string DestinationPathFile, bool OverWriteDestination, bool WithBreakPage)
{
#region Control arguments
List<string> ListError = new List<string>();
if (ListPathFilesToMerge == null || ListPathFilesToMerge.Count == 0)
{
ListError.Add("Il n'y a aucun fichier à fusionner dans la liste passée en paramètre ListPathFilesToMerge");
}
else
{
foreach (var item in ListPathFilesToMerge.Where(x => Path.GetExtension(x.ToLower()) != ".docx"))
{
ListError.Add(string.Format("Le fichier '{0}' indiqué dans la liste passée en paramètre ListPathFilesToMerge n'a pas l'extension .docx", item));
}
foreach (var item in ListPathFilesToMerge.Where(x => !File.Exists(x)))
{
ListError.Add(string.Format("Le fichier '{0}' indiqué dans la liste passée en paramètre ListPathFilesToMerge n'existe pas", item));
}
}
if (string.IsNullOrWhiteSpace(DestinationPathFile))
{
ListError.Add("Le fichier destination FinalPathFile passé en paramètre ne peut être vide");
}
else
{
if (Path.GetExtension(DestinationPathFile.ToLower()) != ".docx")
{
ListError.Add(string.Format("Le fichier destination '{0}' indiqué dans le paramètre DestinationPathFile n'a pas l'extension .docx", DestinationPathFile));
}
if (File.Exists(DestinationPathFile) && !OverWriteDestination)
{
ListError.Add(string.Format("Le fichier destination '{0}' existe déjà. Utilisez l'argument OverWriteDestination si vous souhaitez l'écraser", DestinationPathFile));
}
}
if (ListError.Any())
{
string MessageError = "Des erreurs ont été rencontrés, détail : " + Environment.NewLine + ListError.Select(x => "- " + x).Aggregate((x, y) => x + Environment.NewLine + y);
throw new ArgumentException(MessageError);
}
#endregion Control arguments
#region Merge Files
//Suppression du fichier destination (aucune erreur déclenchée si le fichier n'existe pas)
File.Delete(DestinationPathFile);
//Création du fichier destination à vide
using (WordprocessingDocument document = WordprocessingDocument.Create(DestinationPathFile, WordprocessingDocumentType.Document))
{
MainDocumentPart mainPart = document.AddMainDocumentPart();
mainPart.Document = new Document(new Body());
document.MainDocumentPart.Document.Save();
}
//Fusion des documents
using (WordprocessingDocument myDoc = WordprocessingDocument.Open(DestinationPathFile, true))
{
MainDocumentPart mainPart = myDoc.MainDocumentPart;
Body body = mainPart.Document.Body;
for (int i = 0; i < ListPathFilesToMerge.Count; i++)
{
string currentpathfile = ListPathFilesToMerge[i];
AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.WordprocessingML);
string altchunkid = mainPart.GetIdOfPart(chunk);
using (FileStream fileStream = File.Open(currentpathfile, FileMode.Open))
chunk.FeedData(fileStream);
AltChunk altChunk = new AltChunk();
altChunk.Id = altchunkid;
OpenXmlElement last = body.Elements().LastOrDefault(e => e is AltChunk || e is Paragraph);
body.InsertAfter(altChunk, last);
if (WithBreakPage && i < ListPathFilesToMerge.Count - 1) // If its not the last file, add breakpage
{
last = body.Elements().LastOrDefault(e => e is AltChunk || e is Paragraph);
last.InsertAfterSelf(new Paragraph(new Run(new Break() { Type = BreakValues.Page })));
}
}
mainPart.Document.Save();
}
#endregion Merge Files
}
private static int Main(string[] args)
{
try
{
string DestinationPathFile = #"C:\temp\testfusion\docfinal.docx";
List<string> ListPathFilesToMerge = new List<string>()
{
#"C:\temp\testfusion\fichier1.docx",
#"C:\temp\testfusion\fichier2.docx",
#"C:\temp\testfusion\fichier3.docx"
};
ListPathFilesToMerge.Sort(); //Sort for always have the same file
MergeDocx(ListPathFilesToMerge, DestinationPathFile, true, true);
#if DEBUG
Process.Start(DestinationPathFile); //open file
#endif
return 0;
}
catch (Exception Ex)
{
Console.Error.WriteLine(Ex.Message);
//Log exception here
return -1;
}
}
}
}

Related

How to convert XML to Byte to File/PDF

I have successfully done "find and replace" which created an xml. Now I want to convert the newly created xml file to pdf which will be attached as a file and sent in a mail.
The result of the Base64String was tested on a base64 pdf file converter but the pdf cannot be opened. Got this error: Something went wrong couldn't open the file
HOW CAN I MAKE THIS WORK?
public async Task<string> CreateDocument(string PolicyNumber)
{
var policy = await _context.Policy.SingleOrDefaultAsync(p => p.PolicyNumber == PolicyNumber);
ArgumentNullException.ThrowIfNull(policy, "Policy Not Available");
//CreatePolicyDocument
//create policy document
var files = #"C:\Users\PATHTODOCUMENT\holderTest.docx";
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(files, true))
{
string docText;
using (StreamReader sr = new StreamReader(wordDoc.MainDocumentPart.GetStream()))
{
docText = sr.ReadToEnd();
}
Regex regexText = new Regex("XCONCLUSION_DATEX");
var newWordText = regexText.Replace(docText, "Hi Everyone!");
using (StreamWriter sw = new StreamWriter(wordDoc.MainDocumentPart.GetStream(FileMode.Create)))
{
sw.Write(newWordText);
Encoding encoding = Encoding.UTF8;
byte[] docAsBytes = encoding.GetBytes(newWordText);
File.WriteAllBytes("hello.pdf", docAsBytes);
var file = Convert.ToBase64String(docAsBytes);
}
}
//send message
//
return "";
}
PDF file has its own construct,so you can't generate a pdf file with the contentstring of xml file dirctly.
If you really want to convert xml contentstring to PDF,you could try iTextSharp.
I tried with a simple demo,and here's the code:
[HttpPost]
public IActionResult XMLtoPDF([FromForm] Try T1)
{
var streamContent = new StreamContent(T1.file.OpenReadStream());
var filestring = streamContent.ReadAsStringAsync().Result;
MemoryStream outputStream = new MemoryStream();
Document doc = new Document();
PdfWriter writer = PdfWriter.GetInstance(doc, outputStream);
//PDF settings
PdfDestination pdfDest = new PdfDestination(PdfDestination.XYZ, 0, doc.PageSize.Height, 1f);
doc.Open();
var paragraph = new Paragraph(filestring);
doc.Add(paragraph);
doc.Close();
outputStream.Close();
var pdfArray = outputStream.ToArray();
return File(pdfArray, "application/pdf");
}
Result:

Merge 2 Word Document using c#

I am trying to merge 2 word documents in binary format, however, it only gets the first document. The resulting document should have all elements from each source document included with formatting.
Here is the code so far. Using OpenXml by the way.
private static byte[] Merge(byte[] dest, byte[] src)
{
string altChunkId = "AltChunkId" + DateTime.Now.Ticks.ToString();
var memoryStreamDest = new MemoryStream();
memoryStreamDest.Write(dest, 0, dest.Length);
memoryStreamDest.Seek(0, SeekOrigin.Begin);
using (WordprocessingDocument doc = WordprocessingDocument.Open(memoryStreamDest, true))
{
MainDocumentPart mainPart = doc.MainDocumentPart;
Paragraph para = new Paragraph(new Run((new Break() { Type = BreakValues.Page })));
mainPart.Document.Body.InsertAfter(para, mainPart.Document.Body.LastChild);
//Insert the source file into the target file using AltChunk
AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(
AlternativeFormatImportPartType.WordprocessingML, altChunkId);
using (MemoryStream mem = new MemoryStream())
{
mem.Write(src, 0, (int)src.Length);
mem.Seek(0, SeekOrigin.Begin);
chunk.FeedData(mem);
}
AltChunk altChunk = new AltChunk();
altChunk.Id = altChunkId;
mainPart.Document.Body.InsertAfter(altChunk, mainPart.Document.Body.Descendants<Paragraph>().Last());
mainPart.Document.Save();
return memoryStreamDest.ToArray();
}
}

Throw System.IO.IsolatedStorage.IsolatedStorageException while word file read using openxml API

We are update document using openxml programatically. while we start to read file using below code, Exception throws at that time.
This exception only throws when file size is bigger.
using (WordprocessingDocument wdoc = WordprocessingDocument.Open(strPath, true))
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(m_fileName, true))
{
MainDocumentPart mainPart = wordDoc.MainDocumentPart;
foreach (CustomXmlPart customXmlPart in mainPart.CustomXmlParts)
{
bool exists = false;
using (XmlTextReader xReader = new XmlTextReader(customXmlPart.GetStream(FileMode.Open, FileAccess.Read)))
{
XmlDocument xmlDocument = new XmlDocument();
xReader.MoveToContent();
exists = xReader.NamespaceURI.Equals("urn:TTCProperties:namespace");
if (exists)
{
xmlDocument.Load(xReader);
XmlNodeList n = xmlDocument.GetElementsByTagName("Status");
if (n != null)
{
foreach (XmlNode curr in n)
{
Console.WriteLine(curr.InnerText);
curr.InnerText = "New String";
}
}
StreamReader sr = new StreamReader(customXmlPart.GetStream());
Stream stream = customXmlPart.GetStream();
stream.SetLength(0);
using (StreamWriter ts = new StreamWriter(stream))
ts.Write(xmlDocument.InnerXml);
wordDoc.MainDocumentPart.Document.Save(mainPart);
how can resolve this runtime Exception during file reading

Grab all of the pages of a PDF using textsharp

I am getting a pfd using the older version of itextsharp with this code
string Oldfile = #"C:/test.pdf"; // Gets the Template
(new FileInfo("C:/C:/test.pdf")).Directory.Create(); // Go create this folder if it's not there
string NewFile = "C:/test.pdf";
PdfReader reader = new PdfReader(Oldfile);
iTextSharp.text.Rectangle Size = reader.GetPageSizeWithRotation(1);
Document document = new Document(Size);
// MemoryStream memory_stream = new MemoryStream();
FileStream fs = new FileStream(NewFile, FileMode.Create, FileAccess.Write);
PdfWriter weiter = PdfWriter.GetInstance(document, fs);
document.Open();
PdfContentByte cb = weiter.DirectContent;
PdfImportedPage page = weiter.GetImportedPage(reader, 1);
//PdfImportedPage page2 = weiter.GetImportedPage(reader, 2);
cb.AddTemplate(page, 0, 0);
The problem I am having is when it gets that file it has 2 pages in that pdf but it only gets the 1st page and adds lines and saves the only 1st page of the pdf I want to be able to grab both of them or is there a way to merge them after wards
I bet you need to iterate all pages.
using System;
using System.IO;
using System.Collections.Generic;
using iTextSharp.text;
using iTextSharp.text.pdf;
namespace TestAnything
{
class Program
{
static void Main(string[] args)
{
List<string> filesToMerge = new List<string> { #"c:\temp\1.pdf", #"c:\temp\2.pdf" };
FileInfo destinationFile = new FileInfo(#"c:\temp\merge.pdf");
if (File.Exists(destinationFile.FullName))
File.Delete(destinationFile.FullName);
MergeFiles(filesToMerge, destinationFile);
}
public static void MergeFiles(List<string> sourceFiles, FileInfo destinationFile)
{
if (sourceFiles == null || sourceFiles.Count == 0)
throw new ArgumentNullException("blahhh.");
PdfReader reader = new PdfReader(sourceFiles[0]);
Document document = new Document(reader.GetPageSizeWithRotation(1));
PdfCopy writer = new PdfCopy(document, new FileStream(destinationFile.FullName, FileMode.Create));
document.Open();
try
{
foreach (string sourceFile in sourceFiles)
{
reader = new PdfReader(sourceFile);
reader.ConsolidateNamedDestinations();
for (int x = 1; x <= reader.NumberOfPages; x++)
writer.AddPage(writer.GetImportedPage(reader, x));
PRAcroForm form = reader.AcroForm;
if (form != null)
writer.CopyAcroForm(reader);
}
}
finally
{
if (document.IsOpen())
document.Close();
}
}
}
}

Replace header placeholder, not working now

I want to replace the placeholder 'plcDate' in the header of the doc. Headers start from 2nd page in my doc template.
I'm using the below code. But 'headDate' is always null.
Don't know whether I've to change the code or the doc template.
using (WordprocessingDocument theDoc = WordprocessingDocument.Open(NewPath, true))
{
MainDocumentPart mainPart = theDoc.MainDocumentPart;
foreach (HeaderPart hpart in mainPart.HeaderParts)
{
SdtElement headDate = hpart.Header.Descendants<SdtElement>().Where(r => r.SdtProperties.GetFirstChild<Tag>().Val == "plcDate").SingleOrDefault();
if (headDate != null)
{
headDate.Append(new DocumentFormat.OpenXml.Wordprocessing.Paragraph(new Run(new Text(dateValue))));
}
}
mainPart.Document.Save();
}
Worked after using the following code:
using (WordprocessingDocument theDoc = WordprocessingDocument.Open(NewPath, true))
{
MainDocumentPart mainPart = theDoc.MainDocumentPart;
string content = null;
using (StreamReader reader = new StreamReader(theDoc.MainDocumentPart.HeaderParts.First().GetStream()))
{
content = reader.ReadToEnd();
}
Regex exheadDate = new Regex("plcDate");
content = exheadDate.Replace(content, "27/07/1992");
using (StreamWriter writer = new StreamWriter(theDoc.MainDocumentPart.HeaderParts.First().GetStream(FileMode.Create)))
{
writer.Write(content);
}
mainPart.Document.Save();
}

Categories