diff --git a/OpenXmlPowerTools.Tests/DocumentAssemblerTests.cs b/OpenXmlPowerTools.Tests/DocumentAssemblerTests.cs index cf62d2f0..54f9475a 100644 --- a/OpenXmlPowerTools.Tests/DocumentAssemblerTests.cs +++ b/OpenXmlPowerTools.Tests/DocumentAssemblerTests.cs @@ -171,6 +171,24 @@ public void DA259(string name, string data, bool err) Assert.Equal(4, brCount); } + [Fact] + public void DA240() + { + string name = "DA240-Whitespace.docx"; + DA101(name, "DA240-Whitespace.xml", false); + var assembledDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".docx", "-processed-by-DocumentAssembler.docx"))); + WmlDocument afterAssembling = new WmlDocument(assembledDocx.FullName); + + // when elements are inserted that begin or end with white space, make sure white space is preserved + string firstParaTextIncorrect = afterAssembling.MainDocumentPart.Element(W.body).Elements(W.p).First().Value; + Assert.Equal("Content may or may not have spaces: he/she; he, she; he and she.", firstParaTextIncorrect); + // warning: XElement.Value returns the string resulting from direct concatenation of all W.t elements. This is fast but ignores + // proper handling of xml:space="preserve" attributes, which Word honors when rendering content. Below we also check + // the result of UnicodeMapper.RunToString, which has been enhanced to take xml:space="preserve" into account. + string firstParaTextCorrect = InnerText(afterAssembling.MainDocumentPart.Element(W.body).Elements(W.p).First()); + Assert.Equal("Content may or may not have spaces: he/she; he, she; he and she.", firstParaTextCorrect); + } + [Theory] [InlineData("DA024-TrackedRevisions.docx", "DA-Data.xml")] public void DA102_Throws(string name, string data) @@ -190,6 +208,42 @@ public void DA102_Throws(string name, string data) }); } + [Fact] + public void DATemplateMaior() + { + // this test case was causing incorrect behavior of OpenXmlRegex when replacing fields in paragraphs that contained + // lastRenderedPageBreak XML elements. Recent fixes relating to UnicodeMapper and OpenXmlRegex addressed it. + string name = "DA-TemplateMaior.docx"; + DA101(name, "DA-templateMaior.xml", false); + var assembledDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".docx", "-processed-by-DocumentAssembler.docx"))); + var afterAssembling = new WmlDocument(assembledDocx.FullName); + + var descendants = afterAssembling.MainDocumentPart.Value; + + Assert.False(descendants.Contains(">"), "Found > on text"); + } + + [Fact] + public void DAXmlError() + { + /* The assembly below would originally (prior to bug fixes) cause an exception to be thrown during assembly:  + System.ArgumentException : '', hexadecimal value 0x01, is an invalid character. + */ + string name = "DA-xmlerror.docx"; + string data = "DA-xmlerror.xml"; + + DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var templateDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); + var dataFile = new FileInfo(Path.Combine(sourceDir.FullName, data)); + + var wmlTemplate = new WmlDocument(templateDocx.FullName); + var xmlData = XElement.Load(dataFile.FullName); + + var afterAssembling = DocumentAssembler.AssembleDocument(wmlTemplate, xmlData, out var returnedTemplateError); + var assembledDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, templateDocx.Name.Replace(".docx", "-processed-by-DocumentAssembler.docx"))); + afterAssembling.SaveAs(assembledDocx.FullName); + } + [Theory] [InlineData("DA025-TemplateDocument.docx", "DA-Data.xml", false)] public void DA103_UseXmlDocument(string name, string data, bool err) @@ -221,6 +275,14 @@ public void DA103_UseXmlDocument(string name, string data, bool err) Assert.Equal(err, returnedTemplateError); } + private static string InnerText(XContainer e) + { + return e.Descendants(W.r) + .Where(r => r.Parent.Name != W.del) + .Select(UnicodeMapper.RunToString) + .StringConcatenate(); + } + private static List s_ExpectedErrors = new List() { "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:evenHBand' attribute is not declared.", @@ -237,6 +299,7 @@ public void DA103_UseXmlDocument(string name, string data, bool err) "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:noVBand' attribute is not declared.", "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:oddHBand' attribute is not declared.", "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:oddVBand' attribute is not declared.", + "The attribute 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:name' has invalid value 'useWord2013TrackBottomHyphenation'. The Enumeration constraint failed.", }; } } diff --git a/OpenXmlPowerTools.Tests/OpenXmlRegexTests.cs b/OpenXmlPowerTools.Tests/OpenXmlRegexTests.cs index 1921db11..282af0f5 100644 --- a/OpenXmlPowerTools.Tests/OpenXmlRegexTests.cs +++ b/OpenXmlPowerTools.Tests/OpenXmlRegexTests.cs @@ -202,6 +202,21 @@ public class OpenXmlRegexTests "; + private const string LastRenderedPageBreakXmlString = +@" + + + + ThisIsAParagraphContainingNoNaturalLi + + + + neBreaksSoTheLineBreakIsForced. + + + +"; + private static string InnerText(XContainer e) { return e.Descendants(W.r) @@ -380,6 +395,34 @@ public void CanReplaceTextWithFields() Assert.Equal("As stated in Article {__1} and this Section {__1.1}, this is described in Exhibit 4.", innerText); } } + + [Fact] + public void CanMatchDespiteLastRenderedPageBreaks() + { + XDocument partDocument = XDocument.Parse(LastRenderedPageBreakXmlString); + XElement p = partDocument.Descendants(W.p).Last(); + string innerText = InnerText(p); + + Assert.Equal("ThisIsAParagraphContainingNoNaturalLineBreaksSoTheLineBreakIsForced.", innerText); + + using (var stream = new MemoryStream()) + using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, DocumentType)) + { + MainDocumentPart part = wordDocument.AddMainDocumentPart(); + part.PutXDocument(partDocument); + + IEnumerable content = partDocument.Descendants(W.p); + var regex = new Regex(@"LineBreak"); + int count = OpenXmlRegex.Replace(content, regex, "LB", null); + + p = partDocument.Descendants(W.p).Last(); + innerText = InnerText(p); + + Assert.Equal(2, count); + Assert.Equal("ThisIsAParagraphContainingNoNaturalLBsSoTheLBIsForced.", innerText); + } + } + } } diff --git a/OpenXmlPowerTools.Tests/PtUtilTests.cs b/OpenXmlPowerTools.Tests/PtUtilTests.cs index 4515e5d9..d8a0bb5a 100644 --- a/OpenXmlPowerTools.Tests/PtUtilTests.cs +++ b/OpenXmlPowerTools.Tests/PtUtilTests.cs @@ -20,7 +20,7 @@ namespace OxPt { public class PtUtilTests { - [Theory(Skip = "This is failing on AppVeyor")] + [Theory] [InlineData("PU/PU001-Test001.mht")] public void PU001(string name) { diff --git a/OpenXmlPowerTools.Tests/SpreadsheetWriterTests.cs b/OpenXmlPowerTools.Tests/SpreadsheetWriterTests.cs index 5deccaa2..63ea24e4 100644 --- a/OpenXmlPowerTools.Tests/SpreadsheetWriterTests.cs +++ b/OpenXmlPowerTools.Tests/SpreadsheetWriterTests.cs @@ -286,20 +286,48 @@ public void SW002_AllDataTypes() { Cells = new Sw.CellDfn[] { + new Sw.CellDfn { + CellDataType = Sw.CellDataType.String, + Value = "date (t:d, mm-dd-yy)", + }, new Sw.CellDfn { CellDataType = Sw.CellDataType.Date, - Value = new DateTime(2012, 1, 8), + Value = new DateTime(2012, 1, 8).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff"), FormatCode = "mm-dd-yy", }, + } + }, + new Sw.RowDfn + { + Cells = new Sw.CellDfn[] + { + new Sw.CellDfn { + CellDataType = Sw.CellDataType.String, + Value = "date (t:d, d-mmm-yy)", + }, new Sw.CellDfn { CellDataType = Sw.CellDataType.Date, - Value = new DateTime(2012, 1, 9), - FormatCode = "mm-dd-yy", + Value = new DateTime(2012, 1, 9).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff"), + FormatCode = "d-mmm-yy", Bold = true, HorizontalCellAlignment = Sw.HorizontalCellAlignment.Center, }, } }, + new Sw.RowDfn + { + Cells = new Sw.CellDfn[] + { + new Sw.CellDfn { + CellDataType = Sw.CellDataType.String, + Value = "date (t:d)", + }, + new Sw.CellDfn { + CellDataType = Sw.CellDataType.Date, + Value = new DateTime(2012, 1, 11).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff"), + }, + } + }, } } } diff --git a/OpenXmlPowerTools.Tests/UnicodeMapperTests.cs b/OpenXmlPowerTools.Tests/UnicodeMapperTests.cs index 562a6682..38b09b6b 100644 --- a/OpenXmlPowerTools.Tests/UnicodeMapperTests.cs +++ b/OpenXmlPowerTools.Tests/UnicodeMapperTests.cs @@ -142,6 +142,40 @@ public void CanStringifySymbols() Assert.Equal(symFromChar1.ToString(SaveOptions.None), symFromChar2.ToString(SaveOptions.None)); Assert.Equal(symFromChar1.ToString(SaveOptions.None), symFromChar3.ToString(SaveOptions.None)); } + + [Fact] + public void HonorsXmlSpace() + { + XDocument partDocument = XDocument.Parse(PreserveSpacingXmlString); + XElement p = partDocument.Descendants(W.p).Last(); + string innerText = p.Descendants(W.r) + .Select(UnicodeMapper.RunToString) + .StringConcatenate(); + Assert.Equal(@"The following space is retained: but this one is not:. Similarly these two lines should have only a space between them: Line 1! Line 2!", innerText); + } + + private const string PreserveSpacingXmlString = +@" + + + + The following space is retained: + + + but this one is not: + + + . Similarly these two lines should have only a space between them: + + + + Line 1! +Line 2! + + + + +"; } } diff --git a/OpenXmlPowerTools/ChartUpdater.cs b/OpenXmlPowerTools/ChartUpdater.cs index 79b0f74b..18f3ad48 100644 --- a/OpenXmlPowerTools/ChartUpdater.cs +++ b/OpenXmlPowerTools/ChartUpdater.cs @@ -400,7 +400,8 @@ private static void UpdateEmbeddedWorkbook(ChartPart chartPart, ChartData chartD var embeddedSpreadsheet = chartPart.GetPartById(embeddedSpreadsheetRid); if (embeddedSpreadsheet != null) { - using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(embeddedSpreadsheet.GetStream(), true)) + using (Stream spreadsheetStream = embeddedSpreadsheet.GetStream()) + using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(spreadsheetStream, true)) { var workbookPart = sDoc.WorkbookPart; var wbRoot = workbookPart.GetXDocument().Root; diff --git a/OpenXmlPowerTools/DocumentAssembler.cs b/OpenXmlPowerTools/DocumentAssembler.cs index f4d9673e..e8b50bb0 100644 --- a/OpenXmlPowerTools/DocumentAssembler.cs +++ b/OpenXmlPowerTools/DocumentAssembler.cs @@ -623,7 +623,7 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr p.Add(new XElement(W.r, para.Elements(W.r).Elements(W.rPr).FirstOrDefault(), (p.Elements().Count() > 1) ? new XElement(W.br) : null, - new XElement(W.t, line))); + new XElement(W.t, GetXmlSpaceAttribute(line), line))); } return p; } @@ -635,7 +635,7 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr list.Add(new XElement(W.r, run.Elements().Where(e => e.Name != W.t), (list.Count > 0) ? new XElement(W.br) : null, - new XElement(W.t, line))); + new XElement(W.t, GetXmlSpaceAttribute(line), line))); } return list; } @@ -853,5 +853,18 @@ private static string EvaluateXPathToString(XElement element, string xPath, bool return xPathSelectResult.ToString(); } + + private static XAttribute GetXmlSpaceAttribute(string textOfTextElement) + { + if (!string.IsNullOrEmpty(textOfTextElement)) + { + if (char.IsWhiteSpace(textOfTextElement[0]) || + char.IsWhiteSpace(textOfTextElement[textOfTextElement.Length - 1])) + { + return new XAttribute(XNamespace.Xml + "space", "preserve"); + } + } + return null; + } } } diff --git a/OpenXmlPowerTools/OpenXmlRegex.cs b/OpenXmlPowerTools/OpenXmlRegex.cs index 09b77f0d..a5dc13a0 100644 --- a/OpenXmlPowerTools/OpenXmlRegex.cs +++ b/OpenXmlPowerTools/OpenXmlRegex.cs @@ -387,7 +387,7 @@ private static object WmlSearchAndReplaceTransform(XNode node, Regex regex, stri if (element.Name == W.r) { return element.Elements() - .Where(e => e.Name != W.rPr) + .Where(e => e.Name != W.rPr && e.Name != W.lastRenderedPageBreak) .Select(e => e.Name == W.t ? ((string) e).Select(c => new XElement(W.r, diff --git a/OpenXmlPowerTools/PresentationBuilder.cs b/OpenXmlPowerTools/PresentationBuilder.cs index 9d6de5e4..c151e554 100644 --- a/OpenXmlPowerTools/PresentationBuilder.cs +++ b/OpenXmlPowerTools/PresentationBuilder.cs @@ -336,7 +336,10 @@ private static void CopyPresentationParts(PresentationDocument sourceDocument, P foreach (var legacyDocTextInfo in sourceDocument.PresentationPart.Parts.Where(p => p.OpenXmlPart.RelationshipType == "http://schemas.microsoft.com/office/2006/relationships/legacyDocTextInfo")) { LegacyDiagramTextInfoPart newPart = newDocument.PresentationPart.AddNewPart(); - newPart.FeedData(legacyDocTextInfo.OpenXmlPart.GetStream()); + using (var stream = legacyDocTextInfo.OpenXmlPart.GetStream()) + { + newPart.FeedData(stream); + } } var listOfRootChildren = newPresentation.Root.Elements().ToList(); @@ -383,7 +386,10 @@ private static XElement CreatedEmbeddedFontPart(PresentationDocument sourceDocum fpt = FontPartType.FontOdttf; var newId = "R" + Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16); var newFontPart = newDocument.PresentationPart.AddFontPart(fpt, newId); - newFontPart.FeedData(oldFontPart.GetStream()); + using (var stream = oldFontPart.GetStream()) + { + newFontPart.FeedData(stream); + } newRegular = new XElement(fontXName, new XAttribute(R.id, newId)); return newRegular; @@ -931,12 +937,18 @@ private static void CopyRelatedPartsForContentParts(PresentationDocument newDocu if (oldPartIdPair9 != null) { CustomXmlPart newPart = newDocument.PresentationPart.AddCustomXmlPart(CustomXmlPartType.CustomXml); - newPart.FeedData(oldPartIdPair9.OpenXmlPart.GetStream()); + using (var stream = oldPartIdPair9.OpenXmlPart.GetStream()) + { + newPart.FeedData(stream); + } foreach (var itemProps in oldPartIdPair9.OpenXmlPart.Parts.Where(p => p.OpenXmlPart.ContentType == "application/vnd.openxmlformats-officedocument.customXmlProperties+xml")) { var newId2 = "R" + Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16); CustomXmlPropertiesPart cxpp = newPart.AddNewPart("application/vnd.openxmlformats-officedocument.customXmlProperties+xml", newId2); - cxpp.FeedData(itemProps.OpenXmlPart.GetStream()); + using (var stream = itemProps.OpenXmlPart.GetStream()) + { + cxpp.FeedData(stream); + } } var newId = "R" + Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16); newContentPart.CreateRelationshipToPart(newPart, newId); @@ -1057,6 +1069,21 @@ private static void CopyChartObjects(ChartPart oldChart, ChartPart newChart) dataReference.Attribute(R.id).Value = newChart.GetIdOfPart(newPart); continue; } + ExtendedPart extendedPart = oldPartIdPair.OpenXmlPart as ExtendedPart; + if (extendedPart != null) + { + ExtendedPart newPart = newChart.AddExtendedPart(extendedPart.RelationshipType, extendedPart.ContentType, ".dat"); + using (Stream oldObject = extendedPart.GetStream(FileMode.Open, FileAccess.Read)) + using (Stream newObject = newPart.GetStream(FileMode.Create, FileAccess.ReadWrite)) + { + int byteCount; + byte[] buffer = new byte[65536]; + while ((byteCount = oldObject.Read(buffer, 0, 65536)) != 0) + newObject.Write(buffer, 0, byteCount); + } + dataReference.Attribute(R.id).Value = newChart.GetIdOfPart(newPart); + continue; + } EmbeddedObjectPart oldEmbeddedObjectPart = oldPartIdPair.OpenXmlPart as EmbeddedObjectPart; if (oldEmbeddedObjectPart != null) { @@ -1350,7 +1377,10 @@ private static void CopyRelatedMedia(OpenXmlPart oldContentPart, OpenXmlPart new var ct = oldPart.ContentType; var ext = Path.GetExtension(oldPart.Uri.OriginalString); MediaDataPart newPart = newContentPart.OpenXmlPackage.CreateMediaDataPart(ct, ext); - newPart.FeedData(oldPart.GetStream()); + using (var stream = oldPart.GetStream()) + { + newPart.FeedData(stream); + } string id = null; string relationshipType = null; @@ -1490,7 +1520,10 @@ private static void CopyInkPart(OpenXmlPart oldContentPart, OpenXmlPart newConte var newId = "R" + Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16); CustomXmlPart newPart = newContentPart.AddNewPart("application/inkml+xml", newId); - newPart.FeedData(oldPart.GetStream()); + using (var stream = oldPart.GetStream()) + { + newPart.FeedData(stream); + } contentPartReference.Attribute(attributeName).Value = newId; } @@ -1508,8 +1541,11 @@ private static void CopyActiveXPart(OpenXmlPart oldContentPart, OpenXmlPart newC var newId = "R" + Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16); EmbeddedControlPersistencePart newPart = newContentPart.AddNewPart("application/vnd.ms-office.activeX+xml", newId); - - newPart.FeedData(oldPart.GetStream()); + + using (var stream = oldPart.GetStream()) + { + newPart.FeedData(stream); + } activeXPartReference.Attribute(attributeName).Value = newId; if (newPart.ContentType == "application/vnd.ms-office.activeX+xml") @@ -1521,8 +1557,11 @@ private static void CopyActiveXPart(OpenXmlPart oldContentPart, OpenXmlPart newC var newId2 = "R" + Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16); EmbeddedControlPersistenceBinaryDataPart newPersistencePart = newPart.AddNewPart("application/vnd.ms-office.activeX", newId2); - - newPersistencePart.FeedData(oldPersistencePart.GetStream()); + + using (var stream = oldPersistencePart.GetStream()) + { + newPersistencePart.FeedData(stream); + } axc.Root.Attribute(R.id).Value = newId2; newPart.PutXDocument(); } @@ -1544,7 +1583,10 @@ private static void CopyLegacyDiagramText(OpenXmlPart oldContentPart, OpenXmlPar var newId = "R" + Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16); LegacyDiagramTextPart newPart = newContentPart.AddNewPart(newId); - newPart.FeedData(oldPart.GetStream()); + using (var stream = oldPart.GetStream()) + { + newPart.FeedData(stream); + } textdataReference.Attribute(attributeName).Value = newId; } @@ -1714,7 +1756,10 @@ private static void CopyExtendedPart(OpenXmlPart oldContentPart, OpenXmlPart new newPart = ((XmlSignaturePart)newContentPart).AddExtendedPart(oldPart.RelationshipType, oldPart.ContentType, fileInfo.Extension); relId = newContentPart.GetIdOfPart(newPart); - newPart.FeedData(oldPart.GetStream()); + using (Stream sourceStream = oldPart.GetStream()) + { + newPart.FeedData(sourceStream); + } extendedReference.Attribute(attributeName).Value = relId; } catch (ArgumentOutOfRangeException) @@ -1778,7 +1823,10 @@ private static void CopyRelatedSound(PresentationDocument newDocument, OpenXmlPa { AudioReferenceRelationship temp = (AudioReferenceRelationship)oldContentPart.GetReferenceRelationship(relId); MediaDataPart newSound = newDocument.CreateMediaDataPart(temp.DataPart.ContentType); - newSound.FeedData(temp.DataPart.GetStream()); + using (var stream = temp.DataPart.GetStream()) + { + newSound.FeedData(stream); + } AudioReferenceRelationship newRel = null; if (newContentPart is SlidePart) @@ -1799,7 +1847,10 @@ private static void CopyRelatedSound(PresentationDocument newDocument, OpenXmlPa { MediaReferenceRelationship temp = (MediaReferenceRelationship)oldContentPart.GetReferenceRelationship(relId); MediaDataPart newSound = newDocument.CreateMediaDataPart(temp.DataPart.ContentType); - newSound.FeedData(temp.DataPart.GetStream()); + using (var stream = temp.DataPart.GetStream()) + { + newSound.FeedData(stream); + } MediaReferenceRelationship newRel = null; if (newContentPart is SlidePart) diff --git a/OpenXmlPowerTools/PtOpenXmlDocument.cs b/OpenXmlPowerTools/PtOpenXmlDocument.cs index 86e49db3..ce2ac570 100644 --- a/OpenXmlPowerTools/PtOpenXmlDocument.cs +++ b/OpenXmlPowerTools/PtOpenXmlDocument.cs @@ -339,11 +339,12 @@ private static Type GetDocumentType(byte[] bytes) public static void SavePartAs(OpenXmlPart part, string filePath) { - Stream partStream = part.GetStream(FileMode.Open, FileAccess.Read); - byte[] partContent = new byte[partStream.Length]; - partStream.Read(partContent, 0, (int)partStream.Length); - - File.WriteAllBytes(filePath, partContent); + using (Stream partStream = part.GetStream(FileMode.Open, FileAccess.Read)) + { + byte[] partContent = new byte[partStream.Length]; + partStream.Read(partContent, 0, (int)partStream.Length); + File.WriteAllBytes(filePath, partContent); + } } } diff --git a/OpenXmlPowerTools/UnicodeMapper.cs b/OpenXmlPowerTools/UnicodeMapper.cs index 8cd3b95e..e5b91050 100644 --- a/OpenXmlPowerTools/UnicodeMapper.cs +++ b/OpenXmlPowerTools/UnicodeMapper.cs @@ -69,7 +69,12 @@ public static string RunToString(XElement element) // For w:t elements, we obviously want the element's value. if (element.Name == W.t) - return (string) element; + { + // Emulate Word's handling of the xml:space attribute on text elements + XAttribute spaceAttribute = element.Attribute(XNamespace.Xml + "space"); + string space = spaceAttribute != null ? spaceAttribute.Value : null; + return space == "preserve" ? (string)element : IgnoreTextSpacing((string)element); + } // Turn elements representing special characters into their corresponding // unicode characters. @@ -91,6 +96,9 @@ public static string RunToString(XElement element) return SoftHyphen.ToString(); if (element.Name == W.tab) return HorizontalTabulation.ToString(); + // Ignore temporary layout markers that are not actual document content + if (element.Name == W.lastRenderedPageBreak) + return string.Empty; if (element.Name == W.fldChar) { @@ -122,6 +130,22 @@ public static string RunToString(XElement element) return StartOfHeading.ToString(); } + /// + /// Emulate the way Word treats text elements when attribute xml:space="preserve" + /// is NOT present. + /// + /// The entire content of the w:t element. + /// The corresponding text string Word would display, print, and + /// allow to be edited. + private static string IgnoreTextSpacing(string text) + { + // all whitespace at beginning and end of entire string is ignored + // if text contains line breaks, they are ignored/replaced with a single space + return string.Join(" ", + text.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries) + ).Trim(); + } + /// /// Translate a symbol into a Unicode character, using the specified w:font attribute /// value and unicode value (represented by the w:sym element's w:char attribute), diff --git a/TestFiles/DA-TemplateMaior.docx b/TestFiles/DA-TemplateMaior.docx new file mode 100644 index 00000000..bef4c9ad Binary files /dev/null and b/TestFiles/DA-TemplateMaior.docx differ diff --git a/TestFiles/DA-templateMaior.xml b/TestFiles/DA-templateMaior.xml new file mode 100644 index 00000000..1acbd7ff --- /dev/null +++ b/TestFiles/DA-templateMaior.xml @@ -0,0 +1,407 @@ + + + + + F + SIM + ANDRÉ TENROLLER DE OLIVEIRA FOLADOR + 847.518.000-00 + 1114427329 + SSP/RS + BRASILEIRO + 19/05/1995 + SOLTEIRO + + + FOLADORANDRE@GMAIL.COM + TECNICO DE ELETRONICA + + AV BERNARDI + 164 + CRISTO REDENTOR + APTO 105 + 91040-030 + PORTO ALEGRE + RS + AV BERNARDI, 164, APTO 105, CRISTO REDENTOR, PORTO ALEGRE/RS, CEP: 91040-030 + + + + + + + + + + + + MARCOS ROBERTO FOLADOR + CLAUDIA LUIZA GRAÇA TENROLLER DE OLIVEIRA + NAO + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NAO + + + + + + 001 + 05 + ATO + 001 + Um + 12/08/2020 + 60.458,51 + 60.458,51 + Sessenta mil e quatrocentos e cinquenta e oito reais e cinquenta e um centavos + Sessenta mil e quatrocentos e cinquenta e oito reais e cinquenta e um centavos + 60.458,51 + 60.458,51 + Sessenta mil e quatrocentos e cinquenta e oito reais e cinquenta e um centavos + Sessenta mil e quatrocentos e cinquenta e oito reais e cinquenta e um centavos + 15,47 + Quinze inteiros e Quarenta e sete centésimos por cento + 15.47 + 12,00 + Doze por cento + TP + N + N + N + + + 002 + 01 + MENSAL + 036 + Trinta e seis + 20/09/2020 + 1.200,00 + 43.200,00 + Um mil e duzentos reais + Quarenta e três mil e duzentos reais + 1.200,00 + 43.200,00 + Um mil e duzentos reais + Quarenta e três mil e duzentos reais + 11,05 + Onze inteiros e Cinco décimos por cento + 11.05 + 12,00 + Doze por cento + TP + N + S + S + + + 003 + 09 + UNICA + 001 + Um + 20/02/2021 + 15.000,00 + 15.000,00 + Quinze mil reais + Quinze mil reais + 15.000,00 + 15.000,00 + Quinze mil reais + Quinze mil reais + 3,84 + Três inteiros e Oitenta e quatro centésimos por cento + 3.84 + 12,00 + Doze por cento + TP + N + S + S + + + 004 + 09 + UNICA + 001 + Um + 20/11/2021 + 15.000,00 + 15.000,00 + Quinze mil reais + Quinze mil reais + 15.000,00 + 15.000,00 + Quinze mil reais + Quinze mil reais + 3,84 + Três inteiros e Oitenta e quatro centésimos por cento + 3.84 + 12,00 + Doze por cento + TP + N + S + S + + + 005 + 09 + UNICA + 001 + Um + 20/05/2022 + 15.000,00 + 15.000,00 + Quinze mil reais + Quinze mil reais + 15.000,00 + 15.000,00 + Quinze mil reais + Quinze mil reais + 3,84 + Três inteiros e Oitenta e quatro centésimos por cento + 3.84 + 12,00 + Doze por cento + TP + N + S + S + + + 006 + 01 + MENSAL + 007 + Sete + 20/09/2023 + 1.200,00 + 8.400,00 + Um mil e duzentos reais + Oito mil e quatrocentos reais + 1.245,98 + 8.721,83 + Um mil e duzentos reais + Oito mil e setecentos e vinte e um reais e oitenta e três centavos + 2,15 + Dois inteiros e Quinze centésimos por cento + 2.15 + 12,00 + Doze por cento + TP + N + S + S + + + 007 + 08 + FINANCIAMENTO + 001 + Um + 30/06/2024 + 214.229,73 + 214.229,73 + Duzentos e quatorze mil e duzentos e vinte e nove reais e setenta e três centavos + Duzentos e quatorze mil e duzentos e vinte e nove reais e setenta e três centavos + 235.447,87 + 235.447,87 + Duzentos e quatorze mil e duzentos e vinte e nove reais e setenta e três centavos + Duzentos e trinta e cinco mil e quatrocentos e quarenta e sete reais e oitenta e sete centavos + 59,81 + Cinquenta e nove inteiros e Oitenta e um centésimos por cento + 59.81 + 12,00 + Doze por cento + TP + N + S + S + + + + + ATO +R$ 60.458,51 (Sessenta mil e quatrocentos e cinquenta e oito reais e cinquenta e um centavos), correspondente à 15,47% do preço. +Número de parcelas: 1 (uma) parcela com valor nominal nesta data de R$ 60.458,51 (Sessenta mil e quatrocentos e cinquenta e oito reais e cinquenta e um centavos), cujo valor será pago à vista, ou seja, em parcela única por meio de TED ou boleto bancário, ficando a quitação vinculada respectivamente a efetivação da transferência ou comprovação do pagamento do boleto bancário. +Data de vencimento: 12/08/2020 + + + PARCELAS MENSAIS +R$ 43.200,00 (Quarenta e três mil e duzentos reais). +Número de parcela(s): 036 (Trinta e seis) parcelas, mensais e sucessivas, com valor nominal nesta data de R$ 1.200,00 (Um mil e duzentos reais), cada uma. +Data do vencimento da primeira parcela: 20/09/2020. As demais parcelas terão vencimento nos mesmos dias do calendário civil dos meses imediatamente subsequentes. + + + PARCELA DE REFORÇO +R$ 15.000,00 (Quinze mil reais). +Número de parcela(s): 1 (uma) parcela com valor nominal nesta data de R$ 15.000,00 (Quinze mil reais). +Data do vencimento da parcela: 20/02/2021. + + + PARCELA DE REFORÇO +R$ 15.000,00 (Quinze mil reais). +Número de parcela(s): 1 (uma) parcela com valor nominal nesta data de R$ 15.000,00 (Quinze mil reais). +Data do vencimento da parcela: 20/11/2021. + + + PARCELA DE REFORÇO +R$ 15.000,00 (Quinze mil reais). +Número de parcela(s): 1 (uma) parcela com valor nominal nesta data de R$ 15.000,00 (Quinze mil reais). +Data do vencimento da parcela: 20/05/2022. + + + PARCELAS MENSAIS +R$ 8.400,00 (Oito mil e quatrocentos reais). +Número de parcela(s): 007 (Sete) parcelas, mensais e sucessivas, com valor nominal nesta data de R$ 1.200,00 (Um mil e duzentos reais), cada uma. +Data do vencimento da primeira parcela: 20/09/2023. As demais parcelas terão vencimento nos mesmos dias do calendário civil dos meses imediatamente subsequentes. + + + PARCELA CHAVES +R$ 214.229,73 (Duzentos e quatorze mil e duzentos e vinte e nove reais e setenta e três centavos). +Número de parcela(s): 1 (uma) parcela com valor nominal nesta data de R$ 214.229,73 (Duzentos e quatorze mil e duzentos e vinte e nove reais e setenta e três centavos) +Data do vencimento da parcela: 30/06/2024 + + + + + CPF + 436.268.820-04 + ROSANGELA DENISE RIFFEL GROEHS + 61093 + 6.253,28 + Seis mil e duzentos e cinquenta e três reais e vinte e oito centavos + + + CPF + 644.541.420-68 + GERSON DONISETTE DOS SANTOS SOUZA + 45312 + 2.344,98 + Dois mil e trezentos e quarenta e quatro reais e noventa e oito centavos + + + CPF + 011.998.550-06 + MAURICIO MOREIRA RAMOS + 48811 + 1.172,48 + Um mil e cento e setenta e dois reais e quarenta e oito centavos + + + CNPJ + 28.007.960/0001-00 + PRION PARTICIPACOES S.A. + 25278J + 9.770,75 + Nove mil e setecentos e setenta reais e setenta e cinco centavos + + + + + 10.274.663/0001-58 + + + + 0 + + + + + + + + + PORTO ALEGRE + + 22.922.412/0001-11 + MELNICK EVEN ANDIROBA EMPREENDIMENTO IMOBILIÁRIO LTDA. + + + + RUA CARLOS TREIN FILHO + + AUXILIADORA + 551 + + PORTO ALEGRE + RS + RUA CARLOS TREIN FILHO, 551, AUXILIADORA, PORTO ALEGRE/RS + + + NAO + + PORTO ALEGRE, 21 DE MAIO DE 2021 + 21 DE MAIO DE 2021 + 21/05/2021 + PORTO ALEGRE, 12 DE AGOSTO DE 2020 + 12 DE AGOSTO DE 2020 + 12/08/2020 + 286.021 + Dezenove mil e quinhentos e quarenta e um reais e quarenta e nove centavos + 19.541,49 + Nove mil e setecentos e setenta reais e setenta e cinco centavos + 9.770,75 + Nove mil e setecentos e setenta reais e setenta e quatro centavos + 9.770,74 + Trezentos e setenta e um mil e duzentos e oitenta e oito reais e vinte e quatro centavos + 371.288,24 + Trezentos e noventa mil e oitocentos e vinte e nove reais e setenta e três centavos + 390.829,73 + Localizado no oitavo pavimento, sendo o nono à esquerda de quem chega na circulação pela escada e segue à direita, com área real privativa de 28,73m², área real de uso comum de divisão não proporcional de 14,08m², área real de uso comum de divisão proporcional de 3,44m², e área real total de 46,25m², correspondendo-lhe a fração ideal de 0,000800 no terreno e nas coisas de uso comum e fim proveitoso do condomínio. + RESIDENCIAL + 802 + 080200000000028602128.007.960/000 + + + CARLOS GOMES SQUARE + + AVENIDA CARLOS GOMES + 1130 + TRÊS FIGUEIRAS + + 90480001 + PORTO ALEGRE + RS + AVENIDA CARLOS GOMES, 1130, TRÊS FIGUEIRAS, PORTO ALEGRE/RS, CEP: 90480001 + + + \ No newline at end of file diff --git a/TestFiles/DA-xmlerror.docx b/TestFiles/DA-xmlerror.docx new file mode 100644 index 00000000..2a615493 Binary files /dev/null and b/TestFiles/DA-xmlerror.docx differ diff --git a/TestFiles/DA-xmlerror.xml b/TestFiles/DA-xmlerror.xml new file mode 100644 index 00000000..69714b10 --- /dev/null +++ b/TestFiles/DA-xmlerror.xml @@ -0,0 +1,296 @@ + + + + F + SIM + PAULO IVANIR CITRON + 236.713.860-53 + 6003511992 + SJS/RS + BRASILEIRO + 17/02/1956 + DIVORCIADO + + + PPICITRON@HOTMAIL.COM + ADMINISTRADOR + + R Dr Dário De Bittencourt + 295 + Vila Ipiranga + 103 Bl 5 + 91360-390 + Porto Alegre + RS + R Dr Dário De Bittencourt, 295, 103 Bl 5, Vila Ipiranga, Porto Alegre/RS, CEP: 91360-390 + + + + + + + + + + + + Angelo Perin Citron + Constancia Ana Soares + SIM + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NAO + + + + + + 001 + 05 + ATO + 001 + Um + 03/09/2022 + 116.160,00 + 116.160,00 + Cento e dezesseis mil e cento e sessenta reais + Cento e dezesseis mil e cento e sessenta reais + 116.160,00 + 116.160,00 + Cento e dezesseis mil e cento e sessenta reais + Cento e dezesseis mil e cento e sessenta reais + 20,60 + Vinte inteiros e Sessenta centésimos por cento + 20.60 + 12,00 + Doze por cento + TP + N + N + N + IDENTIFICADOR DA SÉRIE ATO NÃO CONFIGURADA + + + 002 + 09 + UNICA + 001 + Um + 20/12/2022 + 250.000,00 + 250.000,00 + Duzentos e cinquenta mil reais + Duzentos e cinquenta mil reais + 250.000,00 + 250.000,00 + Duzentos e cinquenta mil reais + Duzentos e cinquenta mil reais + 50,32 + Cinquenta inteiros e Trinta e dois centésimos por cento + 50.32 + 12,00 + Doze por cento + TP + N + S + S + IDENTIFICADOR DA SÉRIE UNICA NÃO CONFIGURADA + + + 003 + 08 + FINANCIAMENTO + 001 + Um + 30/03/2023 + 164.000,00 + 164.000,00 + Cento e sessenta e quatro mil reais + Cento e sessenta e quatro mil reais + 168.712,92 + 168.712,92 + Cento e sessenta e quatro mil reais + Cento e sessenta e oito mil e setecentos e doze reais e noventa e dois centavos + 29,08 + Vinte e nove inteiros e Oito décimos por cento + 29.08 + 12,00 + Doze por cento + TP + N + S + S + IDENTIFICADOR DA SÉRIE FINANCIAMENTO NÃO CONFIGURADA + + + + + ATO +R$ 116.160,00 (Cento e dezesseis mil e cento e sessenta reais), correspondente à 20,60% do preço. +Número de parcelas: 1 (uma) parcela com valor nominal nesta data de R$ 116.160,00 (Cento e dezesseis mil e cento e sessenta reais), cujo valor será pago à vista, ou seja, em parcela única por meio de TED ou boleto bancário, ficando a quitação vinculada respectivamente a efetivação da transferência ou comprovação do pagamento do boleto bancário. +Data de vencimento: 03/09/2022 + + + PARCELA DE REFORÇO +R$ 250.000,00 (Duzentos e cinquenta mil reais). +Número de parcela(s): 1 (uma) parcela com valor nominal nesta data de R$ 250.000,00 (Duzentos e cinquenta mil reais). +Data do vencimento da parcela: 20/12/2022. + + + PARCELA CHAVES +R$ 164.000,00 (Cento e sessenta e quatro mil reais). +Número de parcela(s): 1 (uma) parcela com valor nominal nesta data de R$ 164.000,00 (Cento e sessenta e quatro mil reais) +Data do vencimento da parcela: 30/03/2023 + + + + + CPF + 608.037.380-72 + MARCO ANTONIO CIUFFO + 38.805 + 15.228,00 + Quinze mil e duzentos e vinte e oito reais + Não + - + + + + + + + + + CNPJ + 15.361.838/0001-88 + CONFIARE CIA IMOBILIARIA + 23363J + 18.612,00 + Dezoito mil e seiscentos e doze reais + Sim + 90470---470 + BRASIL + RIO GRANDE DO SUL + Porto Alegre + R Sinimbú + 14 + Petrópolis + + + + + 007 + VAGAS RES + 510 + G + 71.170,00 + Setenta e um mil e cento e setenta reais + + + + + + 10.274.663/0001-58 + + + + 0 + + + + + + + + + PORTO ALEGRE + + 22.389.891/0001-52 + MELNICK EVEN CEDRO EMPREENDIMENTO IMOBILIÁRIO LTDA. + + + + RUA CARLOS TREIN FILHO + + AUXILIADORA + 551 + + PORTO ALEGRE + RS + RUA CARLOS TREIN FILHO, 551, AUXILIADORA, PORTO ALEGRE/RS + + + NAO + + PORTO ALEGRE, 13 DE SETEMBRO DE 2022 + 13 DE SETEMBRO DE 2022 + 13/09/2022 + PORTO ALEGRE, 02 DE SETEMBRO DE 2022 + 02 DE SETEMBRO DE 2022 + 02/09/2022 + 351.173 + Trinta e três mil e oitocentos e quarenta reais + 33.840,00 + Dezoito mil e seiscentos e doze reais + 18.612,00 + Quinze mil e duzentos e vinte e oito reais + 15.228,00 + Quinhentos e trinta mil e cento e sessenta reais + 530.160,00 + Quinhentos e sessenta e quatro mil reais + 564.000,00 + Localizado na Torre D, no quarto pavimento, na circulação à direita de quem chega pelos elevadores, sendo o segundo a direita de quem ingressa na dita circulação, com área real privativa de 67,43m², área real de uso comum de divisão não proporcional de 43,51m², e área real total de 110,94m², correspondendo-lhe a fração ideal de 0,001116 no terreno e nas coisas de uso comum e fim proveitoso do condomínio. + RESIDENCIAL D + 402 + 040200000000035117315.361.838/000 + + + ZONA NORTE- TINTAS RENNER + + AV ASSIS BRASIL + 3966 + LINDÓIA + + 91010000 + PORTO ALEGRE + RS + AV ASSIS BRASIL, 3966, LINDÓIA, PORTO ALEGRE/RS, CEP: 91010000 + + + \ No newline at end of file diff --git a/TestFiles/DA240-Whitespace.docx b/TestFiles/DA240-Whitespace.docx new file mode 100644 index 00000000..84f2b228 Binary files /dev/null and b/TestFiles/DA240-Whitespace.docx differ diff --git a/TestFiles/DA240-Whitespace.xml b/TestFiles/DA240-Whitespace.xml new file mode 100644 index 00000000..7c21bac1 --- /dev/null +++ b/TestFiles/DA240-Whitespace.xml @@ -0,0 +1,7 @@ + + + may or may not + / + , + and +