| @@ -35,10 +35,6 @@ | |||
| <WarningLevel>4</WarningLevel> | |||
| </PropertyGroup> | |||
| <ItemGroup> | |||
| <Reference Include="DocX, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> | |||
| <SpecificVersion>False</SpecificVersion> | |||
| <HintPath>..\DocX\bin\Debug\DocX.dll</HintPath> | |||
| </Reference> | |||
| <Reference Include="System" /> | |||
| <Reference Include="System.Core"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| @@ -61,6 +57,12 @@ | |||
| <CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
| </None> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ProjectReference Include="..\DocX\DocX.csproj"> | |||
| <Project>{E863D072-AA8B-4108-B5F1-785241B37F67}</Project> | |||
| <Name>DocX</Name> | |||
| </ProjectReference> | |||
| </ItemGroup> | |||
| <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
| <!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||
| Other similar extension points exist, see Microsoft.Common.targets. | |||
| @@ -19,6 +19,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StringReplaceTestApp", "Str | |||
| EndProject | |||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomPropertyTestApp", "CustomPropertyTestApp\CustomPropertyTestApp.csproj", "{9EABCAA8-175C-4FA2-9829-59E9D9E275D3}" | |||
| EndProject | |||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CreateDocumentOnTheFly", "CreateDocumentOnTheFly\CreateDocumentOnTheFly.csproj", "{40E089B2-BFE4-4E47-83CC-36A4D43269C4}" | |||
| EndProject | |||
| Global | |||
| GlobalSection(TeamFoundationVersionControl) = preSolution | |||
| SccNumberOfProjects = 5 | |||
| @@ -62,6 +64,10 @@ Global | |||
| {9EABCAA8-175C-4FA2-9829-59E9D9E275D3}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
| {9EABCAA8-175C-4FA2-9829-59E9D9E275D3}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
| {9EABCAA8-175C-4FA2-9829-59E9D9E275D3}.Release|Any CPU.Build.0 = Release|Any CPU | |||
| {40E089B2-BFE4-4E47-83CC-36A4D43269C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
| {40E089B2-BFE4-4E47-83CC-36A4D43269C4}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
| {40E089B2-BFE4-4E47-83CC-36A4D43269C4}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
| {40E089B2-BFE4-4E47-83CC-36A4D43269C4}.Release|Any CPU.Build.0 = Release|Any CPU | |||
| EndGlobalSection | |||
| GlobalSection(SolutionProperties) = preSolution | |||
| HideSolutionNode = FALSE | |||
| @@ -97,6 +97,84 @@ namespace Novacode | |||
| get { return customProperties; } | |||
| } | |||
| public Paragraph AddParagraph() | |||
| { | |||
| XElement newParagraph = new XElement(w + "p"); | |||
| mainDoc.Descendants(XName.Get("body", "http://schemas.openxmlformats.org/wordprocessingml/2006/main")).Single().Add(newParagraph); | |||
| // Get all of the paragraphs in this document | |||
| DocX.paragraphs = from p in mainDoc.Descendants(XName.Get("p", "http://schemas.openxmlformats.org/wordprocessingml/2006/main")) | |||
| select new Paragraph(p); | |||
| return new Paragraph(newParagraph); | |||
| } | |||
| public static DocX Create(string uri) | |||
| { | |||
| FileInfo fi = new FileInfo(uri); | |||
| if (fi.Extension != ".docx") | |||
| throw new Exception(string.Format("The input file {0} is not a .docx file", fi.FullName)); | |||
| DocX.uri = uri; | |||
| // Create the docx package | |||
| wdDoc = WordprocessingDocument.Create(uri, DocumentFormat.OpenXml.WordprocessingDocumentType.Document); | |||
| #region MainDocumentPart | |||
| // Create the main document part for this package | |||
| mainDocumentPart = wdDoc.AddMainDocumentPart(); | |||
| // Load the document part into a XDocument object | |||
| using (TextReader tr = new StreamReader(mainDocumentPart.GetStream(FileMode.Create, FileAccess.ReadWrite))) | |||
| { | |||
| mainDoc = XDocument.Parse | |||
| (@"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?> | |||
| <w:document xmlns:ve=""http://schemas.openxmlformats.org/markup-compatibility/2006"" xmlns:o=""urn:schemas-microsoft-com:office:office"" xmlns:r=""http://schemas.openxmlformats.org/officeDocument/2006/relationships"" xmlns:m=""http://schemas.openxmlformats.org/officeDocument/2006/math"" xmlns:v=""urn:schemas-microsoft-com:vml"" xmlns:wp=""http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"" xmlns:w10=""urn:schemas-microsoft-com:office:word"" xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"" xmlns:wne=""http://schemas.microsoft.com/office/word/2006/wordml""> | |||
| <w:body> | |||
| <w:sectPr w:rsidR=""003E25F4"" w:rsidSect=""00FC3028""> | |||
| <w:pgSz w:w=""11906"" w:h=""16838""/> | |||
| <w:pgMar w:top=""1440"" w:right=""1440"" w:bottom=""1440"" w:left=""1440"" w:header=""708"" w:footer=""708"" w:gutter=""0""/> | |||
| <w:cols w:space=""708""/> | |||
| <w:docGrid w:linePitch=""360""/> | |||
| </w:sectPr> | |||
| </w:body> | |||
| </w:document>" | |||
| ); | |||
| } | |||
| // Get all of the paragraphs in this document | |||
| DocX.paragraphs = from p in mainDoc.Descendants(XName.Get("p", "http://schemas.openxmlformats.org/wordprocessingml/2006/main")) | |||
| select new Paragraph(p); | |||
| #endregion | |||
| #region CustomFilePropertiesPart | |||
| // Get the custom file properties part from the package | |||
| customPropertiesPart = wdDoc.CustomFilePropertiesPart; | |||
| // This docx contains a customFilePropertyPart | |||
| if (customPropertiesPart != null) | |||
| { | |||
| // Load the customFilePropertyPart | |||
| using (TextReader tr = new StreamReader(customPropertiesPart.GetStream(FileMode.Open, FileAccess.Read))) | |||
| { | |||
| customPropDoc = XDocument.Load(tr, LoadOptions.PreserveWhitespace); | |||
| } | |||
| // Get all of the custom properties in this document | |||
| DocX.customProperties = from cp in customPropDoc.Descendants(XName.Get("property", customPropertiesSchema.NamespaceName)) | |||
| select new CustomProperty(cp); | |||
| } | |||
| #endregion | |||
| // Save the new docx file to disk and return | |||
| DocX created = new DocX(); | |||
| created.Save(); | |||
| return created; | |||
| } | |||
| /// <summary> | |||
| /// Loads a .docx file into a DocX object. | |||
| /// </summary> | |||
| @@ -390,7 +468,7 @@ namespace Novacode | |||
| /// <summary> | |||
| /// Renumber all ins and del ids in this .docx file. | |||
| /// </summary> | |||
| private static void RenumberIDs() | |||
| internal static void RenumberIDs() | |||
| { | |||
| IEnumerable<XAttribute> trackerIDs = | |||
| (from d in mainDoc.Descendants() | |||
| @@ -43,6 +43,7 @@ | |||
| <Reference Include="System.Core"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| </Reference> | |||
| <Reference Include="System.Drawing" /> | |||
| <Reference Include="System.XML" /> | |||
| <Reference Include="System.Xml.Linq"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| @@ -51,10 +52,15 @@ | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| </Reference> | |||
| <Reference Include="System.Data" /> | |||
| <Reference Include="WindowsBase"> | |||
| <RequiredTargetFramework>3.0</RequiredTargetFramework> | |||
| </Reference> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <Compile Include="CustomProperty.cs" /> | |||
| <Compile Include="Enumerations.cs" /> | |||
| <Compile Include="Extensions.cs" /> | |||
| <Compile Include="Formatting.cs" /> | |||
| <Compile Include="Paragraph.cs" /> | |||
| <Compile Include="DocX.cs" /> | |||
| <Compile Include="Properties\AssemblyInfo.cs" /> | |||
| @@ -0,0 +1,32 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using System.Drawing; | |||
| namespace Novacode | |||
| { | |||
| public static class Extensions | |||
| { | |||
| public static string ToHex(this Color source) | |||
| { | |||
| byte red = source.R; | |||
| byte green = source.G; | |||
| byte blue = source.B; | |||
| string redHex = red.ToString("X"); | |||
| if (redHex.Length < 2) | |||
| redHex = "0" + redHex; | |||
| string blueHex = blue.ToString("X"); | |||
| if (blueHex.Length < 2) | |||
| blueHex = "0" + blueHex; | |||
| string greenHex = green.ToString("X"); | |||
| if (greenHex.Length < 2) | |||
| greenHex = "0" + greenHex; | |||
| return string.Format("{0}{1}{2}", redHex, greenHex, blueHex); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,272 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using System.Xml.Linq; | |||
| using System.Drawing; | |||
| namespace Novacode | |||
| { | |||
| 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}; | |||
| public enum StrickThrough { none, strike, doubleStrike }; | |||
| public enum Misc { none, shadow, outline, outlineShadow, emboss, engrave}; | |||
| public enum CapsStyle { none, caps, smallCaps }; | |||
| public class Formatting | |||
| { | |||
| private XElement rPr; | |||
| private bool hidden; | |||
| private bool bold; | |||
| private bool italic; | |||
| private StrickThrough strikethrough; | |||
| private Script script; | |||
| private Highlight highlight; | |||
| private double? size; | |||
| private Color? fontColor; | |||
| private Color? underlineColor; | |||
| private UnderlineStyle underlineStyle; | |||
| private Misc misc; | |||
| private CapsStyle capsStyle; | |||
| private FontFamily fontFamily; | |||
| private int? percentageScale; | |||
| private int? kerning; | |||
| private int? position; | |||
| private double? spacing; | |||
| public Formatting() | |||
| { | |||
| capsStyle = CapsStyle.none; | |||
| strikethrough = StrickThrough.none; | |||
| script = Script.none; | |||
| highlight = Highlight.none; | |||
| underlineStyle = UnderlineStyle.none; | |||
| misc = Misc.none; | |||
| rPr = new XElement(XName.Get("rPr", DocX.w.NamespaceName)); | |||
| } | |||
| public XElement Xml | |||
| { | |||
| get | |||
| { | |||
| if(spacing.HasValue) | |||
| rPr.Add(new XElement(XName.Get("spacing", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), spacing.Value * 20))); | |||
| if(position.HasValue) | |||
| rPr.Add(new XElement(XName.Get("position", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), position.Value * 2))); | |||
| if (kerning.HasValue) | |||
| rPr.Add(new XElement(XName.Get("kern", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), kerning.Value * 2))); | |||
| if (percentageScale.HasValue) | |||
| rPr.Add(new XElement(XName.Get("w", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), percentageScale))); | |||
| if(fontFamily != null) | |||
| rPr.Add(new XElement(XName.Get("rFonts", DocX.w.NamespaceName), new XAttribute(XName.Get("ascii", DocX.w.NamespaceName), fontFamily.Name))); | |||
| if(hidden) | |||
| rPr.Add(new XElement(XName.Get("vanish", DocX.w.NamespaceName))); | |||
| if (bold) | |||
| rPr.Add(new XElement(XName.Get("b", DocX.w.NamespaceName))); | |||
| if (italic) | |||
| rPr.Add(new XElement(XName.Get("i", DocX.w.NamespaceName))); | |||
| switch (underlineStyle) | |||
| { | |||
| case UnderlineStyle.none: | |||
| break; | |||
| case UnderlineStyle.singleLine: | |||
| rPr.Add(new XElement(XName.Get("u", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), "single"))); | |||
| break; | |||
| case UnderlineStyle.doubleLine: | |||
| rPr.Add(new XElement(XName.Get("u", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), "double"))); | |||
| break; | |||
| default: | |||
| rPr.Add(new XElement(XName.Get("u", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), underlineStyle.ToString()))); | |||
| break; | |||
| } | |||
| if(underlineColor.HasValue) | |||
| { | |||
| // If an underlineColor has been set but no underlineStyle has been set | |||
| if (underlineStyle == UnderlineStyle.none) | |||
| { | |||
| // Set the underlineStyle to the default | |||
| underlineStyle = UnderlineStyle.singleLine; | |||
| rPr.Add(new XElement(XName.Get("u", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), "single"))); | |||
| } | |||
| rPr.Element(XName.Get("u", DocX.w.NamespaceName)).Add(new XAttribute(XName.Get("color", DocX.w.NamespaceName), underlineColor.Value.ToHex())); | |||
| } | |||
| switch (strikethrough) | |||
| { | |||
| case StrickThrough.none: | |||
| break; | |||
| case StrickThrough.strike: | |||
| rPr.Add(new XElement(XName.Get("strike", DocX.w.NamespaceName))); | |||
| break; | |||
| case StrickThrough.doubleStrike: | |||
| rPr.Add(new XElement(XName.Get("dstrike", DocX.w.NamespaceName))); | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| switch (script) | |||
| { | |||
| case Script.none: | |||
| break; | |||
| default: | |||
| rPr.Add(new XElement(XName.Get("vertAlign", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), script.ToString()))); | |||
| break; | |||
| } | |||
| if (size.HasValue) | |||
| { | |||
| rPr.Add(new XElement(XName.Get("sz", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), (size * 2).ToString()))); | |||
| rPr.Add(new XElement(XName.Get("szCs", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), (size * 2).ToString()))); | |||
| } | |||
| if(fontColor.HasValue) | |||
| rPr.Add(new XElement(XName.Get("color", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), fontColor.Value.ToHex()))); | |||
| switch (highlight) | |||
| { | |||
| case Highlight.none: | |||
| break; | |||
| default: | |||
| rPr.Add(new XElement(XName.Get("highlight", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), highlight.ToString()))); | |||
| break; | |||
| } | |||
| switch (capsStyle) | |||
| { | |||
| case CapsStyle.none: | |||
| break; | |||
| default: | |||
| rPr.Add(new XElement(XName.Get(capsStyle.ToString(), DocX.w.NamespaceName))); | |||
| break; | |||
| } | |||
| switch (misc) | |||
| { | |||
| case Misc.none: | |||
| break; | |||
| case Misc.outlineShadow: | |||
| rPr.Add(new XElement(XName.Get("outline", DocX.w.NamespaceName))); | |||
| rPr.Add(new XElement(XName.Get("shadow", DocX.w.NamespaceName))); | |||
| break; | |||
| case Misc.engrave: | |||
| rPr.Add(new XElement(XName.Get("imprint", DocX.w.NamespaceName))); | |||
| break; | |||
| default: | |||
| rPr.Add(new XElement(XName.Get(misc.ToString(), DocX.w.NamespaceName))); | |||
| break; | |||
| } | |||
| return rPr; | |||
| } | |||
| } | |||
| public bool Bold { get { return bold; } set { bold = value;} } | |||
| public bool Italic { get { return Italic; } set { italic = value; } } | |||
| public StrickThrough StrikeThrough { get { return strikethrough; } set { strikethrough = value; } } | |||
| public Script Script { get { return script; } set { script = value; } } | |||
| public double? Size | |||
| { | |||
| get { return size; } | |||
| set | |||
| { | |||
| double? temp = value * 2; | |||
| if (temp - (int)temp == 0) | |||
| { | |||
| if(value > 0 && value < 1639) | |||
| size = value; | |||
| else | |||
| throw new ArgumentException("Size", "Value must be in the range 0 - 1638"); | |||
| } | |||
| else | |||
| throw new ArgumentException("Size", "Value must be either a whole or half number, examples: 32, 32.5"); | |||
| } | |||
| } | |||
| public int? PercentageScale | |||
| { | |||
| get { return percentageScale; } | |||
| set | |||
| { | |||
| if ((new int?[] { 200, 150, 100, 90, 80, 66, 50, 33 }).Contains(value)) | |||
| percentageScale = value; | |||
| else | |||
| throw new ArgumentOutOfRangeException("PercentageScale", "Value must be one of the following: 200, 150, 100, 90, 80, 66, 50 or 33"); | |||
| } | |||
| } | |||
| public int? Kerning | |||
| { | |||
| get { return kerning; } | |||
| set | |||
| { | |||
| if(new int?[] {8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72}.Contains(value)) | |||
| kerning = value; | |||
| else | |||
| throw new ArgumentOutOfRangeException("Kerning", "Value must be one of the following: 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48 or 72"); | |||
| } | |||
| } | |||
| public int? Position | |||
| { | |||
| get { return position; } | |||
| set | |||
| { | |||
| if (value > -1585 && value < 1585) | |||
| position = value; | |||
| else | |||
| throw new ArgumentOutOfRangeException("Position", "Value must be in the range -1585 - 1585"); | |||
| } | |||
| } | |||
| public double? Spacing | |||
| { | |||
| get { return spacing; } | |||
| set | |||
| { | |||
| double? temp = value * 20; | |||
| if (temp - (int)temp == 0) | |||
| { | |||
| if (value > -1585 && value < 1585) | |||
| spacing = value; | |||
| else | |||
| throw new ArgumentException("Spacing", "Value must be in the range: -1584 - 1584"); | |||
| } | |||
| else | |||
| throw new ArgumentException("Spacing", "Value must be either a whole or acurate to one decimal, examples: 32, 32.1, 32.2, 32.9"); | |||
| } | |||
| } | |||
| public Color? FontColor { get { return fontColor; } set { fontColor = value; } } | |||
| public Highlight Highlight { get { return highlight; } set { highlight = value; } } | |||
| public UnderlineStyle UnderlineStyle { get { return underlineStyle; } set { underlineStyle = value; } } | |||
| public Color? UnderlineColor { get { return underlineColor; } set { underlineColor = value; } } | |||
| public Misc Misc { get { return misc; } set { misc = value; } } | |||
| public bool Hidden { get { return hidden; } set { hidden = value; } } | |||
| public CapsStyle CapsStyle { get { return capsStyle; } set { capsStyle = value; } } | |||
| public FontFamily FontFamily { get { return FontFamily; } set { fontFamily = value; } } | |||
| } | |||
| } | |||
| @@ -42,9 +42,13 @@ namespace Novacode | |||
| // Loop through each run in this paragraph | |||
| foreach (XElement run in runs) | |||
| { | |||
| Run r = new Run(startIndex, run); | |||
| runLookup.Add(r.EndIndex, r); | |||
| startIndex = r.EndIndex; | |||
| // Only add runs which contain text | |||
| if (GetElementTextLength(run) > 0) | |||
| { | |||
| Run r = new Run(startIndex, run); | |||
| runLookup.Add(r.EndIndex, r); | |||
| startIndex = r.EndIndex; | |||
| } | |||
| } | |||
| } | |||
| @@ -149,6 +153,10 @@ namespace Novacode | |||
| /// <returns>The first Run that will be effected</returns> | |||
| public Run GetFirstRunEffectedByInsert(int index) | |||
| { | |||
| // This paragraph contains no Runs and insertion is at index 0 | |||
| if (runLookup.Keys.Count() == 0 && index == 0) | |||
| return null; | |||
| foreach (int runEndIndex in runLookup.Keys) | |||
| { | |||
| if (runEndIndex >= index) | |||
| @@ -267,6 +275,11 @@ namespace Novacode | |||
| ); | |||
| } | |||
| public void Insert(int index, string value, bool trackChanges) | |||
| { | |||
| Insert(index, value, null, trackChanges); | |||
| } | |||
| /// <summary> | |||
| /// Inserts a specified instance of System.String into a Novacode.DocX.Paragraph at a specified index position. | |||
| /// </summary> | |||
| @@ -311,7 +324,7 @@ namespace Novacode | |||
| /// <param name="index">The index position of the insertion.</param> | |||
| /// <param name="value">The System.String to insert.</param> | |||
| /// <param name="trackChanges">Flag this insert as a change</param> | |||
| public void Insert(int index, string value, bool trackChanges) | |||
| public void Insert(int index, string value, Formatting formatting, bool trackChanges) | |||
| { | |||
| // Timestamp to mark the start of insert | |||
| DateTime now = DateTime.Now; | |||
| @@ -320,82 +333,102 @@ namespace Novacode | |||
| // Get the first run effected by this Insert | |||
| Run run = GetFirstRunEffectedByInsert(index); | |||
| // Convert value into a List of XElement's | |||
| List<XElement> newRuns = formatInput(value, run.Xml.Element(XName.Get("rPr", DocX.w.NamespaceName))); | |||
| if (run == null) | |||
| { | |||
| object insert; | |||
| if (formatting != null) | |||
| insert = formatInput(value, formatting.Xml); | |||
| else | |||
| insert = formatInput(value, null); | |||
| if (trackChanges) | |||
| insert = CreateEdit(EditType.ins, insert_datetime, insert); | |||
| p.Add(insert); | |||
| } | |||
| // The parent of this Run | |||
| XElement parentElement = run.Xml.Parent; | |||
| switch (parentElement.Name.LocalName) | |||
| else | |||
| { | |||
| case "ins": | |||
| { | |||
| // The datetime that this ins was created | |||
| DateTime parent_ins_date = DateTime.Parse(parentElement.Attribute(XName.Get("date", DocX.w.NamespaceName)).Value); | |||
| /* | |||
| * Special case: You want to track changes, | |||
| * and the first Run effected by this insert | |||
| * has a datetime stamp equal to now. | |||
| */ | |||
| if (trackChanges && parent_ins_date.CompareTo(insert_datetime) == 0) | |||
| object newRuns; | |||
| if (formatting != null) | |||
| newRuns = formatInput(value, formatting.Xml); | |||
| else | |||
| newRuns = formatInput(value, run.Xml.Element(XName.Get("rPr", DocX.w.NamespaceName))); | |||
| // The parent of this Run | |||
| XElement parentElement = run.Xml.Parent; | |||
| switch (parentElement.Name.LocalName) | |||
| { | |||
| case "ins": | |||
| { | |||
| // The datetime that this ins was created | |||
| DateTime parent_ins_date = DateTime.Parse(parentElement.Attribute(XName.Get("date", DocX.w.NamespaceName)).Value); | |||
| /* | |||
| * Special case: You want to track changes, | |||
| * and the first Run effected by this insert | |||
| * has a datetime stamp equal to now. | |||
| */ | |||
| if (trackChanges && parent_ins_date.CompareTo(insert_datetime) == 0) | |||
| { | |||
| /* | |||
| * Inserting into a non edit and this special case, is the same procedure. | |||
| */ | |||
| goto default; | |||
| } | |||
| /* | |||
| * Inserting into a non edit and this special case, is the same procedure. | |||
| * If not the special case above, | |||
| * then inserting into an ins or a del, is the same procedure. | |||
| */ | |||
| goto default; | |||
| goto case "del"; | |||
| } | |||
| /* | |||
| * If not the special case above, | |||
| * then inserting into an ins or a del, is the same procedure. | |||
| */ | |||
| goto case "del"; | |||
| } | |||
| case "del": | |||
| { | |||
| object insert = newRuns; | |||
| if (trackChanges) | |||
| insert = CreateEdit(EditType.ins, insert_datetime, newRuns); | |||
| case "del": | |||
| { | |||
| object insert = newRuns; | |||
| if (trackChanges) | |||
| insert = CreateEdit(EditType.ins, insert_datetime, newRuns); | |||
| // Split this Edit at the point you want to insert | |||
| XElement[] splitEdit = SplitEdit(parentElement, index, EditType.ins); | |||
| // Replace the origional run | |||
| parentElement.ReplaceWith | |||
| ( | |||
| splitEdit[0], | |||
| insert, | |||
| splitEdit[1] | |||
| ); | |||
| break; | |||
| } | |||
| // Split this Edit at the point you want to insert | |||
| XElement[] splitEdit = SplitEdit(parentElement, index, EditType.ins); | |||
| default: | |||
| { | |||
| object insert = newRuns; | |||
| if (trackChanges && !parentElement.Name.LocalName.Equals("ins")) | |||
| insert = CreateEdit(EditType.ins, insert_datetime, newRuns); | |||
| // Split this run at the point you want to insert | |||
| XElement[] splitRun = Run.SplitRun(run, index); | |||
| // Replace the origional run | |||
| run.Xml.ReplaceWith | |||
| ( | |||
| splitRun[0], | |||
| insert, | |||
| splitRun[1] | |||
| ); | |||
| break; | |||
| } | |||
| // Replace the origional run | |||
| parentElement.ReplaceWith | |||
| ( | |||
| splitEdit[0], | |||
| insert, | |||
| splitEdit[1] | |||
| ); | |||
| break; | |||
| } | |||
| default: | |||
| { | |||
| object insert = newRuns; | |||
| if (trackChanges && !parentElement.Name.LocalName.Equals("ins")) | |||
| insert = CreateEdit(EditType.ins, insert_datetime, newRuns); | |||
| // Split this run at the point you want to insert | |||
| XElement[] splitRun = Run.SplitRun(run, index); | |||
| // Replace the origional run | |||
| run.Xml.ReplaceWith | |||
| ( | |||
| splitRun[0], | |||
| insert, | |||
| splitRun[1] | |||
| ); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| // Rebuild the run lookup for this paragraph | |||
| runLookup.Clear(); | |||
| BuildRunLookup(p); | |||
| DocX.RenumberIDs(); | |||
| } | |||
| /// <summary> | |||
| @@ -446,7 +479,8 @@ namespace Novacode | |||
| int min = Math.Min(count - processed, run.Xml.ElementsAfterSelf().Sum(e => GetElementTextLength(e))); | |||
| XElement[] splitEditAfter = SplitEdit(parentElement, index + processed + min, EditType.del); | |||
| object middle = CreateEdit(EditType.del, remove_datetime, SplitEdit(splitEditBefore[1], min, EditType.del)[0].Descendants()); | |||
| XElement temp = SplitEdit(splitEditBefore[1], index + processed + min, EditType.del)[0]; | |||
| object middle = CreateEdit(EditType.del, remove_datetime, temp.Elements().ToArray()); | |||
| processed += GetElementTextLength(middle as XElement); | |||
| if (!trackChanges) | |||
| @@ -509,6 +543,7 @@ namespace Novacode | |||
| // Rebuild the run lookup | |||
| runLookup.Clear(); | |||
| BuildRunLookup(p); | |||
| DocX.RenumberIDs(); | |||
| } | |||
| /// <summary> | |||
| @@ -75,9 +75,13 @@ namespace Novacode | |||
| case "t": goto case "delText"; | |||
| case "delText": | |||
| { | |||
| textLookup.Add(start + text.Value.Length, new Text(start, text)); | |||
| Value += text.Value; | |||
| start += text.Value.Length; | |||
| // Only add strings which are not empty | |||
| if (text.Value.Length > 0) | |||
| { | |||
| textLookup.Add(start + text.Value.Length, new Text(start, text)); | |||
| Value += text.Value; | |||
| start += text.Value.Length; | |||
| } | |||
| break; | |||
| } | |||
| default: break; | |||
| @@ -5,6 +5,7 @@ using System.Text; | |||
| using Novacode; | |||
| using System.Text.RegularExpressions; | |||
| using System.IO; | |||
| using System.Xml.Linq; | |||
| namespace StringReplaceTestApp | |||
| { | |||
| @@ -35,10 +35,6 @@ | |||
| <WarningLevel>4</WarningLevel> | |||
| </PropertyGroup> | |||
| <ItemGroup> | |||
| <Reference Include="DocX, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> | |||
| <SpecificVersion>False</SpecificVersion> | |||
| <HintPath>..\DocX\bin\Debug\DocX.dll</HintPath> | |||
| </Reference> | |||
| <Reference Include="System" /> | |||
| <Reference Include="System.Core"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| @@ -61,6 +57,12 @@ | |||
| <CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
| </None> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ProjectReference Include="..\DocX\DocX.csproj"> | |||
| <Project>{E863D072-AA8B-4108-B5F1-785241B37F67}</Project> | |||
| <Name>DocX</Name> | |||
| </ProjectReference> | |||
| </ItemGroup> | |||
| <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
| <!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||
| Other similar extension points exist, see Microsoft.Common.targets. | |||