Selaa lähdekoodia

Merge pull request #99 from VictorLoktev/master

Bugfix in Row.Height, removing empty paragraphs in cells, Row.InsertRow keeps formatting
master
PrzemyslawKlys 9 vuotta sitten
vanhempi
commit
b536b5e517

+ 18
- 17
DocX/Container.cs Näytä tiedosto

@@ -474,7 +474,7 @@ namespace Novacode
return uniqueResults.Keys.ToList(); // return the unique list of results
}
public virtual void ReplaceText(string searchValue, string newValue, bool trackChanges = false, RegexOptions options = RegexOptions.None, Formatting newFormatting = null, Formatting matchFormatting = null, MatchFormattingOptions formattingOptions = MatchFormattingOptions.SubsetMatch, bool escapeRegEx = true, bool useRegExSubstitutions = false)
public virtual void ReplaceText(string searchValue, string newValue, bool trackChanges = false, RegexOptions options = RegexOptions.None, Formatting newFormatting = null, Formatting matchFormatting = null, MatchFormattingOptions formattingOptions = MatchFormattingOptions.SubsetMatch, bool escapeRegEx = true, bool useRegExSubstitutions = false, bool removeEmptyParagraph = true)
{
if (string.IsNullOrEmpty(searchValue))
throw new ArgumentException("oldValue cannot be null or empty", "searchValue");
@@ -486,31 +486,32 @@ namespace Novacode
foreach (var header in headerList)
if (header != null)
foreach (var paragraph in header.Paragraphs)
paragraph.ReplaceText(searchValue, newValue, trackChanges, options, newFormatting, matchFormatting, formattingOptions, escapeRegEx, useRegExSubstitutions);
paragraph.ReplaceText(searchValue, newValue, trackChanges, options, newFormatting, matchFormatting, formattingOptions, escapeRegEx, useRegExSubstitutions, removeEmptyParagraph);
// ReplaceText int main body of document.
foreach (var paragraph in Paragraphs)
paragraph.ReplaceText(searchValue, newValue, trackChanges, options, newFormatting, matchFormatting, formattingOptions, escapeRegEx, useRegExSubstitutions);
paragraph.ReplaceText(searchValue, newValue, trackChanges, options, newFormatting, matchFormatting, formattingOptions, escapeRegEx, useRegExSubstitutions, removeEmptyParagraph);
// ReplaceText in Footers of the document.
var footerList = new List<Footer> { Document.Footers.first, Document.Footers.even, Document.Footers.odd };
foreach (var footer in footerList)
if (footer != null)
foreach (var paragraph in footer.Paragraphs)
paragraph.ReplaceText(searchValue, newValue, trackChanges, options, newFormatting, matchFormatting, formattingOptions, escapeRegEx, useRegExSubstitutions);
paragraph.ReplaceText(searchValue, newValue, trackChanges, options, newFormatting, matchFormatting, formattingOptions, escapeRegEx, useRegExSubstitutions, removeEmptyParagraph);
}
/// <summary>
///
/// </summary>
/// <param name="searchValue">Value to find</param>
/// <param name="regexMatchHandler">A Func that accepts the matching regex search group value and passes it to this to return the replacement string</param>
/// <param name="trackChanges">Enable trackchanges</param>
/// <param name="options">Regex options</param>
/// <param name="newFormatting"></param>
/// <param name="matchFormatting"></param>
/// <param name="formattingOptions"></param>
public virtual void ReplaceText(string searchValue, Func<string, string> regexMatchHandler, bool trackChanges = false, RegexOptions options = RegexOptions.None, Formatting newFormatting = null, Formatting matchFormatting = null, MatchFormattingOptions formattingOptions = MatchFormattingOptions.SubsetMatch)
/// <summary>
///
/// </summary>
/// <param name="searchValue">Value to find</param>
/// <param name="regexMatchHandler">A Func that accepts the matching regex search group value and passes it to this to return the replacement string</param>
/// <param name="trackChanges">Enable trackchanges</param>
/// <param name="options">Regex options</param>
/// <param name="newFormatting"></param>
/// <param name="matchFormatting"></param>
/// <param name="formattingOptions"></param>
/// <param name="removeEmptyParagraph">Remove empty paragraph</param>
public virtual void ReplaceText(string searchValue, Func<string, string> regexMatchHandler, bool trackChanges = false, RegexOptions options = RegexOptions.None, Formatting newFormatting = null, Formatting matchFormatting = null, MatchFormattingOptions formattingOptions = MatchFormattingOptions.SubsetMatch, bool removeEmptyParagraph = true)
{
if (string.IsNullOrEmpty(searchValue))
throw new ArgumentException("oldValue cannot be null or empty", "searchValue");
@@ -525,11 +526,11 @@ namespace Novacode
foreach (var container in containerList)
if (container != null)
foreach (var paragraph in container.Paragraphs)
paragraph.ReplaceText(searchValue, regexMatchHandler, trackChanges, options, newFormatting, matchFormatting, formattingOptions);
paragraph.ReplaceText(searchValue, regexMatchHandler, trackChanges, options, newFormatting, matchFormatting, formattingOptions, removeEmptyParagraph);
// ReplaceText int main body of document.
foreach (var paragraph in Paragraphs)
paragraph.ReplaceText(searchValue, regexMatchHandler, trackChanges, options, newFormatting, matchFormatting, formattingOptions);
paragraph.ReplaceText(searchValue, regexMatchHandler, trackChanges, options, newFormatting, matchFormatting, formattingOptions, removeEmptyParagraph);
}
/// <summary>

+ 15
- 12
DocX/Paragraph.cs Näytä tiedosto

@@ -3679,7 +3679,7 @@ namespace Novacode
/// <param name="count">The number of characters to delete</param>
/// <param name="trackChanges">Track changes</param>
/// <param name="removeEmptyParagraph">Remove empty paragraph</param>
public void RemoveText(int index, int count, bool trackChanges = false, bool removeEmptyParagraph=true)
public void RemoveText(int index, int count, bool trackChanges = false, bool removeEmptyParagraph = true)
{
// Timestamp to mark the start of insert
DateTime now = DateTime.Now;
@@ -3759,17 +3759,20 @@ namespace Novacode
}
}
// If after this remove the parent element is empty, remove it.
if (removeEmptyParagraph && GetElementTextLength(parentElement) == 0)
{
if (parentElement.Parent != null && parentElement.Parent.Name.LocalName != "tc")
{
// Need to make sure there is no drawing element within the parent element.
// Picture elements contain no text length but they are still content.
if (parentElement.Descendants(XName.Get("drawing", DocX.w.NamespaceName)).Count() == 0)
parentElement.Remove();
}
}
// Removing of empty paragraph is allowed if text is empty and removeEmptyParagraph=true
bool removeEmpty = removeEmptyParagraph && GetElementTextLength( parentElement ) == 0;
if( parentElement.Parent != null )
{
// Need to make sure there is another paragraph in parent cell
removeEmpty &= parentElement.Parent.Name.LocalName == "tc" &&
parentElement.Parent.Elements( XName.Get( "p", DocX.w.NamespaceName ) ).Count() > 1;
// Need to make sure there is no drawing element within the parent element.
// Picture elements contain no text length but they are still content.
removeEmpty &= parentElement.Descendants( XName.Get( "drawing", DocX.w.NamespaceName ) ).Count() == 0;
}
if( removeEmpty )
parentElement.Remove();
}
while (processed < count);

+ 39
- 21
DocX/Table.cs Näytä tiedosto

@@ -1239,13 +1239,16 @@ namespace Novacode
return InsertRow(RowCount);
}
/// <summary>
/// Insert a copy of a row at the end of this table.
/// </summary>
/// <returns>A new row.</returns>
public Row InsertRow(Row row)
/// <summary>
/// Insert a copy of a row at the end of this table.
/// </summary>
/// <returns>A new row.</returns>
/// <param name="row">The row to insert</param>
/// <param name="keepFormatting">True to clone everithing, False to clone cell structure only.</param>
/// <returns></returns>
public Row InsertRow(Row row, bool keepFormatting = false)
{
return InsertRow(row, RowCount);
return InsertRow(row, RowCount, keepFormatting);
}
/// <summary>
@@ -1482,13 +1485,14 @@ namespace Novacode
return InsertRow(content, index);
}
/// <summary>
/// Insert a copy of a row into this table.
/// </summary>
/// <param name="row">Row to copy and insert.</param>
/// <param name="index">Index to insert row at.</param>
/// <returns>A new Row</returns>
public Row InsertRow(Row row, int index)
/// <summary>
/// Insert a copy of a row into this table.
/// </summary>
/// <param name="row">Row to copy and insert.</param>
/// <param name="index">Index to insert row at.</param>
/// <param name="keepFormatting">True to clone everithing, False to clone cell structure only.</param>
/// <returns>A new Row</returns>
public Row InsertRow(Row row, int index, bool keepFormatting = false)
{
if (row == null)
throw new ArgumentNullException(nameof(row));
@@ -1496,8 +1500,13 @@ namespace Novacode
if (index < 0 || index > RowCount)
throw new IndexOutOfRangeException();
List<XElement> content = row.Xml.Elements(XName.Get("tc", DocX.w.NamespaceName)).Select(element => HelperFunctions.CloneElement(element)).ToList();
return InsertRow(content, index);
List<XElement> content;
if( keepFormatting )
content = row.Xml.Elements().Select(element => HelperFunctions.CloneElement(element)).ToList();
else
content = row.Xml.Elements(XName.Get("tc", DocX.w.NamespaceName)).Select(element => HelperFunctions.CloneElement(element)).ToList();
return InsertRow(content, index);
}
private Row InsertRow(List<XElement> content, Int32 index)
@@ -2545,15 +2554,23 @@ namespace Novacode
XElement trPr = Xml.Element(XName.Get("trPr", DocX.w.NamespaceName));
if (trPr == null)
{
Xml.SetElementValue(XName.Get("trPr", DocX.w.NamespaceName), string.Empty);
trPr = Xml.Element(XName.Get("trPr", DocX.w.NamespaceName));
}
Xml.SetElementValue(XName.Get("trPr", DocX.w.NamespaceName), string.Empty);
trPr = Xml.Element(XName.Get("trPr", DocX.w.NamespaceName));
/*
// Swapping trPr and tc elements - making trPr the first
XElement tc = Xml.Element( XName.Get( "tc", DocX.w.NamespaceName ) );
if( tc != null )
{
trPr.Remove();
tc.AddBeforeSelf( trPr );
}
}
/*
* Get the trHeight element for this Row,
* null will be return if no such element exists.
*/
XElement trHeight = trPr.Element(XName.Get("trHeight", DocX.w.NamespaceName));
XElement trHeight = trPr.Element(XName.Get("trHeight", DocX.w.NamespaceName));
if (trHeight == null)
{
trPr.SetElementValue(XName.Get("trHeight", DocX.w.NamespaceName), string.Empty);
@@ -2564,7 +2581,8 @@ namespace Novacode
trHeight.SetAttributeValue(XName.Get("hRule", DocX.w.NamespaceName), exact ? _hRule_Exact : _hRule_AtLeast);
// 15 "word units" is equal to one pixel.
trHeight.SetAttributeValue(XName.Get("val", DocX.w.NamespaceName), (height * 15).ToString());
trHeight.SetAttributeValue(XName.Get("val", DocX.w.NamespaceName),
((int)(Math.Round(height * 15,0))).ToString( CultureInfo.InvariantCulture )); // national separators anf fraction should be avoided
}
/// <summary>
/// Min-Height in pixels. // Added by Nick Kusters.

+ 8
- 3
DocX/bin/Release/DocX.XML Näytä tiedosto

@@ -370,7 +370,7 @@
<param name="options"></param>
<returns></returns>
</member>
<member name="M:Novacode.Container.ReplaceText(System.String,System.Func{System.String,System.String},System.Boolean,System.Text.RegularExpressions.RegexOptions,Novacode.Formatting,Novacode.Formatting,Novacode.MatchFormattingOptions)">
<member name="M:Novacode.Container.ReplaceText(System.String,System.Func{System.String,System.String},System.Boolean,System.Text.RegularExpressions.RegexOptions,Novacode.Formatting,Novacode.Formatting,Novacode.MatchFormattingOptions,System.Boolean)">
<summary>
</summary>
@@ -381,6 +381,7 @@
<param name="newFormatting"></param>
<param name="matchFormatting"></param>
<param name="formattingOptions"></param>
<param name="removeEmptyParagraph">Remove empty paragraph</param>
</member>
<member name="M:Novacode.Container.RemoveTextInGivenFormat(Novacode.Formatting,Novacode.MatchFormattingOptions)">
<summary>
@@ -988,11 +989,14 @@
</example>
<returns>A new row.</returns>
</member>
<member name="M:Novacode.Table.InsertRow(Novacode.Row)">
<member name="M:Novacode.Table.InsertRow(Novacode.Row,System.Boolean)">
<summary>
Insert a copy of a row at the end of this table.
</summary>
<returns>A new row.</returns>
<param name="row">The row to insert</param>
<param name="keepFormatting">True to clone everithing, False to clone cell structure only.</param>
<returns></returns>
</member>
<member name="M:Novacode.Table.InsertColumn">
<summary>
@@ -1153,12 +1157,13 @@
<param name="index">Index to insert row at.</param>
<returns>A new Row</returns>
</member>
<member name="M:Novacode.Table.InsertRow(Novacode.Row,System.Int32)">
<member name="M:Novacode.Table.InsertRow(Novacode.Row,System.Int32,System.Boolean)">
<summary>
Insert a copy of a row into this table.
</summary>
<param name="row">Row to copy and insert.</param>
<param name="index">Index to insert row at.</param>
<param name="keepFormatting">True to clone everithing, False to clone cell structure only.</param>
<returns>A new Row</returns>
</member>
<member name="M:Novacode.Table.InsertColumn(System.Int32,System.Boolean)">

BIN
DocX/bin/Release/DocX.dll Näytä tiedosto


+ 125
- 1
UnitTests/DocXUnitTests.cs Näytä tiedosto

@@ -150,7 +150,131 @@ namespace UnitTests
}
}
public string ReplaceFunc(string findStr)
/// <summary>
/// TextRemove should not remove empty paragraphs in case the paragraph is alone in the cell.
/// In the rest cases empty paragraph may be removed.
/// </summary>
[Test]
public void Test_Table_Paragraph_RemoveText()
{
using( var input = File.Open( Path.Combine( _directoryWithFiles, "TableSpecifiedHeights.docx" ), FileMode.Open ) )
{
using( var doc = DocX.Load( input ) )
{
// Make sure content of the file is ok for test
Assert.IsTrue( doc.Tables.Count == 1 );
Assert.IsTrue( doc.Tables[ 0 ].RowCount == 3 );
string text = "paragraph";
// == Paragraph in the cell is not alone ==
doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].InsertParagraph( text );
Assert.IsTrue( doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].Paragraphs.Count == 2 );
doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].ReplaceText( text, "", removeEmptyParagraph: true );
Assert.IsTrue( doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].Paragraphs.Count == 1 );
doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].InsertParagraph( text );
Assert.IsTrue( doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].Paragraphs.Count == 2 );
doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].ReplaceText( text, "", removeEmptyParagraph: false );
Assert.IsTrue( doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].Paragraphs.Count == 2 );
// == Paragraph in the cell is alone ==
doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].InsertParagraph( text );
doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].Paragraphs[ 0 ].Remove( false );
doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].Paragraphs[ 0 ].Remove( false );
Assert.IsTrue( doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].Paragraphs.Count == 1 );
doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].ReplaceText( text, "", removeEmptyParagraph: true );
Assert.IsTrue( doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].Paragraphs.Count == 1 );
doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].InsertParagraph( text );
Assert.IsTrue( doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].Paragraphs.Count == 2 );
doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].ReplaceText( text, "", removeEmptyParagraph: false );
Assert.IsTrue( doc.Tables[ 0 ].Rows[ 0 ].Cells[ 0 ].Paragraphs.Count == 2 );
}
}
}
[Test]
public void Test_Table_MinHeight()
{
using( var input = File.Open( Path.Combine( _directoryWithFiles, "TableSpecifiedHeights.docx" ), FileMode.Open ) )
{
using( var doc = DocX.Load( input ) )
{
// Make sure content of the file is ok for test
Assert.IsTrue( doc.Tables.Count == 1 );
Assert.IsTrue( doc.Tables[ 0 ].RowCount == 3 );
// Check heights load is ok
Assert.IsTrue( double.IsNaN( doc.Tables[ 0 ].Rows[ 0 ].Height ) );
Assert.IsTrue( double.IsNaN( doc.Tables[ 0 ].Rows[ 0 ].MinHeight ) );
Assert.IsTrue( Math.Abs( doc.Tables[ 0 ].Rows[ 1 ].Height - 37.8f ) < 0.0001f );
Assert.IsTrue( Math.Abs( doc.Tables[ 0 ].Rows[ 1 ].MinHeight - 37.8f ) < 0.0001f );
Assert.IsTrue( Math.Abs( doc.Tables[ 0 ].Rows[ 2 ].Height - 37.8f ) < 0.0001f );
Assert.IsTrue( Math.Abs( doc.Tables[ 0 ].Rows[ 2 ].MinHeight - 37.8f ) < 0.0001f );
// Set MinHeight
doc.Tables[ 0 ].Rows[ 0 ].MinHeight = 37.8f;
Assert.IsTrue( Math.Abs( doc.Tables[ 0 ].Rows[ 0 ].Height - 37.8f ) < 0.0001f );
Assert.IsTrue( Math.Abs( doc.Tables[ 0 ].Rows[ 0 ].MinHeight - 37.8f ) < 0.0001f );
}
}
}
[Test]
public void Test_Table_InsertRow_Keeps_Formatting()
{
using( var input = File.Open( Path.Combine( _directoryWithFiles, "TableSpecifiedHeights.docx" ), FileMode.Open ) )
{
using( var doc = DocX.Load( input ) )
{
// Make sure content of the file is ok for test
Assert.IsTrue( doc.Tables.Count == 1 );
Assert.IsTrue( doc.Tables[ 0 ].RowCount == 3 );
// Check heights load is ok
Assert.IsTrue( double.IsNaN( doc.Tables[ 0 ].Rows[ 0 ].Height ) );
Assert.IsTrue( double.IsNaN( doc.Tables[ 0 ].Rows[ 0 ].MinHeight ) );
Assert.IsTrue( Math.Abs( doc.Tables[ 0 ].Rows[ 1 ].Height - 37.8f ) < 0.0001f );
Assert.IsTrue( Math.Abs( doc.Tables[ 0 ].Rows[ 1 ].MinHeight - 37.8f ) < 0.0001f );
Assert.IsTrue( Math.Abs( doc.Tables[ 0 ].Rows[ 2 ].Height - 37.8f ) < 0.0001f );
Assert.IsTrue( Math.Abs( doc.Tables[ 0 ].Rows[ 2 ].MinHeight - 37.8f ) < 0.0001f );
// Clone all rows and check heights
int n = doc.Tables[ 0 ].RowCount;
for( int index = 0; index < n; index++ )
{
doc.Tables[ 0 ].InsertRow( doc.Tables[ 0 ].Rows[ index ], true );
}
Assert.IsTrue( doc.Tables[ 0 ].RowCount == 2 * n );
for( int index = 0; index < n; index++ )
{
// Compare height of original row and cloned
Assert.IsTrue( double.IsNaN( doc.Tables[ 0 ].Rows[ n + index ].Height ) == double.IsNaN( doc.Tables[ 0 ].Rows[ index ].Height ) );
if( !double.IsNaN( doc.Tables[ 0 ].Rows[ n + index ].Height ) && !double.IsNaN( doc.Tables[ 0 ].Rows[ index ].Height ) )
Assert.IsTrue( Math.Abs( doc.Tables[ 0 ].Rows[ n + index ].Height - doc.Tables[ 0 ].Rows[ index ].Height ) < 0.0001f );
Assert.IsTrue( double.IsNaN( doc.Tables[ 0 ].Rows[ n + index ].MinHeight ) == double.IsNaN( doc.Tables[ 0 ].Rows[ index ].MinHeight ) );
if( !double.IsNaN( doc.Tables[ 0 ].Rows[ n + index ].MinHeight ) && !double.IsNaN( doc.Tables[ 0 ].Rows[ index ].MinHeight ) )
Assert.IsTrue( Math.Abs( doc.Tables[ 0 ].Rows[ n + index ].MinHeight - doc.Tables[ 0 ].Rows[ index ].MinHeight ) < 0.0001f );
}
// Remove original rows
for( int index = 0; index < n; index++ )
{
doc.Tables[ 0 ].Rows[ 0 ].Remove();
}
// At this point we shuold have document visually equal to original
doc.SaveAs( Path.Combine( _directoryWithFiles, "TableSpecifiedHeights_out.docx" ) );
}
}
}
public string ReplaceFunc(string findStr)
{
var testPatterns = new Dictionary<string, string>
{

+ 3
- 0
UnitTests/UnitTests.csproj Näytä tiedosto

@@ -109,6 +109,9 @@
<None Include="documents\Tables.docx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="documents\TableSpecifiedHeights.docx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="documents\TableSpecifiedWidths.docx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

BIN
UnitTests/documents/TableSpecifiedHeights.docx Näytä tiedosto


Loading…
Peruuta
Tallenna