Cathal: Added overloads to ReplaceText for Format checking and replacement. Joel: Added Row.MergeCells Joel: Added Row.Height property Joel: Added Cell.Width property A huge thanks to Joel: The first user to submit working & tested functions\properties for inclusion in DocX.master
| @@ -0,0 +1,36 @@ | |||
| using System.Reflection; | |||
| using System.Runtime.CompilerServices; | |||
| using System.Runtime.InteropServices; | |||
| // General Information about an assembly is controlled through the following | |||
| // set of attributes. Change these attribute values to modify the information | |||
| // associated with an assembly. | |||
| [assembly: AssemblyTitle("ConsoleApplication1")] | |||
| [assembly: AssemblyDescription("")] | |||
| [assembly: AssemblyConfiguration("")] | |||
| [assembly: AssemblyCompany("Microsoft IT")] | |||
| [assembly: AssemblyProduct("ConsoleApplication1")] | |||
| [assembly: AssemblyCopyright("Copyright © Microsoft IT 2009")] | |||
| [assembly: AssemblyTrademark("")] | |||
| [assembly: AssemblyCulture("")] | |||
| // Setting ComVisible to false makes the types in this assembly not visible | |||
| // to COM components. If you need to access a type in this assembly from | |||
| // COM, set the ComVisible attribute to true on that type. | |||
| [assembly: ComVisible(false)] | |||
| // The following GUID is for the ID of the typelib if this project is exposed to COM | |||
| [assembly: Guid("69b4f03f-dc03-4e14-923d-6fd03ea8ff0c")] | |||
| // Version information for an assembly consists of the following four values: | |||
| // | |||
| // Major Version | |||
| // Minor Version | |||
| // Build Number | |||
| // Revision | |||
| // | |||
| // You can specify all the values or you can default the Build and Revision Numbers | |||
| // by using the '*' as shown below: | |||
| // [assembly: AssemblyVersion("1.0.*")] | |||
| [assembly: AssemblyVersion("1.0.0.0")] | |||
| [assembly: AssemblyFileVersion("1.0.0.0")] | |||
| @@ -7,13 +7,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution | |||
| EndProject | |||
| Global | |||
| GlobalSection(TeamFoundationVersionControl) = preSolution | |||
| SccNumberOfProjects = 2 | |||
| SccNumberOfProjects = 3 | |||
| SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} | |||
| SccTeamFoundationServer = https://tfs08.codeplex.com/ | |||
| SccLocalPath0 = . | |||
| SccProjectUniqueName1 = DocX\\DocX.csproj | |||
| SccProjectName1 = DocX | |||
| SccLocalPath1 = DocX | |||
| SccProjectUniqueName2 = ConsoleApplication1\\ConsoleApplication1.csproj | |||
| SccProjectName2 = ConsoleApplication1 | |||
| SccLocalPath2 = ConsoleApplication1 | |||
| EndGlobalSection | |||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
| Debug|Any CPU = Debug|Any CPU | |||
| @@ -0,0 +1,10 @@ | |||
| "" | |||
| { | |||
| "FILE_VERSION" = "9237" | |||
| "ENLISTMENT_CHOICE" = "NEVER" | |||
| "PROJECT_FILE_RELATIVE_PATH" = "" | |||
| "NUMBER_OF_EXCLUDED_FILES" = "0" | |||
| "ORIGINAL_PROJECT_FILE_PATH" = "" | |||
| "NUMBER_OF_NESTED_PROJECTS" = "0" | |||
| "SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT" | |||
| } | |||
| @@ -410,17 +410,14 @@ namespace Novacode | |||
| foreach (Paragraph p in paragraphs) | |||
| { | |||
| MatchCollection mc = Regex.Matches(p.Text, Regex.Escape(str), options); | |||
| var query = | |||
| ( | |||
| from m in mc.Cast<Match>() | |||
| select m.Index + p.startIndex | |||
| ).ToList(); | |||
| List<int> indexes = p.FindAll(str, options); | |||
| list.AddRange(query); | |||
| for (int i = 0; i < indexes.Count(); i++) | |||
| indexes[0] += p.startIndex; | |||
| list.AddRange(indexes); | |||
| } | |||
| return list; | |||
| } | |||
| @@ -456,6 +453,67 @@ namespace Novacode | |||
| } | |||
| } | |||
| internal string GetCollectiveText(List<PackagePart> list) | |||
| { | |||
| string text = string.Empty; | |||
| foreach (var hp in list) | |||
| { | |||
| using (TextReader tr = new StreamReader(hp.GetStream())) | |||
| { | |||
| XDocument d = XDocument.Load(tr); | |||
| StringBuilder sb = new StringBuilder(); | |||
| // Loop through each text item in this run | |||
| foreach (XElement descendant in d.Descendants()) | |||
| { | |||
| switch (descendant.Name.LocalName) | |||
| { | |||
| case "tab": | |||
| sb.Append("\t"); | |||
| break; | |||
| case "br": | |||
| sb.Append("\n"); | |||
| break; | |||
| case "t": | |||
| goto case "delText"; | |||
| case "delText": | |||
| sb.Append(descendant.Value); | |||
| break; | |||
| default: break; | |||
| } | |||
| } | |||
| text += "\n" + sb.ToString(); | |||
| } | |||
| } | |||
| return text; | |||
| } | |||
| /// <summary> | |||
| /// Gets the concatenated text of all header files in this document. | |||
| /// </summary> | |||
| public string HeaderText | |||
| { | |||
| get | |||
| { | |||
| return GetCollectiveText(headers); | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// Gets the concatenated text of all footer files in this document. | |||
| /// </summary> | |||
| public string FooterText | |||
| { | |||
| get | |||
| { | |||
| return GetCollectiveText(footers); | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// Insert the contents of another document at the end of this document. | |||
| /// </summary> | |||
| @@ -498,7 +556,7 @@ namespace Novacode | |||
| external_elements = internal_body.Elements().Reverse().TakeWhile((i, j) => j < count); | |||
| #endregion | |||
| #region /word/settings.xml | |||
| #region /word/styles.xml | |||
| Uri word_styles_Uri = new Uri("/word/styles.xml", UriKind.Relative); | |||
| // If the external document has a styles.xml, we need to insert its elements into the internal documents styles.xml. | |||
| @@ -698,7 +756,30 @@ namespace Novacode | |||
| { | |||
| return InsertParagraph(index, text, trackChanges, null); | |||
| } | |||
| /// <summary> | |||
| /// Insert a new Paragraph at the end of this document. | |||
| /// </summary> | |||
| /// <returns>A new Paragraph.</returns> | |||
| /// <example> | |||
| /// Inserting a new Paragraph at the end of a document. | |||
| /// <code> | |||
| /// // Load a document. | |||
| /// using (DocX document = DocX.Load(@"C:\Example\Test.docx")) | |||
| /// { | |||
| /// // Insert a new Paragraph at the end of this document. | |||
| /// document.InsertParagraph(); | |||
| /// | |||
| /// // Save all changes made to this document. | |||
| /// document.Save(); | |||
| /// }// Release this document from memory | |||
| /// </code> | |||
| /// </example> | |||
| public Paragraph InsertParagraph() | |||
| { | |||
| return InsertParagraph(string.Empty, false); | |||
| } | |||
| /// <summary> | |||
| /// Insert a Paragraph into this document, this Paragraph may have come from the same or another document. | |||
| /// </summary> | |||
| @@ -2206,7 +2287,7 @@ namespace Novacode | |||
| /// <param name="options">RegexOptions to use for this text replace.</param> | |||
| public void ReplaceText(string oldValue, string newValue, bool trackChanges, RegexOptions options) | |||
| { | |||
| ReplaceText(oldValue, newValue, false, false, trackChanges, options); | |||
| ReplaceText(oldValue, newValue, false, false, trackChanges, options, null, null, MatchFormattingOptions.SubsetMatch); | |||
| } | |||
| /// <summary> | |||
| @@ -2233,9 +2314,59 @@ namespace Novacode | |||
| /// <param name="trackChanges">Should this change be tracked?</param> | |||
| /// <param name="options">RegexOptions to use for this text replace.</param> | |||
| public void ReplaceText(string oldValue, string newValue, bool includeHeaders, bool includeFooters, bool trackChanges, RegexOptions options) | |||
| { | |||
| ReplaceText(oldValue, newValue, includeHeaders, includeFooters, trackChanges, options, null, null, MatchFormattingOptions.SubsetMatch); | |||
| } | |||
| /// <summary> | |||
| /// Replace text in this document, ignore case, include the headers and footers. | |||
| /// </summary> | |||
| /// <example> | |||
| /// Replace every instance of "old" in this document with "new". | |||
| /// <code> | |||
| /// // Load a document. | |||
| /// using (DocX document = DocX.Load(@"Test.docx")) | |||
| /// { | |||
| /// // The formatting to match. | |||
| /// Formatting matchFormatting = new Formatting(); | |||
| /// matchFormatting.Size = 10; | |||
| /// matchFormatting.Italic = true; | |||
| /// matchFormatting.FontFamily = new FontFamily("Times New Roman"); | |||
| /// | |||
| /// // The formatting to apply to the inserted text. | |||
| /// Formatting newFormatting = new Formatting(); | |||
| /// newFormatting.Size = 22; | |||
| /// newFormatting.UnderlineStyle = UnderlineStyle.dotted; | |||
| /// newFormatting.Bold = true; | |||
| /// | |||
| /// /* | |||
| /// * Replace all instances of the string "old" with the string "new", include both the header and footer and ignore case. | |||
| /// * Each inserted instance of "new" should use the Formatting newFormatting. | |||
| /// * Only replace an instance of "old" if it is Size 10, Italic and Times New Roman. | |||
| /// * SubsetMatch means that the formatting must contain all elements of the match formatting, | |||
| /// * but it can also contain additional formatting for example Color, UnderlineStyle, etc. | |||
| /// * ExactMatch means it must not contain additional formatting. | |||
| /// */ | |||
| /// document.ReplaceText("old", "new", true, true, false, RegexOptions.IgnoreCase, newFormatting, matchFormatting, MatchFormattingOptions.SubsetMatch); | |||
| /// | |||
| /// // Save all changes made to this document. | |||
| /// document.Save(); | |||
| /// }// Release this document from memory. | |||
| /// </code> | |||
| /// </example> | |||
| /// <param name="oldValue">The text to replace.</param> | |||
| /// <param name="newValue">The new text to insert.</param> | |||
| /// <param name="includeHeaders">Should ReplaceText include text in the headers?</param> | |||
| /// <param name="includeFooters">Should ReplaceText include text in the footers?</param> | |||
| /// <param name="trackChanges">Should this change be tracked?</param> | |||
| /// <param name="options">RegexOptions to use for this text replace.</param> | |||
| /// <param name="newFormatting">The formatting to apply to the text being inserted.</param> | |||
| /// <param name="matchFormatting">The formatting that the text must match in order to be replaced.</param> | |||
| /// <param name="fo">How should formatting be matched?</param> | |||
| public void ReplaceText(string oldValue, string newValue, bool includeHeaders, bool includeFooters, bool trackChanges, RegexOptions options, Formatting newFormatting, Formatting matchFormatting, MatchFormattingOptions fo) | |||
| { | |||
| foreach (Paragraph p in paragraphs) | |||
| p.ReplaceText(oldValue, newValue, trackChanges, options); | |||
| p.ReplaceText(oldValue, newValue, trackChanges, options, newFormatting, matchFormatting, fo); | |||
| #region Headers & Footers | |||
| List<PackagePart> headerAndFooters = new List<PackagePart>(); | |||
| @@ -2258,13 +2389,13 @@ namespace Novacode | |||
| foreach (Paragraph p in paras) | |||
| { | |||
| p.ReplaceText(oldValue, newValue, trackChanges, options); | |||
| p.ReplaceText(oldValue, newValue, trackChanges, options, newFormatting, matchFormatting, fo); | |||
| } | |||
| } | |||
| using (TextWriter tw = new StreamWriter(pp.GetStream(FileMode.Create))) | |||
| d.Save(tw); | |||
| } | |||
| } | |||
| #endregion | |||
| } | |||
| @@ -2318,7 +2449,7 @@ namespace Novacode | |||
| /// <param name="trackChanges">Should this change be tracked?</param> | |||
| public void ReplaceText(string oldValue, string newValue, bool includeHeaders, bool includeFooters, bool trackChanges) | |||
| { | |||
| ReplaceText(oldValue, newValue, includeHeaders, includeFooters, trackChanges); | |||
| ReplaceText(oldValue, newValue, includeHeaders, includeFooters, trackChanges, RegexOptions.None, null, null, MatchFormattingOptions.SubsetMatch); | |||
| } | |||
| #region IDisposable Members | |||
| @@ -5,6 +5,7 @@ using System.Text; | |||
| namespace Novacode | |||
| { | |||
| public enum MatchFormattingOptions { ExactMatch, SubsetMatch}; | |||
| public enum Script { superscript, subscript, none } | |||
| public enum Highlight { yellow, green, cyan, magenta, blue, red, darkBlue, darkCyan, darkGreen, darkMagenta, darkRed, darkYellow, darkGray, lightGray, black, none }; | |||
| public enum UnderlineStyle { none, singleLine, doubleLine, thick, dotted, dottedHeavy, dash, dashedHeavy, dashLong, dashLongHeavy, dotDash, dashDotHeavy, dotDotDash, dashDotDotHeavy, wave, wavyHeavy, wavyDouble, words }; | |||
| @@ -86,9 +86,10 @@ namespace Novacode | |||
| ( | |||
| from d in styles_element.Descendants() | |||
| let styleId = d.Attribute(XName.Get("styleId", DocX.w.NamespaceName)) | |||
| where styleId != null && styleId.Value == id | |||
| let type = d.Attribute(XName.Get("type", DocX.w.NamespaceName)) | |||
| where type != null && type.Value == "paragraph" && styleId != null && styleId.Value == id | |||
| select d | |||
| ).Single(); | |||
| ).First(); | |||
| styles.Add(style); | |||
| } | |||
| @@ -1697,15 +1698,173 @@ namespace Novacode | |||
| /// <param name="options">A bitwise OR combination of RegexOption enumeration options.</param> | |||
| /// <param name="trackChanges">Track changes</param> | |||
| public void ReplaceText(string oldValue, string newValue, bool trackChanges, RegexOptions options) | |||
| { | |||
| ReplaceText(oldValue, newValue, trackChanges, options, null, null, MatchFormattingOptions.SubsetMatch); | |||
| } | |||
| /// <summary> | |||
| /// Replaces all occurrences of a specified System.String in this instance, with another specified System.String. | |||
| /// </summary> | |||
| /// <example> | |||
| /// <code> | |||
| /// // Create a document using a relative filename. | |||
| /// using (DocX document = DocX.Load(@"C:\Example\Test.docx")) | |||
| /// { | |||
| /// // The formatting to apply to the inserted text. | |||
| /// Formatting newFormatting = new Formatting(); | |||
| /// newFormatting.Size = 22; | |||
| /// newFormatting.UnderlineStyle = UnderlineStyle.dotted; | |||
| /// newFormatting.Bold = true; | |||
| /// | |||
| /// // Iterate through the paragraphs in this document. | |||
| /// foreach (Paragraph p in document.Paragraphs) | |||
| /// { | |||
| /// /* | |||
| /// * Replace all instances of the string "wrong" with the string "right" and ignore case. | |||
| /// * Each inserted instance of "wrong" should use the Formatting newFormatting. | |||
| /// */ | |||
| /// p.ReplaceText("wrong", "right", false, RegexOptions.IgnoreCase, newFormatting); | |||
| /// } | |||
| /// | |||
| /// // Save all changes made to this document. | |||
| /// document.Save(); | |||
| /// }// Release this document from memory. | |||
| /// </code> | |||
| /// </example> | |||
| /// <seealso cref="Paragraph.RemoveText(int, int, bool)"/> | |||
| /// <seealso cref="Paragraph.RemoveText(int, bool)"/> | |||
| /// <seealso cref="Paragraph.InsertText(string, bool)"/> | |||
| /// <seealso cref="Paragraph.InsertText(int, string, bool)"/> | |||
| /// <seealso cref="Paragraph.InsertText(int, string, bool, Formatting)"/> | |||
| /// <seealso cref="Paragraph.InsertText(string, bool, Formatting)"/> | |||
| /// <param name="newValue">A System.String to replace all occurances of oldValue.</param> | |||
| /// <param name="oldValue">A System.String to be replaced.</param> | |||
| /// <param name="options">A bitwise OR combination of RegexOption enumeration options.</param> | |||
| /// <param name="trackChanges">Track changes</param> | |||
| /// <param name="newFormatting">The formatting to apply to the text being inserted.</param> | |||
| public void ReplaceText(string oldValue, string newValue, bool trackChanges, RegexOptions options, Formatting newFormatting) | |||
| { | |||
| ReplaceText(oldValue, newValue, trackChanges, options, null, null, MatchFormattingOptions.SubsetMatch); | |||
| } | |||
| /// <summary> | |||
| /// Replaces all occurrences of a specified System.String in this instance, with another specified System.String. | |||
| /// </summary> | |||
| /// <example> | |||
| /// <code> | |||
| /// // Load a document using a relative filename. | |||
| /// using (DocX document = DocX.Load(@"C:\Example\Test.docx")) | |||
| /// { | |||
| /// // The formatting to match. | |||
| /// Formatting matchFormatting = new Formatting(); | |||
| /// matchFormatting.Size = 10; | |||
| /// matchFormatting.Italic = true; | |||
| /// matchFormatting.FontFamily = new FontFamily("Times New Roman"); | |||
| /// | |||
| /// // The formatting to apply to the inserted text. | |||
| /// Formatting newFormatting = new Formatting(); | |||
| /// newFormatting.Size = 22; | |||
| /// newFormatting.UnderlineStyle = UnderlineStyle.dotted; | |||
| /// newFormatting.Bold = true; | |||
| /// | |||
| /// // Iterate through the paragraphs in this document. | |||
| /// foreach (Paragraph p in document.Paragraphs) | |||
| /// { | |||
| /// /* | |||
| /// * Replace all instances of the string "wrong" with the string "right" and ignore case. | |||
| /// * Each inserted instance of "wrong" should use the Formatting newFormatting. | |||
| /// * Only replace an instance of "wrong" if it is Size 10, Italic and Times New Roman. | |||
| /// * SubsetMatch means that the formatting must contain all elements of the match formatting, | |||
| /// * but it can also contain additional formatting for example Color, UnderlineStyle, etc. | |||
| /// * ExactMatch means it must not contain additional formatting. | |||
| /// */ | |||
| /// p.ReplaceText("wrong", "right", false, RegexOptions.IgnoreCase, newFormatting, matchFormatting, MatchFormattingOptions.SubsetMatch); | |||
| /// } | |||
| /// | |||
| /// // Save all changes made to this document. | |||
| /// document.Save(); | |||
| /// }// Release this document from memory. | |||
| /// </code> | |||
| /// </example> | |||
| /// <seealso cref="Paragraph.RemoveText(int, int, bool)"/> | |||
| /// <seealso cref="Paragraph.RemoveText(int, bool)"/> | |||
| /// <seealso cref="Paragraph.InsertText(string, bool)"/> | |||
| /// <seealso cref="Paragraph.InsertText(int, string, bool)"/> | |||
| /// <seealso cref="Paragraph.InsertText(int, string, bool, Formatting)"/> | |||
| /// <seealso cref="Paragraph.InsertText(string, bool, Formatting)"/> | |||
| /// <param name="newValue">A System.String to replace all occurances of oldValue.</param> | |||
| /// <param name="oldValue">A System.String to be replaced.</param> | |||
| /// <param name="options">A bitwise OR combination of RegexOption enumeration options.</param> | |||
| /// <param name="trackChanges">Track changes</param> | |||
| /// <param name="newFormatting">The formatting to apply to the text being inserted.</param> | |||
| /// <param name="matchFormatting">The formatting that the text must match in order to be replaced.</param> | |||
| /// <param name="fo">How should formatting be matched?</param> | |||
| public void ReplaceText(string oldValue, string newValue, bool trackChanges, RegexOptions options, Formatting newFormatting, Formatting matchFormatting, MatchFormattingOptions fo) | |||
| { | |||
| MatchCollection mc = Regex.Matches(this.Text, Regex.Escape(oldValue), options); | |||
| // Loop through the matches in reverse order | |||
| foreach (Match m in mc.Cast<Match>().Reverse()) | |||
| { | |||
| InsertText(m.Index + oldValue.Length, newValue, trackChanges); | |||
| RemoveText(m.Index, m.Length, trackChanges); | |||
| // Assume the formatting matches until proven otherwise. | |||
| bool formattingMatch = true; | |||
| // Does the user want to match formatting? | |||
| if (matchFormatting != null) | |||
| { | |||
| // The number of characters processed so far | |||
| int processed = 0; | |||
| do | |||
| { | |||
| // Get the next run effected | |||
| Run run = GetFirstRunEffectedByEdit(m.Index + processed); | |||
| // Get this runs properties | |||
| XElement rPr = run.xml.Element(XName.Get("rPr", DocX.w.NamespaceName)); | |||
| if (rPr == null) | |||
| rPr = new Formatting().Xml; | |||
| /* | |||
| * Make sure that every formatting element in f.xml is also in this run, | |||
| * if this is not true, then their formatting does not match. | |||
| */ | |||
| if (!ContainsEveryChildOf(matchFormatting.Xml, rPr, fo)) | |||
| { | |||
| formattingMatch = false; | |||
| break; | |||
| } | |||
| // We have processed some characters, so update the counter. | |||
| processed += run.Value.Length; | |||
| } while (processed < m.Length); | |||
| } | |||
| // If the formatting matches, do the replace. | |||
| if(formattingMatch) | |||
| { | |||
| InsertText(m.Index + oldValue.Length, newValue, trackChanges, newFormatting); | |||
| RemoveText(m.Index, m.Length, trackChanges); | |||
| } | |||
| } | |||
| } | |||
| internal bool ContainsEveryChildOf(XElement a, XElement b, MatchFormattingOptions fo) | |||
| { | |||
| foreach (XElement e in a.Elements()) | |||
| { | |||
| // If a formatting property has the same name and value, its considered to be equivalent. | |||
| if (!b.Elements(e.Name).Where(bElement => bElement.Value == e.Value).Any()) | |||
| return false; | |||
| } | |||
| // If the formatting has to be exact, no additionaly formatting must exist. | |||
| if (fo == MatchFormattingOptions.ExactMatch) | |||
| return a.Elements().Count() == b.Elements().Count(); | |||
| return true; | |||
| } | |||
| /// <summary> | |||
| @@ -1826,7 +1985,7 @@ namespace Novacode | |||
| /// <param name="trackChanges">Track changes</param> | |||
| public void ReplaceText(string oldValue, string newValue, bool trackChanges) | |||
| { | |||
| ReplaceText(oldValue, newValue, trackChanges, RegexOptions.None); | |||
| ReplaceText(oldValue, newValue, trackChanges, RegexOptions.None, null, null, MatchFormattingOptions.SubsetMatch); | |||
| } | |||
| } | |||
| } | |||
| @@ -29,7 +29,7 @@ namespace Novacode | |||
| /// <summary> | |||
| /// The text value of this text element | |||
| /// </summary> | |||
| private string Value { set { value = text; } get { return text; } } | |||
| internal string Value { set { value = text; } get { return text; } } | |||
| internal Run(int startIndex, XElement xml) | |||
| { | |||
| @@ -13,7 +13,7 @@ namespace Novacode | |||
| /// <summary> | |||
| /// Designs\Styles that can be applied to a table. | |||
| /// </summary> | |||
| public enum TableDesign { TableNormal, TableGrid, LightShading, LightShadingAccent1, LightShadingAccent2, LightShadingAccent3, LightShadingAccent4, LightShadingAccent5, LightShadingAccent6, LightList, LightListAccent1, LightListAccent2, LightListAccent3, LightListAccent4, LightListAccent5, LightListAccent6, LightGrid, LightGridAccent1, LightGridAccent2, LightGridAccent3, LightGridAccent4, LightGridAccent5, LightGridAccent6, MediumShading1, MediumShading1Accent1, MediumShading1Accent2, MediumShading1Accent3, MediumShading1Accent4, MediumShading1Accent5, MediumShading1Accent6, MediumShading2, MediumShading2Accent1, MediumShading2Accent2, MediumShading2Accent3, MediumShading2Accent4, MediumShading2Accent5, MediumShading2Accent6, MediumList1, MediumList1Accent1, MediumList1Accent2, MediumList1Accent3, MediumList1Accent4, MediumList1Accent5, MediumList1Accent6, MediumList2, MediumList2Accent1, MediumList2Accent2, MediumList2Accent3, MediumList2Accent4, MediumList2Accent5, MediumList2Accent6, MediumGrid1, MediumGrid1Accent1, MediumGrid1Accent2, MediumGrid1Accent3, MediumGrid1Accent4, MediumGrid1Accent5, MediumGrid1Accent6, MediumGrid2, MediumGrid2Accent1, MediumGrid2Accent2, MediumGrid2Accent3, MediumGrid2Accent4, MediumGrid2Accent5, MediumGrid2Accent6, MediumGrid3, MediumGrid3Accent1, MediumGrid3Accent2, MediumGrid3Accent3, MediumGrid3Accent4, MediumGrid3Accent5, MediumGrid3Accent6, DarkList, DarkListAccent1, DarkListAccent2, DarkListAccent3, DarkListAccent4, DarkListAccent5, DarkListAccent6, ColorfulShading, ColorfulShadingAccent1, ColorfulShadingAccent2, ColorfulShadingAccent3, ColorfulShadingAccent4, ColorfulShadingAccent5, ColorfulShadingAccent6, ColorfulList, ColorfulListAccent1, ColorfulListAccent2, ColorfulListAccent3, ColorfulListAccent4, ColorfulListAccent5, ColorfulListAccent6, ColorfulGrid, ColorfulGridAccent1, ColorfulGridAccent2, ColorfulGridAccent3, ColorfulGridAccent4, ColorfulGridAccent5, ColorfulGridAccent6, None}; | |||
| public enum TableDesign { Custom, TableNormal, TableGrid, LightShading, LightShadingAccent1, LightShadingAccent2, LightShadingAccent3, LightShadingAccent4, LightShadingAccent5, LightShadingAccent6, LightList, LightListAccent1, LightListAccent2, LightListAccent3, LightListAccent4, LightListAccent5, LightListAccent6, LightGrid, LightGridAccent1, LightGridAccent2, LightGridAccent3, LightGridAccent4, LightGridAccent5, LightGridAccent6, MediumShading1, MediumShading1Accent1, MediumShading1Accent2, MediumShading1Accent3, MediumShading1Accent4, MediumShading1Accent5, MediumShading1Accent6, MediumShading2, MediumShading2Accent1, MediumShading2Accent2, MediumShading2Accent3, MediumShading2Accent4, MediumShading2Accent5, MediumShading2Accent6, MediumList1, MediumList1Accent1, MediumList1Accent2, MediumList1Accent3, MediumList1Accent4, MediumList1Accent5, MediumList1Accent6, MediumList2, MediumList2Accent1, MediumList2Accent2, MediumList2Accent3, MediumList2Accent4, MediumList2Accent5, MediumList2Accent6, MediumGrid1, MediumGrid1Accent1, MediumGrid1Accent2, MediumGrid1Accent3, MediumGrid1Accent4, MediumGrid1Accent5, MediumGrid1Accent6, MediumGrid2, MediumGrid2Accent1, MediumGrid2Accent2, MediumGrid2Accent3, MediumGrid2Accent4, MediumGrid2Accent5, MediumGrid2Accent6, MediumGrid3, MediumGrid3Accent1, MediumGrid3Accent2, MediumGrid3Accent3, MediumGrid3Accent4, MediumGrid3Accent5, MediumGrid3Accent6, DarkList, DarkListAccent1, DarkListAccent2, DarkListAccent3, DarkListAccent4, DarkListAccent5, DarkListAccent6, ColorfulShading, ColorfulShadingAccent1, ColorfulShadingAccent2, ColorfulShadingAccent3, ColorfulShadingAccent4, ColorfulShadingAccent5, ColorfulShadingAccent6, ColorfulList, ColorfulListAccent1, ColorfulListAccent2, ColorfulListAccent3, ColorfulListAccent4, ColorfulListAccent5, ColorfulListAccent6, ColorfulGrid, ColorfulGridAccent1, ColorfulGridAccent2, ColorfulGridAccent3, ColorfulGridAccent4, ColorfulGridAccent5, ColorfulGridAccent6, None}; | |||
| public enum AutoFit{Contents, Window, ColoumnWidth}; | |||
| /// <summary> | |||
| @@ -67,7 +67,17 @@ namespace Novacode | |||
| XAttribute val = style.Attribute(XName.Get("val", DocX.w.NamespaceName)); | |||
| if (val != null) | |||
| design = (TableDesign)Enum.Parse(typeof(TableDesign), val.Value.Replace("-", string.Empty)); | |||
| { | |||
| try | |||
| { | |||
| design = (TableDesign)Enum.Parse(typeof(TableDesign), val.Value.Replace("-", string.Empty)); | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| design = TableDesign.Custom; | |||
| } | |||
| } | |||
| else | |||
| design = TableDesign.None; | |||
| } | |||
| @@ -1262,6 +1272,165 @@ namespace Novacode | |||
| cells = (from c in xml.Elements(XName.Get("tc", DocX.w.NamespaceName)) | |||
| select new Cell(document, c)).ToList(); | |||
| } | |||
| /// <summary> | |||
| /// Height in pixels. // Added by Joel, refactored by Cathal. | |||
| /// </summary> | |||
| public double Height | |||
| { | |||
| get | |||
| { | |||
| /* | |||
| * Get the trPr (table row properties) element for this Row, | |||
| * null will be return if no such element exists. | |||
| */ | |||
| XElement trPr = xml.Element(XName.Get("trPr", DocX.w.NamespaceName)); | |||
| // If trPr is null, this row contains no height information. | |||
| if(trPr == null) | |||
| return double.NaN; | |||
| /* | |||
| * Get the trHeight element for this Row, | |||
| * null will be return if no such element exists. | |||
| */ | |||
| XElement trHeight = trPr.Element(XName.Get("trHeight", DocX.w.NamespaceName)); | |||
| // If trHeight is null, this row contains no height information. | |||
| if (trHeight == null) | |||
| return double.NaN; | |||
| // Get the val attribute for this trHeight element. | |||
| XAttribute val = trHeight.Attribute(XName.Get("val", DocX.w.NamespaceName)); | |||
| // If w is null, this cell contains no width information. | |||
| if (val == null) | |||
| return double.NaN; | |||
| // If val is not a double, something is wrong with this attributes value, so remove it and return double.NaN; | |||
| double heightInWordUnits; | |||
| if (!double.TryParse(val.Value, out heightInWordUnits)) | |||
| { | |||
| val.Remove(); | |||
| return double.NaN; | |||
| } | |||
| // 15 "word units" in one pixel | |||
| return (heightInWordUnits / 15); | |||
| } | |||
| set | |||
| { | |||
| /* | |||
| * Get the trPr (table row properties) element for this Row, | |||
| * null will be return if no such element exists. | |||
| */ | |||
| XElement trPr = xml.Element(XName.Get("trPr", DocX.w.NamespaceName)); | |||
| if (trPr == null) | |||
| { | |||
| xml.SetElementValue(XName.Get("trPr", DocX.w.NamespaceName), string.Empty); | |||
| trPr = xml.Element(XName.Get("trPr", DocX.w.NamespaceName)); | |||
| } | |||
| /* | |||
| * Get the trHeight element for this Row, | |||
| * null will be return if no such element exists. | |||
| */ | |||
| XElement trHeight = trPr.Element(XName.Get("trHeight", DocX.w.NamespaceName)); | |||
| if (trHeight == null) | |||
| { | |||
| trPr.SetElementValue(XName.Get("trHeight", DocX.w.NamespaceName), string.Empty); | |||
| trHeight = trPr.Element(XName.Get("trHeight", DocX.w.NamespaceName)); | |||
| } | |||
| // The hRule attribute needs to be set to exact. | |||
| trHeight.SetAttributeValue(XName.Get("hRule", DocX.w.NamespaceName), "exact"); | |||
| // 15 "word units" is equal to one pixel. | |||
| trHeight.SetAttributeValue(XName.Get("val", DocX.w.NamespaceName), (value * 15).ToString()); | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// Merge cells starting with startIndex and ending with endIndex. | |||
| /// </summary> | |||
| public void MergeCells(int startIndex, int endIndex) | |||
| { | |||
| // Check for valid start and end indexes. | |||
| if (startIndex < 0 || endIndex <= startIndex || endIndex > Cells.Count + 1) | |||
| throw new IndexOutOfRangeException(); | |||
| // The sum of all merged gridSpans. | |||
| int gridSpanSum = 0; | |||
| // Foreach each Cell between startIndex and endIndex inclusive. | |||
| foreach (Cell c in cells.Where((z, i) => i > startIndex && i <= endIndex)) | |||
| { | |||
| XElement tcPr = c.xml.Element(XName.Get("tcPr", DocX.w.NamespaceName)); | |||
| if (tcPr != null) | |||
| { | |||
| XElement gridSpan = tcPr.Element(XName.Get("gridSpan", DocX.w.NamespaceName)); | |||
| if (gridSpan != null) | |||
| { | |||
| XAttribute val = gridSpan.Attribute(XName.Get("val", DocX.w.NamespaceName)); | |||
| int value = 0; | |||
| if (val != null) | |||
| if (int.TryParse(val.Value, out value)) | |||
| gridSpanSum += value - 1; | |||
| } | |||
| } | |||
| // Add this cells Pragraph to the merge start Cell. | |||
| cells[startIndex].xml.Add(c.xml.Elements(XName.Get("p", DocX.w.NamespaceName))); | |||
| // Remove this Cell. | |||
| c.xml.Remove(); | |||
| } | |||
| /* | |||
| * Get the tcPr (table cell properties) element for the first cell in this merge, | |||
| * null will be returned if no such element exists. | |||
| */ | |||
| XElement start_tcPr = cells[startIndex].xml.Element(XName.Get("tcPr", DocX.w.NamespaceName)); | |||
| if (start_tcPr == null) | |||
| { | |||
| cells[startIndex].xml.SetElementValue(XName.Get("tcPr", DocX.w.NamespaceName), string.Empty); | |||
| start_tcPr = cells[startIndex].xml.Element(XName.Get("tcPr", DocX.w.NamespaceName)); | |||
| } | |||
| /* | |||
| * Get the gridSpan element of this row, | |||
| * null will be returned if no such element exists. | |||
| */ | |||
| XElement start_gridSpan = start_tcPr.Element(XName.Get("gridSpan", DocX.w.NamespaceName)); | |||
| if (start_gridSpan == null) | |||
| { | |||
| start_tcPr.SetElementValue(XName.Get("gridSpan", DocX.w.NamespaceName), string.Empty); | |||
| start_gridSpan = start_tcPr.Element(XName.Get("gridSpan", DocX.w.NamespaceName)); | |||
| } | |||
| /* | |||
| * Get the val attribute of this row, | |||
| * null will be returned if no such element exists. | |||
| */ | |||
| XAttribute start_val = start_gridSpan.Attribute(XName.Get("val", DocX.w.NamespaceName)); | |||
| int start_value = 0; | |||
| if (start_val != null) | |||
| if (int.TryParse(start_val.Value, out start_value)) | |||
| gridSpanSum += start_value - 1; | |||
| // Set the val attribute to the number of merged cells. | |||
| start_gridSpan.SetAttributeValue(XName.Get("val", DocX.w.NamespaceName), (gridSpanSum + (endIndex - startIndex + 1)).ToString()); | |||
| // Rebuild the cells collection. | |||
| cells = | |||
| ( | |||
| from c in xml.Elements(XName.Get("tc", DocX.w.NamespaceName)) | |||
| select new Cell(document, c) | |||
| ).ToList(); | |||
| } | |||
| } | |||
| public class Cell | |||
| @@ -1285,5 +1454,83 @@ namespace Novacode | |||
| p = new Paragraph(document, 0, xml.Element(XName.Get("p", DocX.w.NamespaceName))); | |||
| } | |||
| /// <summary> | |||
| /// Width in pixels. // Added by Joel, refactored by Cathal | |||
| /// </summary> | |||
| public double Width | |||
| { | |||
| get | |||
| { | |||
| /* | |||
| * Get the tcPr (table cell properties) element for this Cell, | |||
| * null will be return if no such element exists. | |||
| */ | |||
| XElement tcPr = xml.Element(XName.Get("tcPr", DocX.w.NamespaceName)); | |||
| // If tcPr is null, this cell contains no width information. | |||
| if (tcPr == null) | |||
| return double.NaN; | |||
| /* | |||
| * Get the tcW (table cell width) element for this Cell, | |||
| * null will be return if no such element exists. | |||
| */ | |||
| XElement tcW = tcPr.Element(XName.Get("tcW", DocX.w.NamespaceName)); | |||
| // If tcW is null, this cell contains no width information. | |||
| if (tcW == null) | |||
| return double.NaN; | |||
| // Get the w attribute of the tcW element. | |||
| XAttribute w = tcW.Attribute(XName.Get("w", DocX.w.NamespaceName)); | |||
| // If w is null, this cell contains no width information. | |||
| if (w == null) | |||
| return double.NaN; | |||
| // If w is not a double, something is wrong with this attributes value, so remove it and return double.NaN; | |||
| double widthInWordUnits; | |||
| if (!double.TryParse(w.Value, out widthInWordUnits)) | |||
| { | |||
| w.Remove(); | |||
| return double.NaN; | |||
| } | |||
| // 15 "word units" is equal to one pixel. | |||
| return (widthInWordUnits / 15); | |||
| } | |||
| set | |||
| { | |||
| /* | |||
| * Get the tcPr (table cell properties) element for this Cell, | |||
| * null will be return if no such element exists. | |||
| */ | |||
| XElement tcPr = xml.Element(XName.Get("tcPr", DocX.w.NamespaceName)); | |||
| if (tcPr == null) | |||
| { | |||
| xml.SetElementValue(XName.Get("tcPr", DocX.w.NamespaceName), string.Empty); | |||
| tcPr = xml.Element(XName.Get("tcPr", DocX.w.NamespaceName)); | |||
| } | |||
| /* | |||
| * Get the tcW (table cell width) element for this Cell, | |||
| * null will be return if no such element exists. | |||
| */ | |||
| XElement tcW = tcPr.Element(XName.Get("tcW", DocX.w.NamespaceName)); | |||
| if (tcW == null) | |||
| { | |||
| tcPr.SetElementValue(XName.Get("tcW", DocX.w.NamespaceName), string.Empty); | |||
| tcW = tcPr.Element(XName.Get("tcW", DocX.w.NamespaceName)); | |||
| } | |||
| // The type attribute needs to be set to dxa which represents "twips" or twentieths of a point. In other words, 1/1440th of an inch. | |||
| tcW.SetAttributeValue(XName.Get("type", DocX.w.NamespaceName), "dxa"); | |||
| // 15 "word units" is equal to one pixel. | |||
| tcW.SetAttributeValue(XName.Get("w", DocX.w.NamespaceName), (value * 15).ToString()); | |||
| } | |||
| } | |||
| } | |||
| } | |||