using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO.Packaging; using System.Xml.Linq; using System.IO; using System.Reflection; using System.IO.Compression; using System.Security.Principal; using System.Globalization; namespace Novacode { internal static class HelperFunctions { internal static void CreateRelsPackagePart(DocX Document, Uri uri) { PackagePart pp = Document.package.CreatePart(uri, "application/vnd.openxmlformats-package.relationships+xml", CompressionOption.Maximum); using (TextWriter tw = new StreamWriter(pp.GetStream())) { XDocument d = new XDocument ( new XDeclaration("1.0", "UTF-8", "yes"), new XElement(XName.Get("Relationships", DocX.rel.NamespaceName)) ); var root = d.Root; d.Save(tw); } } internal static int GetSize(XElement Xml) { switch (Xml.Name.LocalName) { case "tab": return 1; case "br": return 1; case "t": goto case "delText"; case "delText": return Xml.Value.Length; case "tr": goto case "br"; case "tc": goto case "br"; default: return 0; } } internal static string GetText(XElement e) { StringBuilder sb = new StringBuilder(); GetTextRecursive(e, ref sb); return sb.ToString(); } internal static void GetTextRecursive(XElement Xml, ref StringBuilder sb) { sb.Append(ToText(Xml)); if (Xml.HasElements) foreach (XElement e in Xml.Elements()) GetTextRecursive(e, ref sb); } internal static List GetFormattedText(XElement e) { List alist = new List(); GetFormattedTextRecursive(e, ref alist); return alist; } internal static void GetFormattedTextRecursive(XElement Xml, ref List alist) { FormattedText ft = ToFormattedText(Xml); FormattedText last = null; if (ft != null) { if (alist.Count() > 0) last = alist.Last(); if (last != null && last.CompareTo(ft) == 0) { // Update text of last entry. last.text += ft.text; } else { if (last != null) ft.index = last.index + last.text.Length; alist.Add(ft); } } if (Xml.HasElements) foreach (XElement e in Xml.Elements()) GetFormattedTextRecursive(e, ref alist); } internal static FormattedText ToFormattedText(XElement e) { // The text representation of e. String text = ToText(e); if (text == String.Empty) return null; // e is a w:t element, it must exist inside a w:r element, lets climb until we find it. while (!e.Name.Equals(XName.Get("r", DocX.w.NamespaceName))) e = e.Parent; // e is a w:r element, lets find the rPr element. XElement rPr = e.Element(XName.Get("rPr", DocX.w.NamespaceName)); FormattedText ft = new FormattedText(); ft.text = text; ft.index = 0; ft.formatting = null; // Return text with formatting. if (rPr != null) ft.formatting = Formatting.Parse(rPr); return ft; } internal static string ToText(XElement e) { switch (e.Name.LocalName) { case "tab": return "\t"; case "br": return "\n"; case "t": goto case "delText"; case "delText": { if (e.Parent != null && e.Parent.Name.LocalName == "r") { XElement run = e.Parent; var rPr = run.Elements().FirstOrDefault(a => a.Name.LocalName == "rPr"); if (rPr != null) { var caps = rPr.Elements().FirstOrDefault(a => a.Name.LocalName == "caps"); if (caps != null) return e.Value.ToUpper(); } } return e.Value; } case "tr": goto case "br"; case "tc": goto case "tab"; default: return ""; } } internal static XElement CloneElement(XElement element) { return new XElement ( element.Name, element.Attributes(), element.Nodes().Select ( n => { XElement e = n as XElement; if (e != null) return CloneElement(e); return n; } ) ); } internal static PackagePart CreateOrGetSettingsPart(Package package) { PackagePart settingsPart; Uri settingsUri = new Uri("/word/settings.xml", UriKind.Relative); if (!package.PartExists(settingsUri)) { settingsPart = package.CreatePart(settingsUri, "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml", CompressionOption.Maximum); PackagePart mainDocumentPart = package.GetParts().Where ( p => p.ContentType.Equals ( "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", StringComparison.CurrentCultureIgnoreCase ) ).Single(); mainDocumentPart.CreateRelationship(settingsUri, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings"); XDocument settings = XDocument.Parse (@" " ); XElement themeFontLang = settings.Root.Element(XName.Get("themeFontLang", DocX.w.NamespaceName)); themeFontLang.SetAttributeValue(XName.Get("val", DocX.w.NamespaceName), CultureInfo.CurrentCulture); // Save the settings document. using (TextWriter tw = new StreamWriter(settingsPart.GetStream())) settings.Save(tw); } else settingsPart = package.GetPart(settingsUri); return settingsPart; } internal static void CreateCustomPropertiesPart(DocX document) { PackagePart customPropertiesPart = document.package.CreatePart(new Uri("/docProps/custom.xml", UriKind.Relative), "application/vnd.openxmlformats-officedocument.custom-properties+xml", CompressionOption.Maximum); XDocument customPropDoc = new XDocument ( new XDeclaration("1.0", "UTF-8", "yes"), new XElement ( XName.Get("Properties", DocX.customPropertiesSchema.NamespaceName), new XAttribute(XNamespace.Xmlns + "vt", DocX.customVTypesSchema) ) ); using (TextWriter tw = new StreamWriter(customPropertiesPart.GetStream(FileMode.Create, FileAccess.Write))) customPropDoc.Save(tw, SaveOptions.None); document.package.CreateRelationship(customPropertiesPart.Uri, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties"); } internal static XDocument DecompressXMLResource(string manifest_resource_name) { // XDocument to load the compressed Xml resource into. XDocument document; // Get a reference to the executing assembly. Assembly assembly = Assembly.GetExecutingAssembly(); // Open a Stream to the embedded resource. Stream stream = assembly.GetManifestResourceStream(manifest_resource_name); // Decompress the embedded resource. using (GZipStream zip = new GZipStream(stream, CompressionMode.Decompress)) { // Load this decompressed embedded resource into an XDocument using a TextReader. using (TextReader sr = new StreamReader(zip)) { document = XDocument.Load(sr); } } // Return the decompressed Xml as an XDocument. return document; } /// /// If this document does not contain a /word/styles.xml add the default one generated by Microsoft Word. /// /// /// /// internal static XDocument AddDefaultStylesXml(Package package) { XDocument stylesDoc; // Create the main document part for this package PackagePart word_styles = package.CreatePart(new Uri("/word/styles.xml", UriKind.Relative), "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", CompressionOption.Maximum); stylesDoc = HelperFunctions.DecompressXMLResource("Novacode.Resources.default_styles.xml.gz"); XElement lang = stylesDoc.Root.Element(XName.Get("docDefaults", DocX.w.NamespaceName)).Element(XName.Get("rPrDefault", DocX.w.NamespaceName)).Element(XName.Get("rPr", DocX.w.NamespaceName)).Element(XName.Get("lang", DocX.w.NamespaceName)); lang.SetAttributeValue(XName.Get("val", DocX.w.NamespaceName), CultureInfo.CurrentCulture); // Save /word/styles.xml using (TextWriter tw = new StreamWriter(word_styles.GetStream(FileMode.Create, FileAccess.Write))) stylesDoc.Save(tw, SaveOptions.None); PackagePart mainDocumentPart = package.GetParts().Where ( p => p.ContentType.Equals ( "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", StringComparison.CurrentCultureIgnoreCase ) ).Single(); mainDocumentPart.CreateRelationship(word_styles.Uri, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"); return stylesDoc; } static internal XElement CreateEdit(EditType t, DateTime edit_time, object content) { if (t == EditType.del) { foreach (object o in (IEnumerable)content) { if (o is XElement) { XElement e = (o as XElement); IEnumerable ts = e.DescendantsAndSelf(XName.Get("t", DocX.w.NamespaceName)); for (int i = 0; i < ts.Count(); i++) { XElement text = ts.ElementAt(i); text.ReplaceWith(new XElement(DocX.w + "delText", text.Attributes(), text.Value)); } } } } return ( new XElement(DocX.w + t.ToString(), new XAttribute(DocX.w + "id", 0), new XAttribute(DocX.w + "author", WindowsIdentity.GetCurrent().Name), new XAttribute(DocX.w + "date", edit_time), content) ); } internal static XElement CreateTable(int rowCount, int columnCount) { XElement newTable = new XElement ( XName.Get("tbl", DocX.w.NamespaceName), new XElement ( XName.Get("tblPr", DocX.w.NamespaceName), new XElement(XName.Get("tblStyle", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), "TableGrid")), new XElement(XName.Get("tblW", DocX.w.NamespaceName), new XAttribute(XName.Get("w", DocX.w.NamespaceName), "5000"), new XAttribute(XName.Get("type", DocX.w.NamespaceName), "auto")), new XElement(XName.Get("tblLook", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), "04A0")) ) ); XElement tableGrid = new XElement(XName.Get("tblGrid", DocX.w.NamespaceName)); for (int i = 0; i < columnCount; i++) tableGrid.Add(new XElement(XName.Get("gridCol", DocX.w.NamespaceName), new XAttribute(XName.Get("w", DocX.w.NamespaceName), "2310"))); newTable.Add(tableGrid); for (int i = 0; i < rowCount; i++) { XElement row = new XElement(XName.Get("tr", DocX.w.NamespaceName)); for (int j = 0; j < columnCount; j++) { XElement cell = CreateTableCell(); row.Add(cell); } newTable.Add(row); } return newTable; } /// /// Create and return a cell of a table /// internal static XElement CreateTableCell() { return new XElement ( XName.Get("tc", DocX.w.NamespaceName), new XElement(XName.Get("tcPr", DocX.w.NamespaceName), new XElement(XName.Get("tcW", DocX.w.NamespaceName), new XAttribute(XName.Get("w", DocX.w.NamespaceName), "2310"), new XAttribute(XName.Get("type", DocX.w.NamespaceName), "dxa"))), new XElement(XName.Get("p", DocX.w.NamespaceName), new XElement(XName.Get("pPr", DocX.w.NamespaceName))) ); } internal static void RenumberIDs(DocX document) { IEnumerable trackerIDs = (from d in document.mainDoc.Descendants() where d.Name.LocalName == "ins" || d.Name.LocalName == "del" select d.Attribute(XName.Get("id", "http://schemas.openxmlformats.org/wordprocessingml/2006/main"))); for (int i = 0; i < trackerIDs.Count(); i++) trackerIDs.ElementAt(i).Value = i.ToString(); } static internal Paragraph GetFirstParagraphEffectedByInsert(DocX document, int index) { // This document contains no Paragraphs and insertion is at index 0 if (document.paragraphLookup.Keys.Count() == 0 && index == 0) return null; foreach (int paragraphEndIndex in document.paragraphLookup.Keys) { if (paragraphEndIndex >= index) return document.paragraphLookup[paragraphEndIndex]; } throw new ArgumentOutOfRangeException(); } internal static List FormatInput(string text, XElement rPr) { List newRuns = new List(); XElement tabRun = new XElement(DocX.w + "tab"); XElement breakRun = new XElement(DocX.w + "br"); StringBuilder sb = new StringBuilder(); if (string.IsNullOrEmpty(text)) { return newRuns; //I dont wanna get an exception if text == null, so just return empy list } foreach (char c in text) { switch (c) { case '\t': if (sb.Length > 0) { XElement t = new XElement(DocX.w + "t", sb.ToString()); Novacode.Text.PreserveSpace(t); newRuns.Add(new XElement(DocX.w + "r", rPr, t)); sb = new StringBuilder(); } newRuns.Add(new XElement(DocX.w + "r", rPr, tabRun)); break; case '\n': if (sb.Length > 0) { XElement t = new XElement(DocX.w + "t", sb.ToString()); Novacode.Text.PreserveSpace(t); newRuns.Add(new XElement(DocX.w + "r", rPr, t)); sb = new StringBuilder(); } newRuns.Add(new XElement(DocX.w + "r", rPr, breakRun)); break; default: sb.Append(c); break; } } if (sb.Length > 0) { XElement t = new XElement(DocX.w + "t", sb.ToString()); Novacode.Text.PreserveSpace(t); newRuns.Add(new XElement(DocX.w + "r", rPr, t)); } return newRuns; } static internal XElement[] SplitParagraph(Paragraph p, int index) { // In this case edit dosent really matter, you have a choice. Run r = p.GetFirstRunEffectedByEdit(index, EditType.ins); XElement[] split; XElement before, after; if (r.Xml.Parent.Name.LocalName == "ins") { split = p.SplitEdit(r.Xml.Parent, index, EditType.ins); before = new XElement(p.Xml.Name, p.Xml.Attributes(), r.Xml.Parent.ElementsBeforeSelf(), split[0]); after = new XElement(p.Xml.Name, p.Xml.Attributes(), r.Xml.Parent.ElementsAfterSelf(), split[1]); } else if (r.Xml.Parent.Name.LocalName == "del") { split = p.SplitEdit(r.Xml.Parent, index, EditType.del); before = new XElement(p.Xml.Name, p.Xml.Attributes(), r.Xml.Parent.ElementsBeforeSelf(), split[0]); after = new XElement(p.Xml.Name, p.Xml.Attributes(), r.Xml.Parent.ElementsAfterSelf(), split[1]); } else { split = Run.SplitRun(r, index); before = new XElement(p.Xml.Name, p.Xml.Attributes(), r.Xml.ElementsBeforeSelf(), split[0]); after = new XElement(p.Xml.Name, p.Xml.Attributes(), split[1], r.Xml.ElementsAfterSelf()); } if (before.Elements().Count() == 0) before = null; if (after.Elements().Count() == 0) after = null; return new XElement[] { before, after }; } /// static internal bool IsSameFile(Stream streamOne, Stream streamTwo) { int file1byte, file2byte; if (streamOne.Length != streamOne.Length) { // Return false to indicate files are different return false; } // Read and compare a byte from each file until either a // non-matching set of bytes is found or until the end of // file1 is reached. do { // Read one byte from each file. file1byte = streamOne.ReadByte(); file2byte = streamTwo.ReadByte(); } while ((file1byte == file2byte) && (file1byte != -1)); // Return the success of the comparison. "file1byte" is // equal to "file2byte" at this point only if the files are // the same. streamOne.Position = 0; streamTwo.Position = 0; return ((file1byte - file2byte) == 0); } } }