| @@ -0,0 +1,72 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
| <PropertyGroup> | |||
| <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||
| <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||
| <ProductVersion>9.0.30729</ProductVersion> | |||
| <SchemaVersion>2.0</SchemaVersion> | |||
| <ProjectGuid>{9EABCAA8-175C-4FA2-9829-59E9D9E275D3}</ProjectGuid> | |||
| <OutputType>Exe</OutputType> | |||
| <AppDesignerFolder>Properties</AppDesignerFolder> | |||
| <RootNamespace>CustomPropertyTestApp</RootNamespace> | |||
| <AssemblyName>CustomPropertyTestApp</AssemblyName> | |||
| <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> | |||
| <FileAlignment>512</FileAlignment> | |||
| <SccProjectName>SAK</SccProjectName> | |||
| <SccLocalPath>SAK</SccLocalPath> | |||
| <SccAuxPath>SAK</SccAuxPath> | |||
| <SccProvider>SAK</SccProvider> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | |||
| <DebugSymbols>true</DebugSymbols> | |||
| <DebugType>full</DebugType> | |||
| <Optimize>false</Optimize> | |||
| <OutputPath>bin\Debug\</OutputPath> | |||
| <DefineConstants>DEBUG;TRACE</DefineConstants> | |||
| <ErrorReport>prompt</ErrorReport> | |||
| <WarningLevel>4</WarningLevel> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | |||
| <DebugType>pdbonly</DebugType> | |||
| <Optimize>true</Optimize> | |||
| <OutputPath>bin\Release\</OutputPath> | |||
| <DefineConstants>TRACE</DefineConstants> | |||
| <ErrorReport>prompt</ErrorReport> | |||
| <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> | |||
| </Reference> | |||
| <Reference Include="System.Xml.Linq"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| </Reference> | |||
| <Reference Include="System.Data.DataSetExtensions"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| </Reference> | |||
| <Reference Include="System.Data" /> | |||
| <Reference Include="System.Xml" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <Compile Include="Program.cs" /> | |||
| <Compile Include="Properties\AssemblyInfo.cs" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <None Include="Template.docx"> | |||
| <CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
| </None> | |||
| </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. | |||
| <Target Name="BeforeBuild"> | |||
| </Target> | |||
| <Target Name="AfterBuild"> | |||
| </Target> | |||
| --> | |||
| </Project> | |||
| @@ -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" = "PROVIDER" | |||
| } | |||
| @@ -0,0 +1,78 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using System.IO; | |||
| using Novacode; | |||
| namespace CustomPropertyTestApp | |||
| { | |||
| // This class represents a user | |||
| class User | |||
| { | |||
| public string forname, username, freeGift, HomeAddress; | |||
| public DateTime joined; | |||
| public bool RecieveFurtherMail; | |||
| public User() | |||
| { } | |||
| } | |||
| class Program | |||
| { | |||
| static void Main(string[] args) | |||
| { | |||
| // A list which contains three new users | |||
| List<User> newUsers = new List<User> | |||
| { | |||
| new User | |||
| { | |||
| forname = "John", username = "John87", | |||
| freeGift = "toaster", joined = DateTime.Now, | |||
| HomeAddress = "21 Hillview, Naas, Co. Kildare", | |||
| RecieveFurtherMail = true | |||
| }, | |||
| new User | |||
| { | |||
| forname = "James", username = "KingJames", | |||
| freeGift = "kitchen knife", joined = DateTime.Now, | |||
| HomeAddress = "37 Mill Lane, Maynooth, Co. Meath", | |||
| RecieveFurtherMail = false | |||
| }, | |||
| new User | |||
| { | |||
| forname = "Mary", username = "McNamara1", | |||
| freeGift = "microwave", joined = DateTime.Now, | |||
| HomeAddress = "110 Cherry Orchard Drive, Navan, Co. Roscommon", RecieveFurtherMail= true | |||
| } | |||
| }; | |||
| // Foreach of the three new user create a welcome document based on template.docx | |||
| foreach (User newUser in newUsers) | |||
| { | |||
| // Copy template.docx so that we can customize it for this user | |||
| string filename = string.Format(@"{0}.docx", newUser.username); | |||
| File.Copy(@"Template.docx", filename, true); | |||
| /* | |||
| * Load the document to be manipulated and set the custom properties to this | |||
| * users specific data | |||
| */ | |||
| DocX doc = DocX.Load(filename); | |||
| doc.SetCustomProperty("Forname", CustomPropertyType.Text, newUser.forname); | |||
| doc.SetCustomProperty("Username", CustomPropertyType.Text, newUser.username); | |||
| doc.SetCustomProperty("FreeGift", CustomPropertyType.Text, newUser.freeGift); | |||
| doc.SetCustomProperty("HomeAddress", CustomPropertyType.Text, newUser.HomeAddress); | |||
| doc.SetCustomProperty("PleaseWaitNDays", CustomPropertyType.NumberInteger, 4); | |||
| doc.SetCustomProperty("GiftArrivalDate", CustomPropertyType.Date, newUser.joined.AddDays(4).ToUniversalTime()); | |||
| doc.SetCustomProperty("RecieveFurtherMail", CustomPropertyType.YesOrNo, newUser.RecieveFurtherMail); | |||
| // File will be saved to \CustomPropertyTestApp\bin\Debug | |||
| doc.Save(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -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("CustomPropertyTestApp")] | |||
| [assembly: AssemblyDescription("")] | |||
| [assembly: AssemblyConfiguration("")] | |||
| [assembly: AssemblyCompany("Microsoft IT")] | |||
| [assembly: AssemblyProduct("CustomPropertyTestApp")] | |||
| [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("54e1d528-31a1-4718-a659-472cbaf1a551")] | |||
| // 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")] | |||
| @@ -0,0 +1,69 @@ | |||
| | |||
| Microsoft Visual Studio Solution File, Format Version 10.00 | |||
| # Visual Studio 2008 | |||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocX", "DocX\DocX.csproj", "{E863D072-AA8B-4108-B5F1-785241B37F67}" | |||
| EndProject | |||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{6E4A2A95-9A9A-4F8C-A68D-4950E3455700}" | |||
| ProjectSection(ProjectDependencies) = postProject | |||
| {E863D072-AA8B-4108-B5F1-785241B37F67} = {E863D072-AA8B-4108-B5F1-785241B37F67} | |||
| EndProjectSection | |||
| EndProject | |||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DF41F5CE-8BCB-40CC-835F-54A3DB7D802F}" | |||
| ProjectSection(SolutionItems) = preProject | |||
| DocX.vsmdi = DocX.vsmdi | |||
| LocalTestRun.testrunconfig = LocalTestRun.testrunconfig | |||
| ..\..\..\..\..\Desktop\DocX zipped\ReadMe.docx = ..\..\..\..\..\Desktop\DocX zipped\ReadMe.docx | |||
| EndProjectSection | |||
| EndProject | |||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StringReplaceTestApp", "StringReplaceTestApp\StringReplaceTestApp.csproj", "{EA960F96-7CA4-4AA1-BB43-0478E3407C5C}" | |||
| EndProject | |||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomPropertyTestApp", "CustomPropertyTestApp\CustomPropertyTestApp.csproj", "{9EABCAA8-175C-4FA2-9829-59E9D9E275D3}" | |||
| EndProject | |||
| Global | |||
| GlobalSection(TeamFoundationVersionControl) = preSolution | |||
| SccNumberOfProjects = 5 | |||
| SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} | |||
| SccTeamFoundationServer = https://tfs08.codeplex.com/ | |||
| SccLocalPath0 = . | |||
| SccProjectUniqueName1 = CustomPropertyTestApp\\CustomPropertyTestApp.csproj | |||
| SccProjectName1 = CustomPropertyTestApp | |||
| SccLocalPath1 = CustomPropertyTestApp | |||
| SccProjectUniqueName2 = DocX\\DocX.csproj | |||
| SccProjectName2 = DocX | |||
| SccLocalPath2 = DocX | |||
| SccProjectUniqueName3 = StringReplaceTestApp\\StringReplaceTestApp.csproj | |||
| SccProjectName3 = StringReplaceTestApp | |||
| SccLocalPath3 = StringReplaceTestApp | |||
| SccProjectUniqueName4 = UnitTests\\UnitTests.csproj | |||
| SccProjectName4 = UnitTests | |||
| SccLocalPath4 = UnitTests | |||
| EndGlobalSection | |||
| GlobalSection(TestCaseManagementSettings) = postSolution | |||
| CategoryFile = DocX.vsmdi | |||
| EndGlobalSection | |||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
| Debug|Any CPU = Debug|Any CPU | |||
| Release|Any CPU = Release|Any CPU | |||
| EndGlobalSection | |||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | |||
| {E863D072-AA8B-4108-B5F1-785241B37F67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
| {E863D072-AA8B-4108-B5F1-785241B37F67}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
| {E863D072-AA8B-4108-B5F1-785241B37F67}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
| {E863D072-AA8B-4108-B5F1-785241B37F67}.Release|Any CPU.Build.0 = Release|Any CPU | |||
| {6E4A2A95-9A9A-4F8C-A68D-4950E3455700}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
| {6E4A2A95-9A9A-4F8C-A68D-4950E3455700}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
| {6E4A2A95-9A9A-4F8C-A68D-4950E3455700}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
| {6E4A2A95-9A9A-4F8C-A68D-4950E3455700}.Release|Any CPU.Build.0 = Release|Any CPU | |||
| {EA960F96-7CA4-4AA1-BB43-0478E3407C5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
| {EA960F96-7CA4-4AA1-BB43-0478E3407C5C}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
| {EA960F96-7CA4-4AA1-BB43-0478E3407C5C}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
| {EA960F96-7CA4-4AA1-BB43-0478E3407C5C}.Release|Any CPU.Build.0 = Release|Any CPU | |||
| {9EABCAA8-175C-4FA2-9829-59E9D9E275D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
| {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 | |||
| EndGlobalSection | |||
| GlobalSection(SolutionProperties) = preSolution | |||
| HideSolutionNode = FALSE | |||
| EndGlobalSection | |||
| EndGlobal | |||
| @@ -0,0 +1,6 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <TestLists xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2006"> | |||
| <TestList name="Lists of Tests" id="8c43106b-9dc1-4907-a29f-aa66a61bf5b6"> | |||
| <RunConfiguration id="6869d4b0-c7c0-449b-a3b2-541bd9ae1155" name="Local Test Run" storage="localtestrun.testrunconfig" type="Microsoft.VisualStudio.TestTools.Common.TestRunConfiguration, Microsoft.VisualStudio.QualityTools.Common, PublicKeyToken=b03f5f7f11d50a3a" /> | |||
| </TestList> | |||
| </TestLists> | |||
| @@ -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" | |||
| } | |||
| @@ -0,0 +1,81 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using System.Xml.Linq; | |||
| namespace Novacode | |||
| { | |||
| /// <summary> | |||
| /// Represents a .docx custom property. | |||
| /// </summary> | |||
| public class CustomProperty | |||
| { | |||
| // The underlying XElement which this CustomProperty wraps | |||
| private XElement cp; | |||
| // This customPropertys name | |||
| private string name; | |||
| // This customPropertys type | |||
| private CustomPropertyType type; | |||
| // This customPropertys value | |||
| private object value; | |||
| /// <summary> | |||
| /// Custom Propertys name. | |||
| /// </summary> | |||
| public string Name { get { return name; } } | |||
| /// <summary> | |||
| /// Custom Propertys type. | |||
| /// </summary> | |||
| public CustomPropertyType Type { get { return type; } } | |||
| /// <summary> | |||
| /// Custom Propertys value. | |||
| /// </summary> | |||
| public object Value { get { return value; } } | |||
| /// <summary> | |||
| /// Wraps a System.Xml.Linq.XElement as an instance of Novacode.DocX.CustomProperty | |||
| /// </summary> | |||
| /// <param name="cp">The XElement to wrap</param> | |||
| public CustomProperty(XElement cp) | |||
| { | |||
| this.cp = cp; | |||
| name = cp.Attribute(XName.Get("name")).Value; | |||
| XElement p = cp.Elements().SingleOrDefault(); | |||
| switch (p.Name.LocalName) | |||
| { | |||
| case "i4": | |||
| type = CustomPropertyType.NumberInteger; | |||
| value = int.Parse(p.Value); | |||
| break; | |||
| case "r8": | |||
| type = CustomPropertyType.NumberDecimal; | |||
| value = double.Parse(p.Value); | |||
| break; | |||
| case "lpwstr": | |||
| type = CustomPropertyType.Text; | |||
| value = p.Value; | |||
| break; | |||
| case "filetime": | |||
| type = CustomPropertyType.Date; | |||
| value = DateTime.Parse(p.Value); | |||
| break; | |||
| case "bool": | |||
| type = CustomPropertyType.YesOrNo; | |||
| value = bool.Parse(p.Value); | |||
| break; | |||
| default: | |||
| throw new Exception(string.Format("The custom property type {0} is not supported", p.Name.LocalName)); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,404 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using DocumentFormat.OpenXml.Packaging; | |||
| using System.Xml.Linq; | |||
| using System.Xml; | |||
| using System.IO; | |||
| using System.Text.RegularExpressions; | |||
| namespace Novacode | |||
| { | |||
| /// <summary> | |||
| /// Represents a .docx file. | |||
| /// </summary> | |||
| public class DocX: IDisposable | |||
| { | |||
| static internal string editSessionID; | |||
| public static XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; | |||
| /// <summary> | |||
| /// Default constructor | |||
| /// </summary> | |||
| public DocX() | |||
| { | |||
| editSessionID = Guid.NewGuid().ToString().Substring(0, 8); | |||
| } | |||
| #region Private static variable declarations | |||
| // The URI of this .docx | |||
| private static string uri; | |||
| // Object representation of the .docx | |||
| private static WordprocessingDocument wdDoc; | |||
| // Object representation of the \word\document.xml part | |||
| private static MainDocumentPart mainDocumentPart; | |||
| // Object representation of the \docProps\custom.xml part | |||
| private static CustomFilePropertiesPart customPropertiesPart = null; | |||
| // The mainDocument is loaded into a XDocument object for easy querying and editing | |||
| private static XDocument mainDoc; | |||
| // The customPropertyDocument is loaded into a XDocument object for easy querying and editing | |||
| private static XDocument customPropDoc; | |||
| // The collection of Paragraphs <w:p></w:p> in this .docx | |||
| private static IEnumerable<Paragraph> paragraphs; | |||
| // The collection of custom properties in this .docx | |||
| private static IEnumerable<CustomProperty> customProperties = new List<CustomProperty>(); | |||
| private static XNamespace customPropertiesSchema = "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"; | |||
| private static XNamespace customVTypesSchema = "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"; | |||
| private Random random = new Random(); | |||
| #endregion | |||
| /// <summary> | |||
| /// Gets the paragraphs of the .docx file. | |||
| /// </summary> | |||
| /// <seealso cref="Paragraph.Remove"/> | |||
| /// <seealso cref="Paragraph.Insert"/> | |||
| /// <seealso cref="Paragraph.Replace"/> | |||
| /// <example> | |||
| /// <code> | |||
| /// // Load Test.docx | |||
| /// DocX dx = DocX.Load(@"C:\Example.docx"); | |||
| /// | |||
| /// // Iterate through the paragraphs | |||
| /// foreach(Paragraph p in dx.Paragraphs) | |||
| /// { | |||
| /// string paragraphText = p.Value; | |||
| /// } | |||
| /// </code> | |||
| /// </example> | |||
| public IEnumerable<Paragraph> Paragraphs | |||
| { | |||
| get { return paragraphs; } | |||
| } | |||
| /// <summary> | |||
| /// Gets the custom properties of the .docx file. | |||
| /// </summary> | |||
| /// <seealso cref="SetCustomProperty"/> | |||
| /// <example> | |||
| /// <code> | |||
| /// // Load Example.docx | |||
| /// DocX dx = DocX.Load(@"C:\Example.docx"); | |||
| /// | |||
| /// // Iterate through the custom properties | |||
| /// foreach(CustomProperty cp in dx.CustomProperties) | |||
| /// { | |||
| /// string customPropertyName = cp.Name; | |||
| /// CustomPropertyType customPropertyType = cp.Type; | |||
| /// object customPropertyValue = cp.Value; | |||
| /// } | |||
| /// </code> | |||
| /// </example> | |||
| public IEnumerable<CustomProperty> CustomProperties | |||
| { | |||
| get { return customProperties; } | |||
| } | |||
| /// <summary> | |||
| /// Loads a .docx file into a DocX object. | |||
| /// </summary> | |||
| /// <param name="uri">The fully qualified name of the .docx file, or the relative file name.</param> | |||
| /// <returns> | |||
| /// Returns a DocX object which represents the .docx file. | |||
| /// </returns> | |||
| /// <example> | |||
| /// <code> | |||
| /// // Load Example.docx | |||
| /// DocX dx = DocX.Load(@"C:\Example.docx"); | |||
| /// </code> | |||
| /// </example> | |||
| public static DocX Load(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)); | |||
| if (!fi.Exists) | |||
| throw new Exception(string.Format("The input file {0} does not exist", fi.FullName)); | |||
| DocX.uri = uri; | |||
| // Open the docx package | |||
| wdDoc = WordprocessingDocument.Open(uri, true); | |||
| #region MainDocumentPart | |||
| // Get the main document part from the package | |||
| mainDocumentPart = wdDoc.MainDocumentPart; | |||
| // Load the document part into a XDocument object | |||
| using (TextReader tr = new StreamReader(mainDocumentPart.GetStream(FileMode.Open, FileAccess.Read))) | |||
| { | |||
| mainDoc = XDocument.Load(tr, LoadOptions.PreserveWhitespace); | |||
| } | |||
| // 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 | |||
| return new DocX(); | |||
| } | |||
| /// <summary> | |||
| /// Saves all changes made to the DocX object back to the .docx file it represents. | |||
| /// </summary> | |||
| /// <example> | |||
| /// <code> | |||
| /// // Load Example.docx | |||
| /// DocX dx = DocX.Load(@"C:\Example.docx"); | |||
| /// | |||
| /// // Insert code to do something useful with dx | |||
| /// | |||
| /// // Save changes to Example.docx | |||
| /// dx.Save(); | |||
| /// </code> | |||
| /// </example> | |||
| public void Save() | |||
| { | |||
| // Save the main document part back to disk | |||
| using (TextWriter w = new StreamWriter(mainDocumentPart.GetStream(FileMode.Create, FileAccess.Write))) | |||
| { | |||
| mainDoc.Save(w, SaveOptions.DisableFormatting); | |||
| } | |||
| if (customPropertiesPart != null) | |||
| { | |||
| // Save the custom properties part back to disk | |||
| using (TextWriter w = new StreamWriter(customPropertiesPart.GetStream(FileMode.Create, FileAccess.Write))) | |||
| { | |||
| customPropDoc.Save(w, SaveOptions.DisableFormatting); | |||
| } | |||
| } | |||
| // Save and close the .docx file | |||
| wdDoc.Close(); | |||
| // Reopen the .docx file | |||
| Load(uri); | |||
| } | |||
| #region IDisposable Members | |||
| /// <summary> | |||
| /// Releases all resources used by Novacode.DocX object. | |||
| /// </summary> | |||
| public void Dispose() | |||
| { | |||
| wdDoc.Dispose(); | |||
| } | |||
| #endregion | |||
| /// <summary> | |||
| /// Set the value of a custom property. If no custom property exists with the name customPropertyName, then a new custom property is created. | |||
| /// </summary> | |||
| /// <param name="customPropertyName">The name of the custom property to set</param> | |||
| /// <param name="customPropertyType">The type of the custom property</param> | |||
| /// <param name="customPropertyValue">The value of the custom property</param> | |||
| /// <example> | |||
| /// <code> | |||
| /// // Load Example.docx | |||
| /// DocX dx = DocX.Load(@"C:\Example.docx"); | |||
| /// | |||
| /// // Add custom property 'Name' | |||
| /// dx.SetCustomProperty("Name", CustomPropertyType.Text, "Helium"); | |||
| /// | |||
| /// // Add custom property 'Date discovered' | |||
| /// dx.SetCustomProperty("Date discovered", CustomPropertyType.Date, new DateTime(1868, 08, 18)); | |||
| /// | |||
| /// // Add the custom property 'Noble gas' | |||
| /// dx.SetCustomProperty("Noble gas", CustomPropertyType.YesOrNo, true); | |||
| /// | |||
| /// // Add the custom property 'Atomic number' | |||
| /// dx.SetCustomProperty("Atomic number", CustomPropertyType.NumberInteger, 2); | |||
| /// | |||
| /// // Add the custom property 'Boiling point' | |||
| /// dx.SetCustomProperty("Boiling point", CustomPropertyType.NumberDecimal, -268.93); | |||
| /// | |||
| /// // Save changes to Example.docx | |||
| /// dx.Save(); | |||
| /// </code> | |||
| /// <code> | |||
| /// // Load Example.docx | |||
| /// DocX dx = DocX.Load(@"C:\Example.docx"); | |||
| /// | |||
| /// // Add the custom property 'LCID' | |||
| /// dx.SetCustomProperty("LCID", CustomPropertyType.NumberInteger, 1036); | |||
| /// | |||
| /// // Save changes to Example.docx | |||
| /// dx.Save(); | |||
| /// | |||
| /// // Update the custom property 'LCID' with a new value | |||
| /// dx.SetCustomProperty("LCID", CustomPropertyType.NumberInteger, 1041); | |||
| /// | |||
| /// // Save changes to Example.docx | |||
| /// dx.Save(); | |||
| /// </code> | |||
| /// </example> | |||
| public void SetCustomProperty(string customPropertyName, CustomPropertyType customPropertyType, object customPropertyValue) | |||
| { | |||
| string type = ""; | |||
| string value = ""; | |||
| switch (customPropertyType) | |||
| { | |||
| case CustomPropertyType.Text: | |||
| { | |||
| if (!(customPropertyValue is string)) | |||
| throw new Exception("Not a string"); | |||
| type = "lpwstr"; | |||
| value = customPropertyValue as string; | |||
| break; | |||
| } | |||
| case CustomPropertyType.Date: | |||
| { | |||
| if (!(customPropertyValue is DateTime)) | |||
| throw new Exception("Not a DateTime"); | |||
| type = "filetime"; | |||
| // Must be UTC time | |||
| value = (((DateTime)customPropertyValue).ToUniversalTime()).ToString(); | |||
| break; | |||
| } | |||
| case CustomPropertyType.YesOrNo: | |||
| { | |||
| if (!(customPropertyValue is bool)) | |||
| throw new Exception("Not a Boolean"); | |||
| type = "bool"; | |||
| // Must be lower case either {true or false} | |||
| value = (((bool)customPropertyValue)).ToString().ToLower(); | |||
| break; | |||
| } | |||
| case CustomPropertyType.NumberInteger: | |||
| { | |||
| if (!(customPropertyValue is int)) | |||
| throw new Exception("Not an int"); | |||
| type = "i4"; | |||
| value = customPropertyValue.ToString(); | |||
| break; | |||
| } | |||
| case CustomPropertyType.NumberDecimal: | |||
| { | |||
| if (!(customPropertyValue is double)) | |||
| throw new Exception("Not a double"); | |||
| type = "r8"; | |||
| value = customPropertyValue.ToString(); | |||
| break; | |||
| } | |||
| } | |||
| // This docx does not contain a customFilePropertyPart | |||
| if (customPropertiesPart == null) | |||
| { | |||
| customPropertiesPart = wdDoc.AddCustomFilePropertiesPart(); | |||
| customPropDoc = new XDocument(new XDeclaration("1.0", "UTF-8", "yes"), | |||
| new XElement(XName.Get("Properties", customPropertiesSchema.NamespaceName), | |||
| new XAttribute(XNamespace.Xmlns + "vt", customVTypesSchema), | |||
| new XElement(XName.Get("property", customPropertiesSchema.NamespaceName), | |||
| new XAttribute("fmtid", "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"), | |||
| new XAttribute("pid", "2"), | |||
| new XAttribute("name", customPropertyName), | |||
| new XElement(customVTypesSchema + type, customPropertyValue) | |||
| ))); | |||
| } | |||
| // This docx contains a customFilePropertyPart | |||
| else | |||
| { | |||
| // Get the highest PID | |||
| int pid = (from d in customPropDoc.Descendants() | |||
| where d.Name.LocalName == "property" | |||
| select int.Parse(d.Attribute(XName.Get("pid")).Value)).Max<int>(); | |||
| // Get the custom property or null | |||
| var customProperty = (from d in customPropDoc.Descendants() | |||
| where (d.Name.LocalName == "property") && (d.Attribute(XName.Get("name")).Value == customPropertyName) | |||
| select d).SingleOrDefault(); | |||
| if (customProperty != null) | |||
| { | |||
| customProperty.Descendants(XName.Get(type, customVTypesSchema.NamespaceName)).SingleOrDefault().ReplaceWith( | |||
| new XElement(customVTypesSchema + type, customPropertyValue)); | |||
| } | |||
| else | |||
| { | |||
| XElement propertiesElement = customPropDoc.Element(XName.Get("Properties", customPropertiesSchema.NamespaceName)); | |||
| propertiesElement.Add(new XElement(XName.Get("property", customPropertiesSchema.NamespaceName), | |||
| new XAttribute("fmtid", "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"), | |||
| new XAttribute("pid", pid + 1), | |||
| new XAttribute("name", customPropertyName), | |||
| new XElement(customVTypesSchema + type, customPropertyValue) | |||
| )); | |||
| } | |||
| } | |||
| UpdateCustomPropertyValue(customPropertyName, customPropertyValue.ToString()); | |||
| } | |||
| private static void UpdateCustomPropertyValue(string customPropertyName, string customPropertyValue) | |||
| { | |||
| foreach (XElement e in mainDoc.Descendants(XName.Get("fldSimple", w.NamespaceName))) | |||
| { | |||
| if (e.Attribute(XName.Get("instr", w.NamespaceName)).Value.Equals(string.Format(@" DOCPROPERTY {0} \* MERGEFORMAT ", customPropertyName), StringComparison.CurrentCultureIgnoreCase)) | |||
| { | |||
| XElement firstRun = e.Element(w + "r"); | |||
| // Delete everything and insert updated text value | |||
| e.RemoveNodes(); | |||
| XElement t = new XElement(w + "t", customPropertyValue); | |||
| Text.PreserveSpace(t); | |||
| e.Add(new XElement(firstRun.Name, firstRun.Attributes(), firstRun.Element(XName.Get("rPr", w.NamespaceName)), t)); | |||
| } | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// Renumber all ins and del ids in this .docx file. | |||
| /// </summary> | |||
| private static void RenumberIDs() | |||
| { | |||
| IEnumerable<XAttribute> trackerIDs = | |||
| (from d in 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(); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,74 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
| <PropertyGroup> | |||
| <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||
| <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||
| <ProductVersion>9.0.30729</ProductVersion> | |||
| <SchemaVersion>2.0</SchemaVersion> | |||
| <ProjectGuid>{E863D072-AA8B-4108-B5F1-785241B37F67}</ProjectGuid> | |||
| <OutputType>Library</OutputType> | |||
| <AppDesignerFolder>Properties</AppDesignerFolder> | |||
| <RootNamespace>Novacode</RootNamespace> | |||
| <AssemblyName>DocX</AssemblyName> | |||
| <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> | |||
| <FileAlignment>512</FileAlignment> | |||
| <SccProjectName>SAK</SccProjectName> | |||
| <SccLocalPath>SAK</SccLocalPath> | |||
| <SccAuxPath>SAK</SccAuxPath> | |||
| <SccProvider>SAK</SccProvider> | |||
| <StartupObject> | |||
| </StartupObject> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | |||
| <DebugSymbols>true</DebugSymbols> | |||
| <DebugType>full</DebugType> | |||
| <Optimize>false</Optimize> | |||
| <OutputPath>bin\Debug\</OutputPath> | |||
| <DefineConstants>DEBUG;TRACE</DefineConstants> | |||
| <ErrorReport>prompt</ErrorReport> | |||
| <WarningLevel>4</WarningLevel> | |||
| <DocumentationFile>bin\Debug\DocX.XML</DocumentationFile> | |||
| <DeepSeaObfuscate>false</DeepSeaObfuscate> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | |||
| <DebugType>pdbonly</DebugType> | |||
| <Optimize>true</Optimize> | |||
| <OutputPath>bin\Release\</OutputPath> | |||
| <DefineConstants>TRACE</DefineConstants> | |||
| <ErrorReport>prompt</ErrorReport> | |||
| <WarningLevel>4</WarningLevel> | |||
| </PropertyGroup> | |||
| <ItemGroup> | |||
| <Reference Include="DocumentFormat.OpenXml, Version=2.0.3302.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" /> | |||
| <Reference Include="System" /> | |||
| <Reference Include="System.Core"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| </Reference> | |||
| <Reference Include="System.XML" /> | |||
| <Reference Include="System.Xml.Linq"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| </Reference> | |||
| <Reference Include="System.Data.DataSetExtensions"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| </Reference> | |||
| <Reference Include="System.Data" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <Compile Include="CustomProperty.cs" /> | |||
| <Compile Include="Enumerations.cs" /> | |||
| <Compile Include="Paragraph.cs" /> | |||
| <Compile Include="DocX.cs" /> | |||
| <Compile Include="Properties\AssemblyInfo.cs" /> | |||
| <Compile Include="Run.cs" /> | |||
| <Compile Include="Text.cs" /> | |||
| </ItemGroup> | |||
| <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
| <Import Project="$(MSBuildExtensionsPath)\DeepSea Obfuscator\DeepSea.Obfuscator.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. | |||
| <Target Name="BeforeBuild"> | |||
| </Target> | |||
| <Target Name="AfterBuild"> | |||
| </Target> | |||
| --> | |||
| </Project> | |||
| @@ -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" = "PROVIDER" | |||
| } | |||
| @@ -0,0 +1,64 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| namespace Novacode | |||
| { | |||
| /// <summary> | |||
| /// Paragraph edit types | |||
| /// </summary> | |||
| public enum EditType | |||
| { | |||
| /// <summary> | |||
| /// A ins is a tracked insertion | |||
| /// </summary> | |||
| ins, | |||
| /// <summary> | |||
| /// A del is tracked deletion | |||
| /// </summary> | |||
| del | |||
| } | |||
| /// <summary> | |||
| /// Custom property types. | |||
| /// </summary> | |||
| public enum CustomPropertyType | |||
| { | |||
| /// <summary> | |||
| /// System.String | |||
| /// </summary> | |||
| Text, | |||
| /// <summary> | |||
| /// System.DateTime | |||
| /// </summary> | |||
| Date, | |||
| /// <summary> | |||
| /// System.Int32 | |||
| /// </summary> | |||
| NumberInteger, | |||
| /// <summary> | |||
| /// System.Double | |||
| /// </summary> | |||
| NumberDecimal, | |||
| /// <summary> | |||
| /// System.Boolean | |||
| /// </summary> | |||
| YesOrNo | |||
| } | |||
| /// <summary> | |||
| /// Text types in a Run | |||
| /// </summary> | |||
| public enum RunTextType | |||
| { | |||
| /// <summary> | |||
| /// System.String | |||
| /// </summary> | |||
| Text, | |||
| /// <summary> | |||
| /// System.String | |||
| /// </summary> | |||
| DelText, | |||
| } | |||
| } | |||
| @@ -0,0 +1,556 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using System.Xml.Linq; | |||
| using System.Text.RegularExpressions; | |||
| using DocumentFormat.OpenXml.Drawing; | |||
| using System.Security.Principal; | |||
| using System.Collections; | |||
| namespace Novacode | |||
| { | |||
| /// <summary> | |||
| /// Represents a .docx paragraph. | |||
| /// </summary> | |||
| public class Paragraph | |||
| { | |||
| // A lookup for the runs in this paragraph | |||
| Dictionary<int, Run> runLookup = new Dictionary<int, Run>(); | |||
| // The underlying XElement which this Paragraph wraps | |||
| private XElement p; | |||
| /// <summary> | |||
| /// Wraps a XElement as a Paragraph. | |||
| /// </summary> | |||
| /// <param name="p">The XElement to wrap.</param> | |||
| public Paragraph(XElement p) | |||
| { | |||
| this.p = p; | |||
| BuildRunLookup(p); | |||
| } | |||
| private void BuildRunLookup(XElement p) | |||
| { | |||
| // Get the runs in this paragraph | |||
| IEnumerable<XElement> runs = p.Descendants(XName.Get("r", "http://schemas.openxmlformats.org/wordprocessingml/2006/main")); | |||
| int startIndex = 0; | |||
| // 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; | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// Gets the value of this Novacode.DocX.Paragraph. | |||
| /// </summary> | |||
| public string Value | |||
| { | |||
| // Returns the underlying XElement's Value property. | |||
| get | |||
| { | |||
| StringBuilder sb = new StringBuilder(); | |||
| // Loop through each run in this paragraph | |||
| foreach (XElement r in p.Descendants(XName.Get("r", DocX.w.NamespaceName))) | |||
| { | |||
| // Loop through each text item in this run | |||
| foreach (XElement descendant in r.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; | |||
| } | |||
| } | |||
| } | |||
| return sb.ToString(); | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// Creates an Edit either a ins or a del with the specified content and date | |||
| /// </summary> | |||
| /// <param name="t">The type of this edit (ins or del)</param> | |||
| /// <param name="edit_time">The time stamp to use for this edit</param> | |||
| /// <param name="content">The initial content of this edit</param> | |||
| /// <returns></returns> | |||
| private XElement CreateEdit(EditType t, DateTime edit_time, params object[] content) | |||
| { | |||
| if (t == EditType.del) | |||
| { | |||
| foreach (object o in content) | |||
| { | |||
| if (o is XElement) | |||
| { | |||
| XElement e = (o as XElement); | |||
| IEnumerable<XElement> 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) | |||
| ); | |||
| } | |||
| /// <summary> | |||
| /// Return the first Run that will be effected by an edit at the index | |||
| /// </summary> | |||
| /// <param name="index">The index of this edit</param> | |||
| /// <returns>The first Run that will be effected</returns> | |||
| public Run GetFirstRunEffectedByEdit(int index) | |||
| { | |||
| foreach (int runEndIndex in runLookup.Keys) | |||
| { | |||
| if (runEndIndex > index) | |||
| return runLookup[runEndIndex]; | |||
| } | |||
| if (runLookup.Last().Value.EndIndex == index) | |||
| return runLookup.Last().Value; | |||
| throw new ArgumentOutOfRangeException(); | |||
| } | |||
| /// <summary> | |||
| /// Return the first Run that will be effected by an edit at the index | |||
| /// </summary> | |||
| /// <param name="index">The index of this edit</param> | |||
| /// <returns>The first Run that will be effected</returns> | |||
| public Run GetFirstRunEffectedByInsert(int index) | |||
| { | |||
| foreach (int runEndIndex in runLookup.Keys) | |||
| { | |||
| if (runEndIndex >= index) | |||
| return runLookup[runEndIndex]; | |||
| } | |||
| throw new ArgumentOutOfRangeException(); | |||
| } | |||
| /// <summary> | |||
| /// If the value to be inserted contains tab elements or br elements, multiple runs will be inserted. | |||
| /// </summary> | |||
| /// <param name="text">The text to be inserted, this text can contain the special characters \t (tab) and \n (br)</param> | |||
| /// <returns></returns> | |||
| private List<XElement> formatInput(string text, XElement rPr) | |||
| { | |||
| List<XElement> newRuns = new List<XElement>(); | |||
| XElement tabRun = new XElement(DocX.w + "tab"); | |||
| string[] runTexts = text.Split('\t'); | |||
| XElement firstRun; | |||
| if (runTexts[0] != String.Empty) | |||
| { | |||
| XElement firstText = new XElement(DocX.w + "t", runTexts[0]); | |||
| Text.PreserveSpace(firstText); | |||
| firstRun = new XElement(DocX.w + "r", rPr, firstText); | |||
| newRuns.Add(firstRun); | |||
| } | |||
| if (runTexts.Length > 1) | |||
| { | |||
| for (int k = 1; k < runTexts.Length; k++) | |||
| { | |||
| XElement newText = new XElement(DocX.w + "t", runTexts[k]); | |||
| XElement newRun; | |||
| if (runTexts[k] == String.Empty) | |||
| newRun = new XElement(DocX.w + "r", tabRun); | |||
| else | |||
| { | |||
| // Value begins or ends with a space | |||
| Text.PreserveSpace(newText); | |||
| newRun = new XElement(DocX.w + "r", rPr, tabRun, newText); | |||
| } | |||
| newRuns.Add(newRun); | |||
| } | |||
| } | |||
| return newRuns; | |||
| } | |||
| /// <summary> | |||
| /// Counts the text length of an element | |||
| /// </summary> | |||
| /// <param name="run">An element</param> | |||
| /// <returns>The length of this elements text</returns> | |||
| public static int GetElementTextLength(XElement run) | |||
| { | |||
| int count = 0; | |||
| if (run == null) | |||
| return count; | |||
| foreach (var d in run.Descendants()) | |||
| { | |||
| switch (d.Name.LocalName) | |||
| { | |||
| case "tab": goto case "br"; | |||
| case "br": count++; break; | |||
| case "t": goto case "delText"; | |||
| case "delText": count += d.Value.Length; break; | |||
| default: break; | |||
| } | |||
| } | |||
| return count; | |||
| } | |||
| /// <summary> | |||
| /// Splits an edit element at the specified run, at the specified index. | |||
| /// </summary> | |||
| /// <param name="edit">The edit element to split</param> | |||
| /// <param name="run">The run element to split</param> | |||
| /// <param name="index">The index to split at</param> | |||
| /// <returns></returns> | |||
| public XElement[] SplitEdit(XElement edit, int index, EditType type) | |||
| { | |||
| Run run; | |||
| if(type == EditType.del) | |||
| run = GetFirstRunEffectedByEdit(index); | |||
| else | |||
| run = GetFirstRunEffectedByInsert(index); | |||
| XElement[] splitRun = Run.SplitRun(run, index); | |||
| XElement splitLeft = new XElement(edit.Name, edit.Attributes(), run.Xml.ElementsBeforeSelf(), splitRun[0]); | |||
| if (GetElementTextLength(splitLeft) == 0) | |||
| splitLeft = null; | |||
| XElement splitRight = new XElement(edit.Name, edit.Attributes(), splitRun[1], run.Xml.ElementsAfterSelf()); | |||
| if (GetElementTextLength(splitRight) == 0) | |||
| splitRight = null; | |||
| return | |||
| ( | |||
| new XElement[] | |||
| { | |||
| splitLeft, | |||
| splitRight | |||
| } | |||
| ); | |||
| } | |||
| /// <summary> | |||
| /// Inserts a specified instance of System.String into a Novacode.DocX.Paragraph at a specified index position. | |||
| /// </summary> | |||
| /// <example> | |||
| /// <code> | |||
| /// // Description: Simple string insertion | |||
| /// | |||
| /// // Load Example.docx | |||
| /// DocX dx = DocX.Load(@"C:\Example.docx"); | |||
| /// | |||
| /// // Iterate through the paragraphs | |||
| /// foreach (Paragraph p in dx.Paragraphs) | |||
| /// { | |||
| /// // Insert the string "Start: " at the begining of every paragraph and flag it as a change. | |||
| /// p.Insert(0, "Start: ", true); | |||
| /// } | |||
| /// | |||
| /// // Save changes to Example.docx | |||
| /// dx.Save(); | |||
| /// </code> | |||
| /// </example> | |||
| /// <example> | |||
| /// <code> | |||
| /// // Description: Inserting tabs using the \t switch | |||
| /// | |||
| /// // Load Example.docx | |||
| /// DocX dx = DocX.Load(@"C:\Example.docx"); | |||
| /// | |||
| /// // Iterate through the paragraphs | |||
| /// foreach (Paragraph p in dx.Paragraphs) | |||
| /// { | |||
| /// // Insert the string "\tStart:\t" at the begining of every paragraph and flag it as a change. | |||
| /// p.Insert(0, "\tStart:\t", true); | |||
| /// } | |||
| /// | |||
| /// // Save changes to Example.docx | |||
| /// dx.Save(); | |||
| /// </code> | |||
| /// </example> | |||
| /// <seealso cref="Paragraph.Remove"/> | |||
| /// <seealso cref="Paragraph.Replace"/> | |||
| /// <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) | |||
| { | |||
| // Timestamp to mark the start of insert | |||
| DateTime now = DateTime.Now; | |||
| DateTime insert_datetime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0, DateTimeKind.Utc); | |||
| // 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))); | |||
| // 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; | |||
| } | |||
| /* | |||
| * 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); | |||
| // 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; | |||
| } | |||
| 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); | |||
| } | |||
| /// <summary> | |||
| /// Removes characters from a Novacode.DocX.Paragraph. | |||
| /// </summary> | |||
| /// <example> | |||
| /// <code> | |||
| /// // Load Example.docx | |||
| /// DocX dx = DocX.Load(@"C:\Example.docx"); | |||
| /// | |||
| /// // Iterate through the paragraphs | |||
| /// foreach (Paragraph p in dx.Paragraphs) | |||
| /// { | |||
| /// // Remove the first two characters from every paragraph | |||
| /// p.Remove(0, 2); | |||
| /// } | |||
| /// | |||
| /// // Save changes to Example.docx | |||
| /// dx.Save(); | |||
| /// </code> | |||
| /// </example> | |||
| /// <seealso cref="Paragraph.Insert"/> | |||
| /// <seealso cref="Paragraph.Replace"/> | |||
| /// <param name="index">The position to begin deleting characters.</param> | |||
| /// <param name="count">The number of characters to delete</param> | |||
| /// <param name="trackChanges">Track changes</param> | |||
| public void Remove(int index, int count, bool trackChanges) | |||
| { | |||
| // Timestamp to mark the start of insert | |||
| DateTime now = DateTime.Now; | |||
| DateTime remove_datetime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0, DateTimeKind.Utc); | |||
| // The number of characters processed so far | |||
| int processed = 0; | |||
| do | |||
| { | |||
| // Get the first run effected by this Remove | |||
| Run run = GetFirstRunEffectedByEdit(index + processed); | |||
| // The parent of this Run | |||
| XElement parentElement = run.Xml.Parent; | |||
| switch (parentElement.Name.LocalName) | |||
| { | |||
| case "ins": | |||
| { | |||
| XElement[] splitEditBefore = SplitEdit(parentElement, index + processed, EditType.del); | |||
| 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()); | |||
| processed += GetElementTextLength(middle as XElement); | |||
| if (!trackChanges) | |||
| middle = null; | |||
| parentElement.ReplaceWith | |||
| ( | |||
| splitEditBefore[0], | |||
| middle, | |||
| splitEditAfter[1] | |||
| ); | |||
| processed += GetElementTextLength(middle as XElement); | |||
| break; | |||
| } | |||
| case "del": | |||
| { | |||
| if (trackChanges) | |||
| { | |||
| // You cannot delete from a deletion, advance processed to the end of this del | |||
| processed += GetElementTextLength(parentElement); | |||
| } | |||
| else | |||
| goto case "ins"; | |||
| break; | |||
| } | |||
| default: | |||
| { | |||
| XElement[] splitRunBefore = Run.SplitRun(run, index + processed); | |||
| int min = Math.Min(index + processed + (count - processed), run.EndIndex); | |||
| XElement[] splitRunAfter = Run.SplitRun(run, min); | |||
| object middle = CreateEdit(EditType.del, remove_datetime, Run.SplitRun(new Run(run.StartIndex + GetElementTextLength(splitRunBefore[0]), splitRunBefore[1]), min)[0]); | |||
| processed += GetElementTextLength(middle as XElement); | |||
| if (!trackChanges) | |||
| middle = null; | |||
| run.Xml.ReplaceWith | |||
| ( | |||
| splitRunBefore[0], | |||
| middle, | |||
| splitRunAfter[1] | |||
| ); | |||
| break; | |||
| } | |||
| } | |||
| // If after this remove the parent element is empty, remove it. | |||
| if (GetElementTextLength(parentElement) == 0) | |||
| parentElement.Remove(); | |||
| } | |||
| while (processed < count); | |||
| // Rebuild the run lookup | |||
| runLookup.Clear(); | |||
| BuildRunLookup(p); | |||
| } | |||
| /// <summary> | |||
| /// Replaces all occurrences of a specified System.String in this instance, with another specified System.String. | |||
| /// </summary> | |||
| /// <example> | |||
| /// <code> | |||
| /// // Load Example.docx | |||
| /// DocX dx = DocX.Load(@"C:\Example.docx"); | |||
| /// | |||
| /// // Iterate through the paragraphs | |||
| /// foreach (Paragraph p in dx.Paragraphs) | |||
| /// { | |||
| /// // Replace all instances of the string "wrong" with the stirng "right" | |||
| /// p.Replace("wrong", "right"); | |||
| /// } | |||
| /// | |||
| /// // Save changes to Example.docx | |||
| /// dx.Save(); | |||
| /// </code> | |||
| /// </example> | |||
| /// <seealso cref="Paragraph.Remove"/> | |||
| /// <seealso cref="Paragraph.Insert"/> | |||
| /// <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> | |||
| public void Replace(string oldValue, string newValue, bool trackChanges, RegexOptions options) | |||
| { | |||
| MatchCollection mc = Regex.Matches(this.Value, oldValue, options); | |||
| // Loop through the matches in reverse order | |||
| foreach (Match m in mc.Cast<Match>().Reverse()) | |||
| { | |||
| Insert(m.Index + oldValue.Length, newValue, trackChanges); | |||
| Remove(m.Index, m.Length, trackChanges); | |||
| } | |||
| } | |||
| public void Replace(string oldValue, string newValue, bool trackChanges) | |||
| { | |||
| Replace(oldValue, newValue, trackChanges, RegexOptions.None); | |||
| } | |||
| } | |||
| } | |||
| @@ -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("Docx")] | |||
| [assembly: AssemblyDescription("")] | |||
| [assembly: AssemblyConfiguration("")] | |||
| [assembly: AssemblyCompany("Microsoft")] | |||
| [assembly: AssemblyProduct("Docx")] | |||
| [assembly: AssemblyCopyright("Copyright © Microsoft 2008")] | |||
| [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("16123f21-f3d1-47bb-ae9a-eb7c82c0f3c8")] | |||
| // 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")] | |||
| @@ -0,0 +1,138 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using System.Xml.Linq; | |||
| namespace Novacode | |||
| { | |||
| public class Run | |||
| { | |||
| // A lookup for the text elements in this paragraph | |||
| Dictionary<int, Text> textLookup = new Dictionary<int, Text>(); | |||
| private int startIndex; | |||
| private int endIndex; | |||
| private string text; | |||
| private XElement e; | |||
| /// <summary> | |||
| /// Gets the start index of this Text (text length before this text) | |||
| /// </summary> | |||
| public int StartIndex { get { return startIndex; } } | |||
| /// <summary> | |||
| /// Gets the end index of this Text (text length before this text + this texts length) | |||
| /// </summary> | |||
| public int EndIndex { get { return endIndex; } } | |||
| /// <summary> | |||
| /// The text value of this text element | |||
| /// </summary> | |||
| private string Value { set { value = text; } get { return text; } } | |||
| /// <summary> | |||
| /// The underlying XElement of this run | |||
| /// </summary> | |||
| public XElement Xml { get { return e; } } | |||
| /// <summary> | |||
| /// A Run can contain text, delText, br, t and rPr elements | |||
| /// </summary> | |||
| /// <param name="startIndex">The start index of this run (text length before this run)</param> | |||
| /// <param name="endIndex">The end index of this run (text length before this run, plus this runs text length)</param> | |||
| /// <param name="runText">The text value of this run</param> | |||
| /// <param name="e">The underlying XElement of this run</param> | |||
| public Run(int startIndex, XElement e) | |||
| { | |||
| this.startIndex = startIndex; | |||
| this.e = e; | |||
| // Get the text elements in this run | |||
| IEnumerable<XElement> texts = e.Descendants(); | |||
| int start = startIndex; | |||
| // Loop through each text in this run | |||
| foreach (XElement text in texts) | |||
| { | |||
| switch (text.Name.LocalName) | |||
| { | |||
| case "tab": | |||
| { | |||
| textLookup.Add(start + 1, new Text(start, text)); | |||
| Value += "\t"; | |||
| start++; | |||
| break; | |||
| } | |||
| case "br": | |||
| { | |||
| textLookup.Add(start + 1, new Text(start, text)); | |||
| Value += "\n"; | |||
| start++; | |||
| break; | |||
| } | |||
| case "t": goto case "delText"; | |||
| case "delText": | |||
| { | |||
| textLookup.Add(start + text.Value.Length, new Text(start, text)); | |||
| Value += text.Value; | |||
| start += text.Value.Length; | |||
| break; | |||
| } | |||
| default: break; | |||
| } | |||
| } | |||
| endIndex = start; | |||
| } | |||
| /// <summary> | |||
| /// Splits a run element at a specified index | |||
| /// </summary> | |||
| /// <param name="r">The run element to split</param> | |||
| /// <param name="index">The index to split at</param> | |||
| /// <returns>A two element array which contains both sides of the split</returns> | |||
| public static XElement[] SplitRun(Run r, int index) | |||
| { | |||
| Text t = r.GetFirstTextEffectedByEdit(index); | |||
| XElement[] splitText = Text.SplitText(t, index); | |||
| XElement splitLeft = new XElement(r.e.Name, r.e.Attributes(), r.Xml.Element(XName.Get("rPr", DocX.w.NamespaceName)), t.Xml.ElementsBeforeSelf().Where(n => n.Name.LocalName != "rPr"), splitText[0]); | |||
| if(Paragraph.GetElementTextLength(splitLeft) == 0) | |||
| splitLeft = null; | |||
| XElement splitRight = new XElement(r.e.Name, r.e.Attributes(), r.Xml.Element(XName.Get("rPr", DocX.w.NamespaceName)), splitText[1], t.Xml.ElementsAfterSelf().Where(n => n.Name.LocalName != "rPr")); | |||
| if(Paragraph.GetElementTextLength(splitRight) == 0) | |||
| splitRight = null; | |||
| return | |||
| ( | |||
| new XElement[] | |||
| { | |||
| splitLeft, | |||
| splitRight | |||
| } | |||
| ); | |||
| } | |||
| /// <summary> | |||
| /// Return the first Run that will be effected by an edit at the index | |||
| /// </summary> | |||
| /// <param name="index">The index of this edit</param> | |||
| /// <returns>The first Run that will be effected</returns> | |||
| public Text GetFirstTextEffectedByEdit(int index) | |||
| { | |||
| foreach (int textEndIndex in textLookup.Keys) | |||
| { | |||
| if (textEndIndex > index) | |||
| return textLookup[textEndIndex]; | |||
| } | |||
| if (textLookup.Last().Value.EndIndex == index) | |||
| return textLookup.Last().Value; | |||
| throw new ArgumentOutOfRangeException(); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,160 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using System.Xml.Linq; | |||
| namespace Novacode | |||
| { | |||
| public class Text | |||
| { | |||
| private int startIndex; | |||
| private int endIndex; | |||
| private string text; | |||
| private XElement e; | |||
| /// <summary> | |||
| /// Gets the start index of this Text (text length before this text) | |||
| /// </summary> | |||
| public int StartIndex { get { return startIndex; } } | |||
| /// <summary> | |||
| /// Gets the end index of this Text (text length before this text + this texts length) | |||
| /// </summary> | |||
| public int EndIndex { get { return endIndex; } } | |||
| /// <summary> | |||
| /// The text value of this text element | |||
| /// </summary> | |||
| public string Value { get { return text; } } | |||
| /// <summary> | |||
| /// The underlying XElement of this run | |||
| /// </summary> | |||
| public XElement Xml { get { return e; } } | |||
| /// <summary> | |||
| /// A Text element | |||
| /// </summary> | |||
| /// <param name="startIndex">The index this text starts at</param> | |||
| /// <param name="text">The index this text ends at</param> | |||
| /// <param name="e">The underlying xml element that this text wraps</param> | |||
| public Text(int startIndex, XElement e) | |||
| { | |||
| this.startIndex = startIndex; | |||
| this.e = e; | |||
| switch (e.Name.LocalName) | |||
| { | |||
| case "t": | |||
| { | |||
| goto case "delText"; | |||
| } | |||
| case "delText": | |||
| { | |||
| endIndex = startIndex + e.Value.Length; | |||
| text = e.Value; | |||
| break; | |||
| } | |||
| case "br": | |||
| { | |||
| text = "\n"; | |||
| endIndex = startIndex + 1; | |||
| break; | |||
| } | |||
| case "tab": | |||
| { | |||
| text = "\t"; | |||
| endIndex = startIndex + 1; | |||
| break; | |||
| } | |||
| default: | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// Splits a text element at a specified index | |||
| /// </summary> | |||
| /// <param name="e">The text element to split</param> | |||
| /// <param name="index">The index to split at</param> | |||
| /// <returns>A two element array which contains both sides of the split</returns> | |||
| public static XElement[] SplitText(Text t, int index) | |||
| { | |||
| if (index < t.startIndex || index > t.EndIndex) | |||
| throw new ArgumentOutOfRangeException("index"); | |||
| XElement splitLeft = null, splitRight = null; | |||
| if (t.e.Name.LocalName == "t" || t.e.Name.LocalName == "delText") | |||
| { | |||
| // The origional text element, now containing only the text before the index point. | |||
| splitLeft = new XElement(t.e.Name, t.e.Attributes(), t.e.Value.Substring(0, index - t.startIndex)); | |||
| if (splitLeft.Value.Length == 0) | |||
| splitLeft = null; | |||
| else | |||
| PreserveSpace(splitLeft); | |||
| // The origional text element, now containing only the text after the index point. | |||
| splitRight = new XElement(t.e.Name, t.e.Attributes(), t.e.Value.Substring(index - t.startIndex, t.e.Value.Length - (index - t.startIndex))); | |||
| if (splitRight.Value.Length == 0) | |||
| splitRight = null; | |||
| else | |||
| PreserveSpace(splitRight); | |||
| } | |||
| else | |||
| { | |||
| if (index == t.StartIndex) | |||
| splitLeft = t.e; | |||
| else | |||
| splitRight = t.e; | |||
| } | |||
| return | |||
| ( | |||
| new XElement[] | |||
| { | |||
| splitLeft, | |||
| splitRight | |||
| } | |||
| ); | |||
| } | |||
| /// <summary> | |||
| /// If a text element or delText element, starts or ends with a space, | |||
| /// it must have the attribute space, otherwise it must not have it. | |||
| /// </summary> | |||
| /// <param name="e">The (t or delText) element check</param> | |||
| public static void PreserveSpace(XElement e) | |||
| { | |||
| // PreserveSpace should only be used on (t or delText) elements | |||
| if (!e.Name.Equals(DocX.w + "t") && !e.Name.Equals(DocX.w + "delText")) | |||
| throw new ArgumentException("SplitText can only split elements of type t or delText", "e"); | |||
| // Check if this w:t contains a space atribute | |||
| XAttribute space = e.Attributes().Where(a => a.Name.Equals(XNamespace.Xml + "space")).SingleOrDefault(); | |||
| // This w:t's text begins or ends with whitespace | |||
| if (e.Value.StartsWith(" ") || e.Value.EndsWith(" ")) | |||
| { | |||
| // If this w:t contains no space attribute, add one. | |||
| if (space == null) | |||
| e.Add(new XAttribute(XNamespace.Xml + "space", "preserve")); | |||
| } | |||
| // This w:t's text does not begin or end with a space | |||
| else | |||
| { | |||
| // If this w:r contains a space attribute, remove it. | |||
| if (space != null) | |||
| space.Remove(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <TestRunConfiguration name="Local Test Run" id="6869d4b0-c7c0-449b-a3b2-541bd9ae1155" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2006"> | |||
| <Description>This is a default test run configuration for a local test run.</Description> | |||
| <TestTypeSpecific> | |||
| <WebTestRunConfiguration testTypeId="4e7599fa-5ecb-43e9-a887-cd63cf72d207"> | |||
| <Browser name="Internet Explorer 7.0"> | |||
| <Headers> | |||
| <Header name="User-Agent" value="Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)" /> | |||
| <Header name="Accept" value="*/*" /> | |||
| <Header name="Accept-Language" value="{{$IEAcceptLanguage}}" /> | |||
| <Header name="Accept-Encoding" value="GZIP" /> | |||
| </Headers> | |||
| </Browser> | |||
| <Network Name="LAN" BandwidthInKbps="0" /> | |||
| </WebTestRunConfiguration> | |||
| </TestTypeSpecific> | |||
| </TestRunConfiguration> | |||
| @@ -0,0 +1,37 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using Novacode; | |||
| using System.Text.RegularExpressions; | |||
| using System.IO; | |||
| namespace StringReplaceTestApp | |||
| { | |||
| class Program | |||
| { | |||
| static void Main(string[] args) | |||
| { | |||
| File.Copy(@"Test.docx", "Manipulated.docx", true); | |||
| // Load the document that you want to manipulate | |||
| DocX document = DocX.Load(@"Manipulated.docx"); | |||
| // Loop through the paragraphs in the document | |||
| foreach (Paragraph p in document.Paragraphs) | |||
| { | |||
| /* | |||
| * Replace each instance of the string pear with the string banana. | |||
| * Specifying true as the third argument informs DocX to track the | |||
| * changes made by this replace. The fourth argument tells DocX to | |||
| * ignore case when matching the string pear. | |||
| */ | |||
| p.Replace("pear", "banana", true, RegexOptions.IgnoreCase); | |||
| } | |||
| // File will be saved to \StringReplaceTestApp\bin\Debug | |||
| document.Save(); | |||
| } | |||
| } | |||
| } | |||
| @@ -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("StringReplaceTestApp")] | |||
| [assembly: AssemblyDescription("")] | |||
| [assembly: AssemblyConfiguration("")] | |||
| [assembly: AssemblyCompany("Microsoft IT")] | |||
| [assembly: AssemblyProduct("StringReplaceTestApp")] | |||
| [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("3ec6b0e8-aceb-402b-ab0d-03201cc62865")] | |||
| // 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")] | |||
| @@ -0,0 +1,72 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
| <PropertyGroup> | |||
| <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||
| <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||
| <ProductVersion>9.0.30729</ProductVersion> | |||
| <SchemaVersion>2.0</SchemaVersion> | |||
| <ProjectGuid>{EA960F96-7CA4-4AA1-BB43-0478E3407C5C}</ProjectGuid> | |||
| <OutputType>Exe</OutputType> | |||
| <AppDesignerFolder>Properties</AppDesignerFolder> | |||
| <RootNamespace>StringReplaceTestApp</RootNamespace> | |||
| <AssemblyName>StringReplaceTestApp</AssemblyName> | |||
| <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> | |||
| <FileAlignment>512</FileAlignment> | |||
| <SccProjectName>SAK</SccProjectName> | |||
| <SccLocalPath>SAK</SccLocalPath> | |||
| <SccAuxPath>SAK</SccAuxPath> | |||
| <SccProvider>SAK</SccProvider> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | |||
| <DebugSymbols>true</DebugSymbols> | |||
| <DebugType>full</DebugType> | |||
| <Optimize>false</Optimize> | |||
| <OutputPath>bin\Debug\</OutputPath> | |||
| <DefineConstants>DEBUG;TRACE</DefineConstants> | |||
| <ErrorReport>prompt</ErrorReport> | |||
| <WarningLevel>4</WarningLevel> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | |||
| <DebugType>pdbonly</DebugType> | |||
| <Optimize>true</Optimize> | |||
| <OutputPath>bin\Release\</OutputPath> | |||
| <DefineConstants>TRACE</DefineConstants> | |||
| <ErrorReport>prompt</ErrorReport> | |||
| <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> | |||
| </Reference> | |||
| <Reference Include="System.Xml.Linq"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| </Reference> | |||
| <Reference Include="System.Data.DataSetExtensions"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| </Reference> | |||
| <Reference Include="System.Data" /> | |||
| <Reference Include="System.Xml" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <Compile Include="Program.cs" /> | |||
| <Compile Include="Properties\AssemblyInfo.cs" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <None Include="Test.docx"> | |||
| <CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
| </None> | |||
| </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. | |||
| <Target Name="BeforeBuild"> | |||
| </Target> | |||
| <Target Name="AfterBuild"> | |||
| </Target> | |||
| --> | |||
| </Project> | |||
| @@ -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" = "PROVIDER" | |||
| } | |||
| @@ -0,0 +1,136 @@ | |||
| ========================================================================== | |||
| Visual Studio Team System: Overview of Authoring and Running Tests | |||
| ========================================================================== | |||
| This overview describes the features for authoring and running tests in | |||
| Visual Studio Team System and Visual Studio Team Edition for Software Testers. | |||
| Opening Tests | |||
| ------------- | |||
| To open a test, open a test project or a test metadata file (a file with | |||
| extension .vsmdi) that contains the definition of the test. You can find | |||
| test projects and metadata files in Solution Explorer. | |||
| Viewing Tests | |||
| ------------- | |||
| To see which tests are available to you, open the Test View window. Or, | |||
| if you have installed Team Edition for Software Testers, you can also open | |||
| the Test List Editor window to view tests. | |||
| To open the Test View window, click the Test menu, point to Windows, and | |||
| then click Test View. To open the Test List Editor window (if you have | |||
| installed Team Edition for Software Testers), click Test, point to Windows, | |||
| and then click Test List Editor. | |||
| Running Tests | |||
| ------------- | |||
| You can run tests from the Test View window and the Test List Editor window. | |||
| See Viewing Tests to learn how to open these windows. To run one or more | |||
| tests displayed in the Test View window, first select the tests in that | |||
| window; to select multiple tests, hold either the Shift or CTRL key while | |||
| clicking tests. Then click the Run Tests button in the Test View window | |||
| toolbar. | |||
| If you have installed Visual Studio Team Edition for Software Testers, you can | |||
| also use the Test List Editor window to run tests. To run tests in Test List Editor, | |||
| select the check box next to each test that you want to run. Then click the | |||
| Run Tests button in the Test List Editor window toolbar. | |||
| Viewing Test Results | |||
| -------------------- | |||
| When you run a test or a series of tests, the results of the test run will be | |||
| shown in the Test Results window. Each individual test in the run is shown on | |||
| a separate line so that you can see its status. The window contains an | |||
| embedded status bar in the top half of the window that provides you with | |||
| summary details of the complete test run. | |||
| To see more detailed results for a particular test result, double-click it in | |||
| the Test Results window. This opens a window that provides more information | |||
| about the particular test result, such as any specific error messages returned | |||
| by the test. | |||
| Changing the way that tests are run | |||
| ----------------------------------- | |||
| Each time you run one or more tests, a collection of settings is used to | |||
| determine how those tests are run. These settings are contained in a “test | |||
| run configuration” file. | |||
| Here is a partial list of the changes you can make with a test run | |||
| configuration file: | |||
| - Change the naming scheme for each test run. | |||
| - Change the test controller that the tests are run on so that you can run | |||
| tests remotely. | |||
| - Gather code coverage data for the code being tested so that you can see | |||
| which lines of code are covered by your tests. | |||
| - Enable and disable test deployment. | |||
| - Specify additional files to deploy before tests are run. | |||
| - Select a different host, ASP.NET, for running ASP.NET unit tests. | |||
| - Select a different host, the smart device test host, for running smart device unit tests. | |||
| - Set various properties for the test agents that run your tests. | |||
| - Run custom scripts at the start and end of each test run so that you can | |||
| set up the test environment exactly as required each time tests are run. | |||
| - Set time limits for tests and test runs. | |||
| - Set the browser mix and the number of times to repeat Web tests in the | |||
| test run. | |||
| By default, a test run configuration file is created whenever you create a | |||
| new test project. You make changes to this file by double-clicking it in | |||
| Solution Explorer and then changing its settings. (Test run configuration | |||
| files have the extension .testrunconfig.) | |||
| A solution can contain multiple test run configuration files. Only one of | |||
| those files, known as the “Active” test run configuration file, is used to | |||
| determine the settings that are currently used for test runs. You select | |||
| the active test run configuration by clicking Select Active Test Run | |||
| Configuration on the Test menu. | |||
| ------------------------------------------------------------------------------- | |||
| Test Types | |||
| ---------- | |||
| Using Visual Studio Team Edition for Software Testers, you can create a number | |||
| of different test types: | |||
| Unit test: Use a unit test to create a programmatic test in C++, Visual C# or | |||
| Visual Basic that exercises source code. A unit test calls the methods of a | |||
| class, passing suitable parameters, and verifies that the returned value is | |||
| what you expect. | |||
| There are three specialized variants of unit tests: | |||
| - Data-driven unit tests are created when you configure a unit test to be | |||
| called repeatedly for each row of a data source. The data from each row | |||
| is used by the unit test as input data. | |||
| - ASP.NET unit tests are unit tests that exercise code in an ASP.NET Web | |||
| application. | |||
| - Smart device unit tests are unit tests that are deployed to a smart device | |||
| or emulator and then executed by the smart device test host. | |||
| Web Test: Web tests consist of an ordered series of HTTP requests that you | |||
| record in a browser session using Microsoft Internet Explorer. You can have | |||
| the test report specific details about the pages or sites it requests, such | |||
| as whether a particular page contains a specified string. | |||
| Load Test: You use a load test to encapsulate non-manual tests, such as | |||
| unit, Web, and generic tests, and then run them simultaneously by using | |||
| virtual users. Running these tests under load generates test results, | |||
| including performance and other counters, in tables and in graphs. | |||
| Generic test: A generic test is an existing program wrapped to function as a | |||
| test in Visual Studio. The following are examples of tests or programs that | |||
| you can turn into generic tests: | |||
| - An existing test that uses process exit codes to communicate whether the | |||
| test passed or failed. 0 indicates passing and any other value indicates | |||
| a failure. | |||
| - A general program to obtain specific functionality during a test scenario. | |||
| - A test or program that uses a special XML file (called a “summary results | |||
| file”), to communicate detailed results. | |||
| Manual test: The manual test type is used when the test tasks are to be | |||
| completed by a test engineer as opposed to an automated script. | |||
| Ordered test: Use an ordered test to execute a set of tests in an order you | |||
| specify. | |||
| ------------------------------------------------------------------------------- | |||
| @@ -0,0 +1,155 @@ | |||
| using System; | |||
| using System.Text; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | |||
| using Novacode; | |||
| using System.Xml.Linq; | |||
| namespace UnitTests | |||
| { | |||
| /// <summary> | |||
| /// Summary description for PreserveSpaceCheckTests | |||
| /// </summary> | |||
| [TestClass] | |||
| public class PreserveSpaceTests | |||
| { | |||
| public PreserveSpaceTests() | |||
| { | |||
| // | |||
| // TODO: Add constructor logic here | |||
| // | |||
| } | |||
| private TestContext testContextInstance; | |||
| /// <summary> | |||
| ///Gets or sets the test context which provides | |||
| ///information about and functionality for the current test run. | |||
| ///</summary> | |||
| public TestContext TestContext | |||
| { | |||
| get | |||
| { | |||
| return testContextInstance; | |||
| } | |||
| set | |||
| { | |||
| testContextInstance = value; | |||
| } | |||
| } | |||
| #region Additional test attributes | |||
| // | |||
| // You can use the following additional attributes as you write your tests: | |||
| // | |||
| // Use ClassInitialize to run code before running the first test in the class | |||
| // [ClassInitialize()] | |||
| // public static void MyClassInitialize(TestContext testContext) { } | |||
| // | |||
| // Use ClassCleanup to run code after all tests in a class have run | |||
| // [ClassCleanup()] | |||
| // public static void MyClassCleanup() { } | |||
| // | |||
| // Use TestInitialize to run code before running each test | |||
| // [TestInitialize()] | |||
| // public void MyTestInitialize() { } | |||
| // | |||
| // Use TestCleanup to run code after each test has run | |||
| // [TestCleanup()] | |||
| // public void MyTestCleanup() { } | |||
| // | |||
| #endregion | |||
| [TestMethod] | |||
| public void TestPreserveSpace_NoChangeRequired() | |||
| { | |||
| XElement t_origional, t_afterPreserveSpace; | |||
| #region Doesn't start or end with a space | |||
| // The test text element to check | |||
| t_origional = new XElement(DocX.w + "t", "Hello I am a string"); | |||
| t_afterPreserveSpace = t_origional; | |||
| Text.PreserveSpace(t_afterPreserveSpace); | |||
| Assert.AreEqual(t_origional.ToString(), t_afterPreserveSpace.ToString()); | |||
| #endregion | |||
| #region Starts with a space, doesn't end with a space | |||
| // The test text element to check | |||
| t_origional = new XElement(DocX.w + "t", new XAttribute(XNamespace.Xml + "space", "preserve"), " Hello I am a string"); | |||
| t_afterPreserveSpace = t_origional; | |||
| Text.PreserveSpace(t_afterPreserveSpace); | |||
| Assert.AreEqual(t_origional.ToString(), t_afterPreserveSpace.ToString()); | |||
| #endregion | |||
| #region Ends with a space, doesn't start with a space | |||
| // The test text element to check | |||
| t_origional = new XElement(DocX.w + "t", new XAttribute(XNamespace.Xml + "space", "preserve"), "Hello I am a string "); | |||
| t_afterPreserveSpace = t_origional; | |||
| Text.PreserveSpace(t_afterPreserveSpace); | |||
| Assert.AreEqual(t_origional.ToString(), t_afterPreserveSpace.ToString()); | |||
| #endregion | |||
| #region Starts and ends with a space | |||
| // The test text element to check | |||
| t_origional = new XElement(DocX.w + "t", new XAttribute(XNamespace.Xml + "space", "preserve"), " Hello I am a string "); | |||
| t_afterPreserveSpace = t_origional; | |||
| Text.PreserveSpace(t_afterPreserveSpace); | |||
| Assert.AreEqual(t_origional.ToString(), t_afterPreserveSpace.ToString()); | |||
| #endregion | |||
| } | |||
| [TestMethod] | |||
| public void TestPreserveSpace_ChangeRequired() | |||
| { | |||
| XElement t_origional, t_afterPreserveSpace; | |||
| #region Doesn't start or end with a space, but has a space preserve attribute | |||
| // The test text element to check | |||
| t_origional = new XElement(DocX.w + "t", new XAttribute(XNamespace.Xml + "space", "preserve"), "Hello I am a string"); | |||
| t_afterPreserveSpace = t_origional; | |||
| Text.PreserveSpace(t_afterPreserveSpace); | |||
| Assert.AreEqual((new XElement(DocX.w + "t", "Hello I am a string")).ToString(), t_afterPreserveSpace.ToString()); | |||
| #endregion | |||
| #region Start with a space, but has no space preserve attribute | |||
| // The test text element to check | |||
| t_origional = new XElement(DocX.w + "t", " Hello I am a string"); | |||
| t_afterPreserveSpace = t_origional; | |||
| Text.PreserveSpace(t_afterPreserveSpace); | |||
| Assert.AreEqual((new XElement(DocX.w + "t", new XAttribute(XNamespace.Xml + "space", "preserve"), " Hello I am a string")).ToString(), t_afterPreserveSpace.ToString()); | |||
| #endregion | |||
| #region Ends with a space, but has no space preserve attribute | |||
| // The test text element to check | |||
| t_origional = new XElement(DocX.w + "t", "Hello I am a string "); | |||
| t_afterPreserveSpace = t_origional; | |||
| Text.PreserveSpace(t_afterPreserveSpace); | |||
| Assert.AreEqual((new XElement(DocX.w + "t", new XAttribute(XNamespace.Xml + "space", "preserve"), "Hello I am a string ")).ToString(), t_afterPreserveSpace.ToString()); | |||
| #endregion | |||
| #region Starts and ends with a space, but has no space preserve attribute | |||
| // The test text element to check | |||
| t_origional = new XElement(DocX.w + "t", " Hello I am a string "); | |||
| t_afterPreserveSpace = t_origional; | |||
| Text.PreserveSpace(t_afterPreserveSpace); | |||
| Assert.AreEqual((new XElement(DocX.w + "t", new XAttribute(XNamespace.Xml + "space", "preserve"), " Hello I am a string ")).ToString(), t_afterPreserveSpace.ToString()); | |||
| #endregion | |||
| } | |||
| [TestMethod, ExpectedException(typeof(ArgumentException))] | |||
| public void TestPreserveSpace_NotTOrDelTextElement() | |||
| { | |||
| XElement e = new XElement("NotTOrDelText"); | |||
| Text.PreserveSpace(e); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,35 @@ | |||
| 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("UnitTests")] | |||
| [assembly: AssemblyDescription("")] | |||
| [assembly: AssemblyConfiguration("")] | |||
| [assembly: AssemblyCompany("Microsoft IT")] | |||
| [assembly: AssemblyProduct("UnitTests")] | |||
| [assembly: AssemblyCopyright("Copyright © Microsoft IT 2009")] | |||
| [assembly: AssemblyTrademark("")] | |||
| [assembly: AssemblyCulture("")] | |||
| // Setting ComVisible to false makes the types in this assembly not visible | |||
| // to COM componenets. 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("ccefa1fd-d3c3-4130-b7ff-a6962dfc1a98")] | |||
| // 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 Revision and Build Numbers | |||
| // by using the '*' as shown below: | |||
| [assembly: AssemblyVersion("1.0.0.0")] | |||
| [assembly: AssemblyFileVersion("1.0.0.0")] | |||
| @@ -0,0 +1,192 @@ | |||
| using System; | |||
| using System.Text; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | |||
| using System.Xml.Linq; | |||
| using Novacode; | |||
| namespace UnitTests | |||
| { | |||
| /// <summary> | |||
| /// Summary description for SplitEditTests | |||
| /// </summary> | |||
| [TestClass] | |||
| public class SplitEditTests | |||
| { | |||
| public SplitEditTests() | |||
| { | |||
| // | |||
| // TODO: Add constructor logic here | |||
| // | |||
| } | |||
| private TestContext testContextInstance; | |||
| /// <summary> | |||
| ///Gets or sets the test context which provides | |||
| ///information about and functionality for the current test run. | |||
| ///</summary> | |||
| public TestContext TestContext | |||
| { | |||
| get | |||
| { | |||
| return testContextInstance; | |||
| } | |||
| set | |||
| { | |||
| testContextInstance = value; | |||
| } | |||
| } | |||
| #region Additional test attributes | |||
| // | |||
| // You can use the following additional attributes as you write your tests: | |||
| // | |||
| // Use ClassInitialize to run code before running the first test in the class | |||
| // [ClassInitialize()] | |||
| // public static void MyClassInitialize(TestContext testContext) { } | |||
| // | |||
| // Use ClassCleanup to run code after all tests in a class have run | |||
| // [ClassCleanup()] | |||
| // public static void MyClassCleanup() { } | |||
| // | |||
| // Use TestInitialize to run code before running each test | |||
| // [TestInitialize()] | |||
| // public void MyTestInitialize() { } | |||
| // | |||
| // Use TestCleanup to run code after each test has run | |||
| // [TestCleanup()] | |||
| // public void MyTestCleanup() { } | |||
| // | |||
| #endregion | |||
| [TestMethod] | |||
| public void TestSplitEdit() | |||
| { | |||
| // The test text element to split | |||
| XElement run1 = new XElement(DocX.w + "r", new XElement(DocX.w + "rPr", new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0"))), new XElement(DocX.w + "t", "Hello")); | |||
| XElement run2 = new XElement(DocX.w + "r", new XElement(DocX.w + "rPr", new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0"))), new XElement(DocX.w + "t", new XAttribute(XNamespace.Xml + "space", "preserve"), " world")); | |||
| XElement edit = new XElement(DocX.w + "ins", new XAttribute(DocX.w + "id", "0"), new XAttribute(DocX.w + "author", "t-cathco"), new XAttribute(DocX.w + "date", "2009-02-17T21:09:00Z"), run1, run2); | |||
| Paragraph p = new Paragraph(edit); | |||
| #region Split at index 0 | |||
| XElement[] splitEdit = p.SplitEdit(edit, 0, EditType.del); | |||
| Assert.IsNull(splitEdit[0]); | |||
| Assert.AreEqual(edit.ToString(), splitEdit[1].ToString()); | |||
| #endregion | |||
| #region Split at index 1 | |||
| /* | |||
| * Split the text at index 5. | |||
| * This will cause the left side of the split to end with a space and the right to start with a space. | |||
| */ | |||
| XElement[] splitEdit_indexOne = p.SplitEdit(edit, 1, EditType.del); | |||
| // The result I expect to get from splitRun_nearMiddle | |||
| XElement splitEdit_indexOne_left = new XElement(DocX.w + "ins", new XAttribute(DocX.w + "id", "0"), new XAttribute(DocX.w + "author", "t-cathco"), new XAttribute(DocX.w + "date", "2009-02-17T21:09:00Z"), new XElement(DocX.w + "r", new XElement(DocX.w + "rPr", new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0"))), new XElement(DocX.w + "t", "H"))); | |||
| XElement splitEdit_indexOne_right = new XElement(DocX.w + "ins", new XAttribute(DocX.w + "id", "0"), new XAttribute(DocX.w + "author", "t-cathco"), new XAttribute(DocX.w + "date", "2009-02-17T21:09:00Z"), new XElement(DocX.w + "r",new XElement(DocX.w + "rPr", new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0"))), new XElement(DocX.w + "t", "ello")), new XElement(DocX.w + "r", new XElement(DocX.w + "rPr", new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0"))), new XElement(DocX.w + "t", new XAttribute(XNamespace.Xml + "space", "preserve"), " world"))); | |||
| // Check if my expectations have been met | |||
| Assert.AreEqual(splitEdit_indexOne_left.ToString(), splitEdit_indexOne[0].ToString()); | |||
| Assert.AreEqual(splitEdit_indexOne_right.ToString(), splitEdit_indexOne[1].ToString()); | |||
| #endregion | |||
| #region Split near the middle | |||
| /* | |||
| * Split the text at index 5. | |||
| * This will cause the left side of the split to end with a space and the right to start with a space. | |||
| */ | |||
| XElement[] splitEdit_nearMiddle = p.SplitEdit(edit, 5, EditType.del); | |||
| // The result I expect to get from splitRun_nearMiddle | |||
| XElement splitEdit_nearMiddle_left = new XElement(DocX.w + "ins", new XAttribute(DocX.w + "id", "0"), new XAttribute(DocX.w + "author", "t-cathco"), new XAttribute(DocX.w + "date", "2009-02-17T21:09:00Z"), new XElement(DocX.w + "r", new XElement(DocX.w + "rPr", new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0"))), new XElement(DocX.w + "t", "Hello"))); | |||
| XElement splitEdit_nearMiddle_right = new XElement(DocX.w + "ins", new XAttribute(DocX.w + "id", "0"), new XAttribute(DocX.w + "author", "t-cathco"), new XAttribute(DocX.w + "date", "2009-02-17T21:09:00Z"), new XElement(DocX.w + "r", new XElement(DocX.w + "rPr", new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0"))), new XElement(DocX.w + "t", new XAttribute(XNamespace.Xml + "space", "preserve"), " world"))); | |||
| // Check if my expectations have been met | |||
| Assert.AreEqual(splitEdit_nearMiddle_left.ToString(), splitEdit_nearMiddle[0].ToString()); | |||
| Assert.AreEqual(splitEdit_nearMiddle_right.ToString(), splitEdit_nearMiddle[1].ToString()); | |||
| #endregion | |||
| #region Split at index Length - 1 | |||
| /* | |||
| * Split the text at index 5. | |||
| * This will cause the left side of the split to end with a space and the right to start with a space. | |||
| */ | |||
| XElement[] splitEdit_indexOneFromLength = p.SplitEdit(edit, Paragraph.GetElementTextLength(edit) - 1, EditType.del); | |||
| // The result I expect to get from splitRun_nearMiddle | |||
| XElement splitEdit_OneFromLength_left = new XElement(DocX.w + "ins", new XAttribute(DocX.w + "id", "0"), new XAttribute(DocX.w + "author", "t-cathco"), new XAttribute(DocX.w + "date", "2009-02-17T21:09:00Z"), new XElement(DocX.w + "r", new XElement(DocX.w + "rPr", new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0"))), new XElement(DocX.w + "t", "Hello")), new XElement(DocX.w + "r", new XElement(DocX.w + "rPr", new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0"))), new XElement(DocX.w + "t", new XAttribute(XNamespace.Xml + "space", "preserve"), " worl"))); | |||
| XElement splitEdit_OneFromLength_right = new XElement(DocX.w + "ins", new XAttribute(DocX.w + "id", "0"), new XAttribute(DocX.w + "author", "t-cathco"), new XAttribute(DocX.w + "date", "2009-02-17T21:09:00Z"), new XElement(DocX.w + "r", new XElement(DocX.w + "rPr", new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0"))), new XElement(DocX.w + "t", "d"))); | |||
| // Check if my expectations have been met | |||
| Assert.AreEqual(splitEdit_OneFromLength_left.ToString(), splitEdit_indexOneFromLength[0].ToString()); | |||
| Assert.AreEqual(splitEdit_OneFromLength_right.ToString(), splitEdit_indexOneFromLength[1].ToString()); | |||
| #endregion | |||
| #region Split at index Length | |||
| XElement[] splitEdit_indexZero = p.SplitEdit(edit, Paragraph.GetElementTextLength(edit), EditType.del); | |||
| Assert.AreEqual(edit.ToString(), splitEdit_indexZero[0].ToString()); | |||
| Assert.IsNull(splitEdit_indexZero[1]); | |||
| #endregion | |||
| } | |||
| [TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))] | |||
| public void TestSplitEdit_IndexLessThanTextStartIndex() | |||
| { | |||
| // The test text element to split | |||
| XElement run1 = new XElement(DocX.w + "r", new XElement(DocX.w + "rPr", new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0"))), new XElement(DocX.w + "t", "Hello")); | |||
| XElement run2 = new XElement(DocX.w + "r", new XElement(DocX.w + "rPr", new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0"))), new XElement(DocX.w + "t", new XAttribute(XNamespace.Xml + "space", "preserve"), " world")); | |||
| XElement edit = new XElement(DocX.w + "ins", new XAttribute(DocX.w + "id", "0"), new XAttribute(DocX.w + "author", "t-cathco"), new XAttribute(DocX.w + "date", "2009-02-17T21:09:00Z"), run1, run2); | |||
| Paragraph p = new Paragraph(edit); | |||
| /* | |||
| * Split r at a negative index. | |||
| * This will cause an argument out of range exception to be thrown. | |||
| */ | |||
| p.SplitEdit(edit, -1, EditType.del); | |||
| } | |||
| [TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))] | |||
| public void TestSplitEdit_IndexGreaterThanTextEndIndex() | |||
| { | |||
| // The test text element to split | |||
| XElement run1 = new XElement(DocX.w + "r", new object[] { new XElement(DocX.w + "rPr", new object[] { new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0")) }), new XElement(DocX.w + "t", "Hello") }); | |||
| XElement run2 = new XElement(DocX.w + "r", new object[] { new XElement(DocX.w + "rPr", new object[] { new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0")) }), new XElement(DocX.w + "t", new object[] { new XAttribute(XNamespace.Xml + "space", "preserve"), " world" }) }); | |||
| XElement edit = new XElement(DocX.w + "ins", new object[] { new XAttribute(DocX.w + "id", "0"), new XAttribute(DocX.w + "author", "t-cathco"), new XAttribute(DocX.w + "date", "2009-02-17T21:09:00Z"), run1, run2 }); | |||
| Paragraph p = new Paragraph(edit); | |||
| /* | |||
| * Split r at a negative index. | |||
| * This will cause an argument out of range exception to be thrown. | |||
| */ | |||
| p.SplitEdit(edit, Paragraph.GetElementTextLength(edit) + 1, EditType.del); | |||
| } | |||
| [TestMethod] | |||
| public void TestSplitEditOfLengthOne() | |||
| { | |||
| XElement edit = new XElement(DocX.w + "ins", new object[] { new XAttribute(DocX.w + "id", "0"), new XAttribute(DocX.w + "author", "t-cathco"), new XAttribute(DocX.w + "date", "2009-02-17T21:09:00Z"), new XElement(DocX.w + "r", new XElement(DocX.w + "tab"))}); | |||
| Paragraph p = new Paragraph(edit); | |||
| XElement[] splitEditOfLengthOne; | |||
| #region Split before | |||
| splitEditOfLengthOne = p.SplitEdit(edit, 0, EditType.del); | |||
| // Check if my expectations have been met | |||
| Assert.AreEqual(edit.ToString(), splitEditOfLengthOne[0].ToString()); | |||
| Assert.IsNull(splitEditOfLengthOne[1]); | |||
| #endregion | |||
| #region Split after | |||
| splitEditOfLengthOne = p.SplitEdit(edit, Paragraph.GetElementTextLength(edit), EditType.del); | |||
| // Check if my expectations have been met | |||
| Assert.IsNull(splitEditOfLengthOne[0]); | |||
| Assert.AreEqual(edit.ToString(), splitEditOfLengthOne[1].ToString()); | |||
| #endregion | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,185 @@ | |||
| using System; | |||
| using System.Text; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | |||
| using Novacode; | |||
| using System.Xml.Linq; | |||
| namespace UnitTests | |||
| { | |||
| /// <summary> | |||
| /// Summary description for SplitRunTests | |||
| /// </summary> | |||
| [TestClass] | |||
| public class SplitRunTests | |||
| { | |||
| public SplitRunTests() | |||
| { | |||
| // | |||
| // TODO: Add constructor logic here | |||
| // | |||
| } | |||
| private TestContext testContextInstance; | |||
| /// <summary> | |||
| ///Gets or sets the test context which provides | |||
| ///information about and functionality for the current test run. | |||
| ///</summary> | |||
| public TestContext TestContext | |||
| { | |||
| get | |||
| { | |||
| return testContextInstance; | |||
| } | |||
| set | |||
| { | |||
| testContextInstance = value; | |||
| } | |||
| } | |||
| #region Additional test attributes | |||
| // | |||
| // You can use the following additional attributes as you write your tests: | |||
| // | |||
| // Use ClassInitialize to run code before running the first test in the class | |||
| // [ClassInitialize()] | |||
| // public static void MyClassInitialize(TestContext testContext) { } | |||
| // | |||
| // Use ClassCleanup to run code after all tests in a class have run | |||
| // [ClassCleanup()] | |||
| // public static void MyClassCleanup() { } | |||
| // | |||
| // Use TestInitialize to run code before running each test | |||
| // [TestInitialize()] | |||
| // public void MyTestInitialize() { } | |||
| // | |||
| // Use TestCleanup to run code after each test has run | |||
| // [TestCleanup()] | |||
| // public void MyTestCleanup() { } | |||
| // | |||
| #endregion | |||
| [TestMethod] | |||
| public void TestSplitRun() | |||
| { | |||
| // The test text element to split | |||
| Run r = new Run(0, new XElement(DocX.w + "r", new object[] { new XElement(DocX.w + "rPr", new object[] { new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0")) }), new XElement(DocX.w + "t", "Hello world") })); | |||
| #region Split at index 0 | |||
| /* | |||
| * Split r at index 0. | |||
| * This will cause the left side of the split to be null and the right side to be equal to r. | |||
| */ | |||
| XElement[] splitRun_indexZero = Run.SplitRun(r, 0); | |||
| // Check if my expectations have been met | |||
| Assert.IsNull(splitRun_indexZero[0]); | |||
| Assert.AreEqual(r.Xml.ToString(), splitRun_indexZero[1].ToString()); | |||
| #endregion | |||
| #region Split at index 1 | |||
| XElement[] splitRun_indexOne = Run.SplitRun(r, 1); | |||
| // The result I expect to get from splitRun_indexOne | |||
| XElement splitRun_indexOne_left = new XElement(DocX.w + "r", new object[] { new XElement(DocX.w + "rPr", new object[] { new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0")) }), new XElement(DocX.w + "t", "H") }); | |||
| XElement splitRun_indexOne_right = new XElement(DocX.w + "r", new object[] { new XElement(DocX.w + "rPr", new object[] { new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0")) }), new XElement(DocX.w + "t", "ello world") }); | |||
| // Check if my expectations have been met | |||
| Assert.AreEqual(splitRun_indexOne_left.ToString(), splitRun_indexOne[0].ToString()); | |||
| Assert.AreEqual(splitRun_indexOne_right.ToString(), splitRun_indexOne[1].ToString()); | |||
| #endregion | |||
| #region Split near the middle | |||
| /* | |||
| * Split the text at index 11. | |||
| * This will cause the left side of the split to end with a space and the right to start with a space. | |||
| */ | |||
| XElement[] splitRun_nearMiddle = Run.SplitRun(r, 5); | |||
| // The result I expect to get from splitRun_nearMiddle | |||
| XElement splitRun_nearMiddle_left = new XElement(DocX.w + "r", new object[] { new XElement(DocX.w + "rPr", new object[] { new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0")) }), new XElement(DocX.w + "t", "Hello") }); | |||
| XElement splitRun_nearMiddle_right = new XElement(DocX.w + "r", new object[] { new XElement(DocX.w + "rPr", new object[] { new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0")) }), new XElement(DocX.w + "t", new object[] { new XAttribute(XNamespace.Xml + "space", "preserve"), " world" }) }); | |||
| // Check if my expectations have been met | |||
| Assert.AreEqual(splitRun_nearMiddle_left.ToString(), splitRun_nearMiddle[0].ToString()); | |||
| Assert.AreEqual(splitRun_nearMiddle_right.ToString(), splitRun_nearMiddle[1].ToString()); | |||
| #endregion | |||
| #region Split at index Length - 1 | |||
| XElement[] splitRun_indexOneFromLength = Run.SplitRun(r, Paragraph.GetElementTextLength(r.Xml) - 1); | |||
| // The result I expect to get from splitRun_indexOne | |||
| XElement splitRun_indexOneFromLength_left = new XElement(DocX.w + "r", new object[] { new XElement(DocX.w + "rPr", new object[] { new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0")) }), new XElement(DocX.w + "t", "Hello worl") }); | |||
| XElement splitRun_indexOneFromLength_right = new XElement(DocX.w + "r", new object[] { new XElement(DocX.w + "rPr", new object[] { new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0")) }), new XElement(DocX.w + "t", "d") }); | |||
| // Check if my expectations have been met | |||
| Assert.AreEqual(splitRun_indexOneFromLength_left.ToString(), splitRun_indexOneFromLength[0].ToString()); | |||
| Assert.AreEqual(splitRun_indexOneFromLength_right.ToString(), splitRun_indexOneFromLength[1].ToString()); | |||
| #endregion | |||
| #region Split at index Length | |||
| /* | |||
| * Split r at index Length. | |||
| * This will cause the left side of the split to equal to r and the right side to be null. | |||
| */ | |||
| XElement[] splitRun_indexLength = Run.SplitRun(r, Paragraph.GetElementTextLength(r.Xml)); | |||
| // Check if my expectations have been met | |||
| Assert.AreEqual(r.Xml.ToString(), splitRun_indexLength[0].ToString()); | |||
| Assert.IsNull(splitRun_indexLength[1]); | |||
| #endregion | |||
| } | |||
| [TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))] | |||
| public void TestSplitRun_IndexLessThanTextStartIndex() | |||
| { | |||
| // The test text element to split | |||
| Run r = new Run(0, new XElement(DocX.w + "r", new object[] { new XElement(DocX.w + "rPr", new object[] { new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0")) }), new XElement(DocX.w + "t", "Hello world") })); | |||
| /* | |||
| * Split r at a negative index. | |||
| * This will cause an argument out of range exception to be thrown. | |||
| */ | |||
| Run.SplitRun(r, r.StartIndex - 1); | |||
| } | |||
| [TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))] | |||
| public void TestSplitRun_IndexGreaterThanTextEndIndex() | |||
| { | |||
| // The test text element to split | |||
| Run r = new Run(0, new XElement(DocX.w + "r", new object[] { new XElement(DocX.w + "rPr", new object[] { new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0")) }), new XElement(DocX.w + "t", "Hello world") })); | |||
| /* | |||
| * Split r at a length + 1. | |||
| * This will cause an argument out of range exception to be thrown. | |||
| */ | |||
| Run.SplitRun(r, r.EndIndex + 1); | |||
| } | |||
| [TestMethod] | |||
| public void TestSplitRunOfLengthOne() | |||
| { | |||
| // The test text element to split | |||
| Run r = new Run(0, new XElement(DocX.w + "r", new object[] { new XElement(DocX.w + "rPr", new object[] { new XElement(DocX.w + "b"), new XElement(DocX.w + "i"), new XElement(DocX.w + "color", new XAttribute(DocX.w + "val", "7030A0")) }), new XElement(DocX.w + "br") })); | |||
| XElement[] splitRunOfLengthOne; | |||
| #region Split before | |||
| splitRunOfLengthOne = Run.SplitRun(r, r.StartIndex); | |||
| // Check if my expectations have been met | |||
| Assert.AreEqual(r.Xml.ToString(), splitRunOfLengthOne[0].ToString()); | |||
| Assert.IsNull(splitRunOfLengthOne[1]); | |||
| #endregion | |||
| #region Split after | |||
| splitRunOfLengthOne = Run.SplitRun(r, r.EndIndex); | |||
| // Check if my expectations have been met | |||
| Assert.IsNull(splitRunOfLengthOne[0]); | |||
| Assert.AreEqual(r.Xml.ToString(), splitRunOfLengthOne[1].ToString()); | |||
| #endregion | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,183 @@ | |||
| using System; | |||
| using System.Text; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | |||
| using Novacode; | |||
| using System.Xml.Linq; | |||
| namespace UnitTests | |||
| { | |||
| /// <summary> | |||
| /// This class tests the SplitText function of Novacode.Paragraph | |||
| /// </summary> | |||
| [TestClass] | |||
| public class SplitTextTests | |||
| { | |||
| public SplitTextTests() | |||
| { | |||
| // | |||
| // TODO: Add constructor logic here | |||
| // | |||
| } | |||
| private TestContext testContextInstance; | |||
| /// <summary> | |||
| ///Gets or sets the test context which provides | |||
| ///information about and functionality for the current test run. | |||
| ///</summary> | |||
| public TestContext TestContext | |||
| { | |||
| get | |||
| { | |||
| return testContextInstance; | |||
| } | |||
| set | |||
| { | |||
| testContextInstance = value; | |||
| } | |||
| } | |||
| #region Additional test attributes | |||
| // | |||
| // You can use the following additional attributes as you write your tests: | |||
| // | |||
| // Use ClassInitialize to run code before running the first test in the class | |||
| // [ClassInitialize()] | |||
| // public static void MyClassInitialize(TestContext testContext) { } | |||
| // | |||
| // Use ClassCleanup to run code after all tests in a class have run | |||
| // [ClassCleanup()] | |||
| // public static void MyClassCleanup() { } | |||
| // | |||
| // Use TestInitialize to run code before running each test | |||
| // [TestInitialize()] | |||
| // public void MyTestInitialize() { } | |||
| // | |||
| // Use TestCleanup to run code after each test has run | |||
| // [TestCleanup()] | |||
| // public void MyTestCleanup() { } | |||
| // | |||
| #endregion | |||
| [TestMethod] | |||
| public void TestSplitText() | |||
| { | |||
| // The test text element to split | |||
| Text t = new Text(0, new XElement(DocX.w + "t", "Hello I am a string")); | |||
| #region Split at index 0 | |||
| /* | |||
| * Split t at index 0. | |||
| * This will cause the left side of the split to be null and the right side to be equal to t. | |||
| */ | |||
| XElement[] splitText_indexZero = Text.SplitText(t, 0); | |||
| // Check if my expectations have been met | |||
| Assert.IsNull(splitText_indexZero[0]); | |||
| Assert.AreEqual(t.Xml.ToString(), splitText_indexZero[1].ToString()); | |||
| #endregion | |||
| #region Split at index 1 | |||
| XElement[] splitText_indexOne = Text.SplitText(t, 1); | |||
| // The result I expect to get from splitText1 | |||
| XElement splitText_indexOne_left = new XElement(DocX.w + "t", "H"); | |||
| XElement splitText_indexOne_right = new XElement(DocX.w + "t", "ello I am a string"); | |||
| // Check if my expectations have been met | |||
| Assert.AreEqual(splitText_indexOne_left.ToString(), splitText_indexOne[0].ToString()); | |||
| Assert.AreEqual(splitText_indexOne_right.ToString(), splitText_indexOne[1].ToString()); | |||
| #endregion | |||
| #region Split near the middle causing starting and ending spaces | |||
| /* | |||
| * Split the text at index 11. | |||
| * This will cause the left side of the split to end with a space and the right to start with a space. | |||
| */ | |||
| XElement[] splitText_nearMiddle = Text.SplitText(t, 11); | |||
| // The result I expect to get from splitText1 | |||
| XElement splitText_nearMiddle_left = new XElement(DocX.w + "t", new object[] { new XAttribute(XNamespace.Xml + "space", "preserve"), "Hello I am " }); | |||
| XElement splitText_nearMiddle_right = new XElement(DocX.w + "t", new object[] { new XAttribute(XNamespace.Xml + "space", "preserve"), " a string" }); | |||
| // Check if my expectations have been met | |||
| Assert.AreEqual(splitText_nearMiddle_left.ToString(), splitText_nearMiddle[0].ToString()); | |||
| Assert.AreEqual(splitText_nearMiddle_right.ToString(), splitText_nearMiddle[1].ToString()); | |||
| #endregion | |||
| #region Split at text.Value.Length - 1 | |||
| XElement[] splitText_indexOneFromLength = Text.SplitText(t, t.Value.Length - 1); | |||
| // The result I expect to get from splitText1 | |||
| XElement splitText_indexOneFromLength_left = new XElement(DocX.w + "t", "Hello I am a strin"); | |||
| XElement splitText_indexOneFromLength_right = new XElement(DocX.w + "t", "g"); | |||
| // Check if my expectations have been met | |||
| Assert.AreEqual(splitText_indexOneFromLength_left.ToString(), splitText_indexOneFromLength[0].ToString()); | |||
| Assert.AreEqual(splitText_indexOneFromLength_right.ToString(), splitText_indexOneFromLength[1].ToString()); | |||
| #endregion | |||
| #region Split at index text.Value.Length | |||
| /* | |||
| * Split the text at index text.Value.Length. | |||
| * This will cause the left side of the split to be equal to text and the right side to be null. | |||
| */ | |||
| XElement[] splitText_indexLength = Text.SplitText(t, t.Value.Length); | |||
| // Check if my expectations have been met | |||
| Assert.AreEqual(t.Xml.ToString(), splitText_indexLength[0].ToString()); | |||
| Assert.IsNull(splitText_indexLength[1]); | |||
| #endregion | |||
| } | |||
| [TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))] | |||
| public void TestSplitText_IndexLessThanTextStartIndex() | |||
| { | |||
| // The test text element to split | |||
| Text t = new Text(0, new XElement(DocX.w + "t", "Hello I am a string")); | |||
| /* | |||
| * Split t at a negative index. | |||
| * This will cause an argument out of range exception to be thrown. | |||
| */ | |||
| Text.SplitText(t, t.StartIndex - 1); | |||
| } | |||
| [TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))] | |||
| public void TestSplitText_IndexGreaterThanTextEndIndex() | |||
| { | |||
| // The test text element to split | |||
| Text t = new Text(0, new XElement(DocX.w + "t", "Hello I am a string")); | |||
| /* | |||
| * Split t at an index greater than its text length. | |||
| * This will cause an argument out of range exception to be thrown. | |||
| */ | |||
| Text.SplitText(t, t.EndIndex + 1); | |||
| } | |||
| [TestMethod] | |||
| public void TestSplitTextOfLengthOne() | |||
| { | |||
| // The test text element to split | |||
| Text t = new Text(0, new XElement(DocX.w + "tab")); | |||
| XElement[] splitTextOfLengthOne; | |||
| #region Split before | |||
| splitTextOfLengthOne = Text.SplitText(t, t.StartIndex); | |||
| // Check if my expectations have been met | |||
| Assert.AreEqual(t.Xml.ToString(), splitTextOfLengthOne[0].ToString()); | |||
| Assert.IsNull(splitTextOfLengthOne[1]); | |||
| #endregion | |||
| #region Split after | |||
| splitTextOfLengthOne = Text.SplitText(t, t.EndIndex); | |||
| // Check if my expectations have been met | |||
| Assert.IsNull(splitTextOfLengthOne[0]); | |||
| Assert.AreEqual(t.Xml.ToString(), splitTextOfLengthOne[1].ToString()); | |||
| #endregion | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,70 @@ | |||
| <Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
| <PropertyGroup> | |||
| <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||
| <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||
| <ProductVersion>9.0.30729</ProductVersion> | |||
| <SchemaVersion>2.0</SchemaVersion> | |||
| <ProjectGuid>{6E4A2A95-9A9A-4F8C-A68D-4950E3455700}</ProjectGuid> | |||
| <OutputType>Library</OutputType> | |||
| <AppDesignerFolder>Properties</AppDesignerFolder> | |||
| <RootNamespace>UnitTests</RootNamespace> | |||
| <AssemblyName>UnitTests</AssemblyName> | |||
| <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> | |||
| <FileAlignment>512</FileAlignment> | |||
| <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> | |||
| <SccProjectName>SAK</SccProjectName> | |||
| <SccLocalPath>SAK</SccLocalPath> | |||
| <SccAuxPath>SAK</SccAuxPath> | |||
| <SccProvider>SAK</SccProvider> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | |||
| <DebugSymbols>true</DebugSymbols> | |||
| <DebugType>full</DebugType> | |||
| <Optimize>false</Optimize> | |||
| <OutputPath>bin\Debug\</OutputPath> | |||
| <DefineConstants>DEBUG;TRACE</DefineConstants> | |||
| <ErrorReport>prompt</ErrorReport> | |||
| <WarningLevel>4</WarningLevel> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | |||
| <DebugType>pdbonly</DebugType> | |||
| <Optimize>true</Optimize> | |||
| <OutputPath>bin\Release\</OutputPath> | |||
| <DefineConstants>TRACE</DefineConstants> | |||
| <ErrorReport>prompt</ErrorReport> | |||
| <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="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" /> | |||
| <Reference Include="System" /> | |||
| <Reference Include="System.Core"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| </Reference> | |||
| <Reference Include="System.XML" /> | |||
| <Reference Include="System.Xml.Linq"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| </Reference> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <Compile Include="PreserveSpaceCheckTests.cs" /> | |||
| <Compile Include="Properties\AssemblyInfo.cs" /> | |||
| <Compile Include="SplitEditTests.cs" /> | |||
| <Compile Include="SplitRunTests.cs" /> | |||
| <Compile Include="SplitTextTests.cs" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <Content Include="AuthoringTests.txt" /> | |||
| </ItemGroup> | |||
| <Import Project="$(MSBuildBinPath)\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. | |||
| <Target Name="BeforeBuild"> | |||
| </Target> | |||
| <Target Name="AfterBuild"> | |||
| </Target> | |||
| --> | |||
| </Project> | |||
| @@ -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" = "PROVIDER" | |||
| } | |||