diff --git a/Xceed.Document.NET/AssemblyVersionInfo.cs b/Xceed.Document.NET/AssemblyVersionInfo.cs index 734fbe34..2913e77d 100644 --- a/Xceed.Document.NET/AssemblyVersionInfo.cs +++ b/Xceed.Document.NET/AssemblyVersionInfo.cs @@ -21,7 +21,7 @@ COMMUNITY LICENSE AGREEMENT (for non-commercial use) as published at internal static class _XceedVersionInfo { [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields" )] - public const string BaseVersion = "1.8"; + public const string BaseVersion = "2.0"; [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields" )] public const string Version = BaseVersion + ".0.0"; diff --git a/Xceed.Document.NET/Properties/AssemblyInfo.cs b/Xceed.Document.NET/Properties/AssemblyInfo.cs index 4b74702a..747d5027 100644 --- a/Xceed.Document.NET/Properties/AssemblyInfo.cs +++ b/Xceed.Document.NET/Properties/AssemblyInfo.cs @@ -23,10 +23,10 @@ COMMUNITY LICENSE AGREEMENT (for non-commercial use) as published at // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle( "Xceed Document for .NET" )] -[assembly: AssemblyDescription( "This assembly implements the classes for Xceed Document for .NET Standard." )] +[assembly: AssemblyDescription( "This assembly implements the classes for Xceed Document for .NET." )] [assembly: AssemblyCompany( "Xceed Software Inc." )] -[assembly: AssemblyProduct( "Xceed Document for .NET Standard" )] +[assembly: AssemblyProduct( "Xceed Document for .NET" )] [assembly: AssemblyCopyright( "Copyright (C) Xceed Software Inc. 2009-2021" )] [assembly: AssemblyCulture("")] @@ -52,6 +52,13 @@ COMMUNITY LICENSE AGREEMENT (for non-commercial use) as published at "131b925dcf84a73d22264352eca7c3fcf9387f3eee1d60ac7974f04866e6c72928dc0609abe341" + "f92cbfb5")] +[assembly: InternalsVisibleTo( "Xceed.Words.NET5" + ",PublicKey=" + + "0024000004800000940000000602000000240000525341310004000001000100d59d8147eb2015" + + "ca98a92da860fd766d101271d8c2f545894870fd6183255737d79347bbf5250291ae75651e1150" + + "1b7452ee003b80b936614cdda51db8eb6f8fde913e67d45395b480a992be17bf04744a7fe803ea" + + "131b925dcf84a73d22264352eca7c3fcf9387f3eee1d60ac7974f04866e6c72928dc0609abe341" + + "f92cbfb5")] + [assembly: InternalsVisibleTo( "Xceed.PdfCreator.NET" + ",PublicKey=" + "0024000004800000940000000602000000240000525341310004000001000100d59d8147eb2015" + "ca98a92da860fd766d101271d8c2f545894870fd6183255737d79347bbf5250291ae75651e1150" + @@ -66,6 +73,13 @@ COMMUNITY LICENSE AGREEMENT (for non-commercial use) as published at "131b925dcf84a73d22264352eca7c3fcf9387f3eee1d60ac7974f04866e6c72928dc0609abe341" + "f92cbfb5")] +[assembly: InternalsVisibleTo( "Xceed.PdfCreator.NET5" + ",PublicKey=" + + "0024000004800000940000000602000000240000525341310004000001000100d59d8147eb2015" + + "ca98a92da860fd766d101271d8c2f545894870fd6183255737d79347bbf5250291ae75651e1150" + + "1b7452ee003b80b936614cdda51db8eb6f8fde913e67d45395b480a992be17bf04744a7fe803ea" + + "131b925dcf84a73d22264352eca7c3fcf9387f3eee1d60ac7974f04866e6c72928dc0609abe341" + + "f92cbfb5")] + [assembly: InternalsVisibleTo( "UnitTests" + ",PublicKey=" + "0024000004800000940000000602000000240000525341310004000001000100d59d8147eb2015" + "ca98a92da860fd766d101271d8c2f545894870fd6183255737d79347bbf5250291ae75651e1150" + @@ -79,6 +93,8 @@ COMMUNITY LICENSE AGREEMENT (for non-commercial use) as published at + + #pragma warning disable 1699 [assembly: AssemblyDelaySign( false )] [assembly: AssemblyKeyFile( @"..\..\sn.snk" )] diff --git a/Xceed.Document.NET/Resources/SignatureLine.emf b/Xceed.Document.NET/Resources/SignatureLine.emf new file mode 100644 index 00000000..3625a9d3 Binary files /dev/null and b/Xceed.Document.NET/Resources/SignatureLine.emf differ diff --git a/Xceed.Document.NET/Resources/default_styles.xml.gz b/Xceed.Document.NET/Resources/default_styles.xml.gz index b1c1a471..4502bac5 100644 Binary files a/Xceed.Document.NET/Resources/default_styles.xml.gz and b/Xceed.Document.NET/Resources/default_styles.xml.gz differ diff --git a/Xceed.Document.NET/Src/Bookmark.cs b/Xceed.Document.NET/Src/Bookmark.cs index 7c2ce164..e266915a 100644 --- a/Xceed.Document.NET/Src/Bookmark.cs +++ b/Xceed.Document.NET/Src/Bookmark.cs @@ -24,6 +24,7 @@ public string Name { get; set; } + public Paragraph Paragraph { get; set; diff --git a/Xceed.Document.NET/Src/Charts/Axis.cs b/Xceed.Document.NET/Src/Charts/Axis.cs index c46fd2d8..726216fa 100644 --- a/Xceed.Document.NET/Src/Charts/Axis.cs +++ b/Xceed.Document.NET/Src/Charts/Axis.cs @@ -15,6 +15,9 @@ COMMUNITY LICENSE AGREEMENT (for non-commercial use) as published at using System; +using System.ComponentModel; +using System.Linq; +using System.Xml; using System.Xml.Linq; namespace Xceed.Document.NET @@ -25,12 +28,19 @@ namespace Xceed.Document.NET /// public abstract class Axis { + + #region Private properties + + + #endregion + + #region Public Properties /// /// ID of this Axis /// - public String Id + public string Id { get { @@ -41,7 +51,7 @@ public String Id /// /// Return true if this axis is visible /// - public Boolean IsVisible + public bool IsVisible { get { @@ -60,7 +70,25 @@ public Boolean IsVisible -#endregion + + + + + + #endregion + + + + + + + + + + + + + #region Internal Properties diff --git a/Xceed.Document.NET/Src/Charts/Chart.cs b/Xceed.Document.NET/Src/Charts/Chart.cs index b88a4ffe..c9ce3f52 100644 --- a/Xceed.Document.NET/Src/Charts/Chart.cs +++ b/Xceed.Document.NET/Src/Charts/Chart.cs @@ -33,6 +33,9 @@ public abstract class Chart { #region Private Members + private Paragraph _parentParagraph; + private PackageRelationship _packageRelationship; + #endregion @@ -54,13 +57,10 @@ public List Series get { var series = new List(); - var ser = XName.Get( "ser", Document.c.NamespaceName ); - int index = 1; - foreach( var element in ChartXml.Elements( ser ) ) + var ser = ChartXml.Elements( XName.Get( "ser", Document.c.NamespaceName ) ); + foreach( var element in ser ) { - element.Add( new XElement( XName.Get( "idx", Document.c.NamespaceName ) ), index.ToString() ); series.Add( new Series( element ) ); - ++index; } return series; } @@ -91,7 +91,12 @@ public ChartLegend Legend /// public CategoryAxis CategoryAxis { - get; private set; + get + { + var catAxXML = ChartRootXml.Descendants( XName.Get( "catAx", Document.c.NamespaceName ) ).SingleOrDefault(); + + return ( catAxXML != null ) ? new CategoryAxis( catAxXML ) : null; + } } /// @@ -99,7 +104,12 @@ public CategoryAxis CategoryAxis /// public ValueAxis ValueAxis { - get; private set; + get + { + var valAxXML = ChartRootXml.Descendants( XName.Get( "valAx", Document.c.NamespaceName ) ).SingleOrDefault(); + + return ( valAxXML != null ) ? new ValueAxis( valAxXML ) : null; + } } /// @@ -220,11 +230,11 @@ public Chart() // if axes exists, create their if( this.IsAxisExist ) { - this.CategoryAxis = new CategoryAxis( "148921728" ); - this.ValueAxis = new ValueAxis( "154227840" ); + var categoryAxis = new CategoryAxis( "148921728" ); + var valueAxis = new ValueAxis( "154227840" ); - var axIDcatXml = XElement.Parse( String.Format( @"", this.CategoryAxis.Id ) ); - var axIDvalXml = XElement.Parse( String.Format( @"", this.ValueAxis.Id ) ); + var axIDcatXml = XElement.Parse( String.Format( @"", categoryAxis.Id ) ); + var axIDvalXml = XElement.Parse( String.Format( @"", valueAxis.Id ) ); var gapWidth = this.ChartXml.Element( XName.Get( "gapWidth", Document.c.NamespaceName ) ); if( gapWidth != null ) @@ -238,8 +248,8 @@ public Chart() this.ChartXml.Add( axIDvalXml ); } - plotAreaXml.Add( this.CategoryAxis.Xml ); - plotAreaXml.Add( this.ValueAxis.Xml ); + plotAreaXml.Add( categoryAxis.Xml ); + plotAreaXml.Add( valueAxis.Xml ); } this.ChartRootXml = this.Xml.Root.Element( XName.Get( "chart", Document.c.NamespaceName ) ); @@ -301,6 +311,33 @@ public void RemoveLegend() } } + public void Remove() + { + if( _packageRelationship.Package != null ) + { + _packageRelationship.Package.DeletePart( _packageRelationship.TargetUri ); + } + + if( _parentParagraph.Document.PackagePart != null ) + { + _parentParagraph.Document.PackagePart.DeleteRelationship( _packageRelationship.Id ); + } + + // Remove the Xml from document. + var parentParagrahChart = _parentParagraph.Xml.Descendants( XName.Get( "chart", Document.c.NamespaceName ) ) + .FirstOrDefault( c => c.GetAttribute( XName.Get( "id", "http://schemas.openxmlformats.org/officeDocument/2006/relationships" ) ) == _packageRelationship.Id ); + if( parentParagrahChart != null ) + { + var parentDrawing = parentParagrahChart.Ancestors( XName.Get( "drawing", Document.w.NamespaceName ) ).FirstOrDefault(); + if( parentDrawing != null ) + { + parentDrawing.Remove(); + } + } + } + + + #endregion @@ -529,10 +566,10 @@ public void Bind( IList categories, IList values ) for( int index = 0; index < categories.Count; index++ ) { pt = new XElement( XName.Get( "pt", Document.c.NamespaceName ), new XAttribute( XName.Get( "idx" ), index ), - new XElement( XName.Get( "v", Document.c.NamespaceName ), categories[ index ].ToString() ) ); + new XElement( XName.Get( "v", Document.c.NamespaceName ), categories[index].ToString() ) ); _strCache.Add( pt ); pt = new XElement( XName.Get( "pt", Document.c.NamespaceName ), new XAttribute( XName.Get( "idx" ), index ), - new XElement( XName.Get( "v", Document.c.NamespaceName ), values[ index ].ToString() ) ); + new XElement( XName.Get( "v", Document.c.NamespaceName ), values[index].ToString() ) ); _numCache.Add( pt ); } } diff --git a/Xceed.Document.NET/Src/Container.cs b/Xceed.Document.NET/Src/Container.cs index ca45391f..1b20617f 100644 --- a/Xceed.Document.NET/Src/Container.cs +++ b/Xceed.Document.NET/Src/Container.cs @@ -179,7 +179,12 @@ public virtual List Lists } } } - lists.Add( list ); + + if( list.Items.Count > 0 ) + { + lists.Add( list ); + } + return lists; } } @@ -1028,10 +1033,18 @@ private void InitParagraphs( List paragraphs ) foreach( var p in paragraphs ) { var nextElement = p.Xml.ElementsAfterSelf().FirstOrDefault(); - if( ( nextElement == null ) && p.IsInSdt() ) + if ((nextElement == null) && p.IsInSdt()) { nextElement = p.GetParentSdt().ElementsAfterSelf().FirstOrDefault(); } + else if ((nextElement != null) && nextElement.Name.Equals(Document.w + "sdt")) + { + var sdtContent = nextElement.Element(XName.Get("sdtContent", Document.w.NamespaceName)); + if (sdtContent != null) + { + nextElement = sdtContent.Element(XName.Get("tbl", Document.w.NamespaceName)); + } + } var containsSectionBreak = p.GetOrCreate_pPr().Element( XName.Get( "sectPr", Document.w.NamespaceName ) ); // Add FollowingTable to paragraph....only when paragraph is not the last one from a section. while( ( nextElement != null ) && ( nextElement.Name.Equals( Document.w + "tbl" ) ) && ( containsSectionBreak == null ) ) diff --git a/Xceed.Document.NET/Src/CustomProperty.cs b/Xceed.Document.NET/Src/CustomProperty.cs index b65acdb9..d2185c08 100644 --- a/Xceed.Document.NET/Src/CustomProperty.cs +++ b/Xceed.Document.NET/Src/CustomProperty.cs @@ -141,7 +141,9 @@ internal CustomProperty( string name, string type, string value, Formatting form case "bool": { - realValue = bool.Parse( value ); + realValue = ( value == "0" ) + ? false + : ( value == "1" ) ? true : bool.Parse( value ); break; } diff --git a/Xceed.Document.NET/Src/Document.cs b/Xceed.Document.NET/Src/Document.cs index 4c9b7bf8..f917c117 100644 --- a/Xceed.Document.NET/Src/Document.cs +++ b/Xceed.Document.NET/Src/Document.cs @@ -29,8 +29,9 @@ COMMUNITY LICENSE AGREEMENT (for non-commercial use) as published at using System.Net; using System.Diagnostics; using System.Drawing.Drawing2D; -using System.Resources; -using System.Globalization; +using System.Reflection; +using System.ComponentModel; + namespace Xceed.Document.NET { @@ -66,6 +67,11 @@ public class Document : Container, IDisposable static internal XNamespace mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"; static internal XNamespace wps = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape"; static internal XNamespace w14 = "http://schemas.microsoft.com/office/word/2010/wordml"; + static internal XNamespace w15 = "http://schemas.microsoft.com/office/word/2012/wordml"; + static internal XNamespace o = "urn:schemas-microsoft-com:office:office"; + static internal XNamespace d = "http://www.w3.org/2000/09/xmldsig#"; + static internal XNamespace doffice = "http://schemas.microsoft.com/office/2006/digsig"; + static internal XNamespace w10 = "urn:schemas-microsoft-com:office:word"; #endregion #region Private Members @@ -148,6 +154,48 @@ public override IList
Sections } } + public NoteProperties EndnoteProperties + { + get + { + if( this.Sections.Count > 1 ) + { + Debug.WriteLine( "This document contains more than 1 section. Consider using Sections[wantedSection].EndnoteProperties." ); + } + return this.Sections[ 0 ].EndnoteProperties; + } + + set + { + if( this.Sections.Count > 1 ) + { + Debug.WriteLine( "This document contains more than 1 section. Consider using Sections[wantedSection].EndnoteProperties." ); + } + this.Sections[ 0 ].EndnoteProperties = value; + } + } + + public NoteProperties FootnoteProperties + { + get + { + if( this.Sections.Count > 1 ) + { + Debug.WriteLine( "This document contains more than 1 section. Consider using Sections[wantedSection].FootnoteProperties." ); + } + return this.Sections[ 0 ].FootnoteProperties; + } + + set + { + if( this.Sections.Count > 1 ) + { + Debug.WriteLine( "This document contains more than 1 section. Consider using Sections[wantedSection].FootnoteProperties." ); + } + this.Sections[ 0 ].FootnoteProperties = value; + } + } + public float MarginTop { get @@ -896,13 +944,13 @@ public BookmarkCollection Bookmarks var bookmarks = new BookmarkCollection(); // In Body. // Faster to search the document.Xml instead of document.Paragraphs. - var documentBookmarkStartsXml = this.Xml.Descendants(XName.Get("bookmarkStart", Document.w.NamespaceName)); - foreach (var bookmarkStartXml in documentBookmarkStartsXml ) + var documentBookmarkStartsXml = this.Xml.Descendants( XName.Get( "bookmarkStart", Document.w.NamespaceName ) ); + foreach( var bookmarkStartXml in documentBookmarkStartsXml ) { var bookmarkName = bookmarkStartXml.Attribute( XName.Get( "name", Document.w.NamespaceName ) ).Value; var paragraphXml = bookmarkStartXml.Parent; - while ((paragraphXml != null) && (paragraphXml.Name != XName.Get("p", Document.w.NamespaceName))) + while( ( paragraphXml != null ) && ( paragraphXml.Name != XName.Get( "p", Document.w.NamespaceName ) ) ) { paragraphXml = paragraphXml.Parent; } @@ -922,75 +970,75 @@ public BookmarkCollection Bookmarks while( ( paragraphXml != null ) && ( paragraphXml.Name != XName.Get( "p", Document.w.NamespaceName ) ) ) { paragraphXml = paragraphXml.Parent; - } + } // bookmarkStart is not in a paragraph, get bookmarkEnd. if( paragraphXml == null ) throw new InvalidDataException( "bookmarkStart and bookmarkEnd are not part of a paragraph. This is currently not supported." ); } - bookmarks.Add(new Bookmark + bookmarks.Add( new Bookmark { Name = bookmarkName, Id = bookmarkStartXml.Attribute( XName.Get( "id", Document.w.NamespaceName ) ).Value, - Paragraph = new Paragraph(this, paragraphXml, -1) { PackagePart = this.PackagePart } - }); + Paragraph = new Paragraph( this, paragraphXml, -1 ) { PackagePart = this.PackagePart } + } ); } - foreach (var section in this.Sections) + foreach( var section in this.Sections ) { // In Headers. var headers = section.Headers; - if (headers != null) + if( headers != null ) { - if (headers.Odd != null) + if( headers.Odd != null ) { - foreach (var paragraph in headers.Odd.Paragraphs) + foreach( var paragraph in headers.Odd.Paragraphs ) { - bookmarks.AddRange(paragraph.GetBookmarks()); + bookmarks.AddRange( paragraph.GetBookmarks() ); } } - if (headers.Even != null) + if( headers.Even != null ) { - foreach (var paragraph in headers.Even.Paragraphs) + foreach( var paragraph in headers.Even.Paragraphs ) { - bookmarks.AddRange(paragraph.GetBookmarks()); + bookmarks.AddRange( paragraph.GetBookmarks() ); } } - if (headers.First != null) + if( headers.First != null ) { - foreach (var paragraph in headers.First.Paragraphs) + foreach( var paragraph in headers.First.Paragraphs ) { - bookmarks.AddRange(paragraph.GetBookmarks()); + bookmarks.AddRange( paragraph.GetBookmarks() ); } } } // In Footers. var footers = section.Footers; - if (footers != null) + if( footers != null ) { - if (footers.Odd != null) + if( footers.Odd != null ) { - foreach (var paragraph in footers.Odd.Paragraphs) + foreach( var paragraph in footers.Odd.Paragraphs ) { - bookmarks.AddRange(paragraph.GetBookmarks()); + bookmarks.AddRange( paragraph.GetBookmarks() ); } } - if (footers.Even != null) + if( footers.Even != null ) { - foreach (var paragraph in footers.Even.Paragraphs) + foreach( var paragraph in footers.Even.Paragraphs ) { - bookmarks.AddRange(paragraph.GetBookmarks()); + bookmarks.AddRange( paragraph.GetBookmarks() ); } } - if (footers.First != null) + if( footers.First != null ) { - foreach (var paragraph in footers.First.Paragraphs) + foreach( var paragraph in footers.First.Paragraphs ) { - bookmarks.AddRange(paragraph.GetBookmarks()); + bookmarks.AddRange( paragraph.GetBookmarks() ); } } } @@ -1022,13 +1070,11 @@ public BookmarkCollection Bookmarks + #endregion + #region Public Methods - - - #endregion - #region Public Methods public override Section InsertSection( bool trackChanges ) { @@ -1849,6 +1895,172 @@ public Table AddTable( int rowCount, int columnCount ) + public Footnote AddFootnote( object footnoteContent, Formatting formatting = null ) + { + if( footnoteContent == null ) + return null; + + var footnotes_package_uri = new Uri( "/word/footnotes.xml", UriKind.Relative ); + + if( !this.Document._package.PartExists( footnotes_package_uri ) ) + { + var mainDocPart = HelperFunctions.GetMainDocumentPart( this.Document._package ); + mainDocPart.CreateRelationship( footnotes_package_uri, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes" ); + + // Create default footnotes in _footnotes. + this.Document._footnotes = XDocument.Parse + ( @" + + + + + + + + + + + + + + + + + + + + + + " + ); + + // Create Footnotes package. + var footnotes_package = this.Document._package.CreatePart( footnotes_package_uri, "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml", CompressionOption.Maximum ); + this.Document._footnotesPart = footnotes_package; + } + + var newFootnoteId = this.GetMaxId( this.Document._footnotes.Root.LastNode as XElement ); + if( newFootnoteId < 0 ) + return null; + + // Create new footnote. + var newFootnoteXml = XElement.Parse + ( string.Format( @" + ", newFootnoteId ) + ); + + // Create new footnote paragraph. + var newFootnoteParagraphXml = XElement.Parse( + @" + + + + + + + + + + + + + " ); + var newFootnoteParagraph = new Paragraph( this, newFootnoteParagraphXml, 0 ) { PackagePart = _footnotesPart }; + + this.UpdateNoteXmlFromContent( newFootnoteXml, newFootnoteParagraph, footnoteContent, formatting ); + + // Create new footnote from updated newFootnoteXml. + var newFootnote = new Footnote( this, _footnotesPart, newFootnoteXml ); + + // Add newFootnote in _footnotes. + this.Document._footnotes.Root.Add( newFootnote.Xml ); + + return newFootnote; + } + + public Endnote AddEndnote( object endnoteContent, Formatting formatting = null ) + { + if( endnoteContent == null ) + return null; + + var endnotes_package_uri = new Uri( "/word/endnotes.xml", UriKind.Relative ); + + if( !this.Document._package.PartExists( endnotes_package_uri ) ) + { + var mainDocPart = HelperFunctions.GetMainDocumentPart( this.Document._package ); + mainDocPart.CreateRelationship( endnotes_package_uri, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes" ); + + // Create default endnotes in _endnotes. + this.Document._endnotes = XDocument.Parse + ( @" + + + + + + + + + + + + + + + + + + + + + + " + ); + + // Create Endnotes package. + var endnotes_package = this.Document._package.CreatePart( endnotes_package_uri, "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml", CompressionOption.Maximum ); + this.Document._endnotesPart = endnotes_package; + } + + var newEndnoteId = this.GetMaxId( this.Document._endnotes.Root.LastNode as XElement ); + if( newEndnoteId < 0 ) + return null; + + // Create new endnote. + var newEndnoteXml = XElement.Parse + ( string.Format( @" + ", newEndnoteId ) + ); + + // Create new endnote paragraph. + var newEndnoteParagraphXml = XElement.Parse( + @" + + + + + + + + + + + + + " ); + var newEndnoteParagraph = new Paragraph( this, newEndnoteParagraphXml, 0 ) { PackagePart = _endnotesPart }; + + this.UpdateNoteXmlFromContent( newEndnoteXml, newEndnoteParagraph, endnoteContent, formatting ); + + // Create new endnote from updated newEndnoteXml. + var newEndnote = new Endnote( this, _endnotesPart, newEndnoteXml ); + + // Add newEndnote in _endnotes. + this.Document._endnotes.Root.Add( newEndnote.Xml ); + + return newEndnote; + } + /// /// Applies document template to the document. Document template may include styles, headers, footers, properties, etc. as well as text content. /// @@ -2009,7 +2221,6 @@ public void ApplyTemplate( Stream templateStream, bool includeContent ) } finally { - _package.Flush(); templatePackage.Close(); PopulateDocument( Document, _package ); } @@ -2607,7 +2818,19 @@ public void InsertChartAfterParagraph( Chart chart, Paragraph paragraph, float w ///
public List AddList( string listText = null, int level = 0, ListItemType listType = ListItemType.Numbered, int? startNumber = null, bool trackChanges = false, bool continueNumbering = false, Formatting formatting = null ) { - return AddListItem( new List( this, null ), listText, level, listType, startNumber, trackChanges, continueNumbering, formatting ); + if( startNumber.HasValue && continueNumbering ) + throw new InvalidOperationException( "Cannot specify a start number and at the same time continue numbering from another list" ); + + var list = new List( this, null ); + var result = HelperFunctions.CreateItemInList( list, listText, level, listType, startNumber, trackChanges, continueNumbering, formatting ); + var lastItem = result.Items.LastOrDefault(); + + if( lastItem != null ) + { + lastItem.PackagePart = this.PackagePart; + } + + return result; } @@ -2616,10 +2839,20 @@ public List AddList( string listText = null, int level = 0, ListItemType listTyp + + + /// /// Add a list item to an existing list /// - public List AddListItem( List list, string listText, int level = 0, ListItemType listType = ListItemType.Numbered, int? startNumber = null, bool trackChanges = false, bool continueNumbering = false, Formatting formatting = null ) + public List AddListItem( List list, + string listText, + int level = 0, + ListItemType listType = ListItemType.Numbered, + int? startNumber = null, + bool trackChanges = false, + bool continueNumbering = false, + Formatting formatting = null ) { if( startNumber.HasValue && continueNumbering ) throw new InvalidOperationException( "Cannot specify a start number and at the same time continue numbering from another list" ); @@ -2662,7 +2895,7 @@ public override List InsertList( List list, double fontSize ) /// Insert a list at an index location in the document /// /// Index in document to insert the list. - /// The list that was inserted into the document. + /// The list that was inserted into the document. /// public new List InsertList( int index, List list ) { @@ -2859,6 +3092,8 @@ public void SetDefaultFont( Font fontFamily, double fontSize = 11d, Color? fontC + + #endregion #region Internal Methods @@ -2867,6 +3102,11 @@ protected internal virtual void SaveHeadersFooters() { } + + + + + internal XDocument GetSettings() { if( _settings != null ) @@ -3010,6 +3250,13 @@ internal static Document PostLoad( ref Package package, Document document, Docum } } + var docType = document._mainDoc.DocumentType; + if( ( docType != null ) && !string.IsNullOrEmpty( docType.InternalSubset ) ) + { + if( docType.InternalSubset.Contains( "ENTITY" ) ) + throw new InvalidDataException( "Document to load contains external entities which are not supported" ); + } + return document; } @@ -3035,6 +3282,7 @@ internal static Document Load( Stream stream, Document document, DocumentTypes d document._package = package; document._memoryStream = ms; document._stream = stream; + return document; } @@ -3056,6 +3304,7 @@ internal static Document Load( string filename, Document document, DocumentTypes Stream receiveStream = null; try { + ServicePointManager.SecurityProtocol = ( SecurityProtocolType )3072; request = ( HttpWebRequest )WebRequest.Create( filename ); response = ( HttpWebResponse )request.GetResponse(); receiveStream = response.GetResponseStream(); @@ -3344,6 +3593,44 @@ internal Image AddImage( object o, string contentType = "image/jpeg" ) } } + internal Image GetImage( Stream stream ) + { + if( stream == null ) + return null; + + stream.Position = 0; + + var imageParts = new Dictionary(); + var partLookup = _package.GetParts().ToDictionary( x => x.Uri.ToString(), x => x, StringComparer.Ordinal ); + + // all image relationships. + var relationshipImages = this.PackagePart.GetRelationshipsByType( RelationshipImage ); + // take all used images (from relationships) + foreach( var item in relationshipImages ) + { + var targetUri = item.TargetUri.ToString(); + PackagePart part; + if( partLookup.TryGetValue( targetUri, out part ) ) + { + // all document's used image parts. + imageParts.Add( item, part ); + } + } + + foreach( var entry in imageParts ) + { + // Get the image object for this image part. + using( var tempStream = entry.Value.GetStream( FileMode.Open, FileAccess.Read ) ) + { + // Compare this image to the new image being added. + if( HelperFunctions.IsSameFile( tempStream, stream ) ) + return new Image( this, entry.Key ); + } + } + + return null; + } + internal static void UpdateCorePropertyValue( Document document, string corePropertyName, string corePropertyValue ) { var matchPattern = string.Format( @"(DOCPROPERTY)?{0}\\\*MERGEFORMAT", corePropertyName ).ToLower(); @@ -3488,10 +3775,23 @@ internal static void UpdateCustomPropertyValue( Document document, CustomPropert foreach( XElement doc in documents ) { #region Word 2010+ - foreach( XElement e in doc.Descendants( XName.Get( "instrText", w.NamespaceName ) ) ) + var instrTexts = doc.Descendants( XName.Get( "instrText", w.NamespaceName ) ); + for( int i = 0; i < instrTexts.Count(); ++i ) { + var e = instrTexts.ElementAt( i ); var attr_value = e.Value.Replace( " ", string.Empty ).Trim(); + if( attr_value.StartsWith( "DOCPROPERTY" ) && !attr_value.EndsWith( "MERGEFORMAT" ) && ( i < instrTexts.Count() - 1 ) ) + { + var nextInstrText = instrTexts.ElementAt( i + 1 ); + var nextAttr_value = nextInstrText.Value.Replace( " ", string.Empty ).Trim(); + if( !nextAttr_value.StartsWith( "DOCPROP" ) && nextAttr_value.EndsWith( "FORMAT" ) ) + { + attr_value += nextAttr_value; + i++; + } + } + if( attr_value.Equals( match_value, StringComparison.CurrentCultureIgnoreCase ) ) { var node = e.Parent.NextNode; @@ -4506,6 +4806,20 @@ private void merge_styles( PackagePart remote_pp, PackagePart local_pp, XDocumen + + + + + + + + + + + + + + protected void clonePackageRelationship( Document remote_document, PackagePart pp, XDocument remote_mainDoc ) @@ -4874,6 +5188,53 @@ private void InsertChart( Chart chart, Paragraph paragraph, float width = 432f, p.Xml.Add( chartElement ); } + private void UpdateNoteXmlFromContent( XElement noteXml, Paragraph noteParagraph, object footnoteContent, Formatting formatting ) + { + Debug.Assert( noteXml != null, "noteXml should not be null." ); + Debug.Assert( noteParagraph != null, "noteParagraph should not be null." ); + + if( footnoteContent is string ) + { + noteParagraph.InsertText( footnoteContent as string, false, formatting ); + } + else if( footnoteContent is Hyperlink ) + { + noteParagraph.InsertHyperlink( footnoteContent as Hyperlink, noteParagraph.Text.Length ); + } + else if( footnoteContent is Picture ) + { + noteParagraph.InsertPicture( footnoteContent as Picture, noteParagraph.Text.Length ); + } + else if( !( footnoteContent is Table ) ) + { + throw new ArgumentException( "Unknown object received for footnote/endnote. Valid objects are string, Picture, Hyperlink or Table. If other configuration is needed, try using the Note.Paragraphs property and configure the Footnote/Endnote paragraphs." ); + } + + noteXml.Add( noteParagraph.Xml ); + + // Tables need to be added after the newFootnoteParagraph is included inside the newFootnoteXml. This is because table is located after paragraph. + if( footnoteContent is Table ) + { + noteParagraph.InsertTableAfterSelf( footnoteContent as Table ); + } + } + + private int GetMaxId( XElement xElement ) + { + if( xElement == null ) + return -1; + + var lastId = xElement.Attribute( XName.Get( "id", Document.w.NamespaceName ) ); + if( lastId == null ) + return -1; + + return Int32.Parse( lastId.Value ) + 1; + } + + + + + #endregion diff --git a/Xceed.Document.NET/Src/Endnote.cs b/Xceed.Document.NET/Src/Endnote.cs new file mode 100644 index 00000000..087902ea --- /dev/null +++ b/Xceed.Document.NET/Src/Endnote.cs @@ -0,0 +1,69 @@ +/*************************************************************************************** + + DocX – DocX is the community edition of Xceed Words for .NET + + Copyright (C) 2009-2020 Xceed Software Inc. + + This program is provided to you under the terms of the XCEED SOFTWARE, INC. + COMMUNITY LICENSE AGREEMENT (for non-commercial use) as published at + https://github.com/xceedsoftware/DocX/blob/master/license.md + + For more features and fast professional support, + pick up Xceed Words for .NET at https://xceed.com/xceed-words-for-net/ + + *************************************************************************************/ + + +using System.IO.Packaging; +using System.Xml.Linq; + +namespace Xceed.Document.NET +{ + public class Endnote: Note + { + #region Constructor + + internal Endnote( Document document, PackagePart part, XElement xml ) : base( document, part, xml ) + { + } + + #endregion + + #region Overrides + + internal override string GetNoteRefType() + { + return "endnoteRef"; + } + + internal override XElement CreateReferenceRunCore( bool customMarkFollows, XElement symbol, Formatting noteNumberFormatting ) + { + var rPr = (noteNumberFormatting != null) + ? noteNumberFormatting.Xml + : new XElement( XName.Get( "rPr", Document.w.NamespaceName ) ); + + rPr.AddFirst( new XElement( XName.Get( "rStyle", Document.w.NamespaceName ), + new XAttribute( XName.Get( "val", Document.w.NamespaceName ), "EndnoteReference" ) ) ); + + var r = new XElement( XName.Get( "r", Document.w.NamespaceName ) ); + r.Add( rPr ); + + var endNoteReference = new XElement( XName.Get( "endnoteReference", Document.w.NamespaceName ), + new XAttribute( XName.Get( "id", Document.w.NamespaceName ), this.Id ) ); + if( customMarkFollows ) + { + endNoteReference.SetAttributeValue( XName.Get( "customMarkFollows", Document.w.NamespaceName ), "1" ); + } + if( symbol != null ) + { + endNoteReference.Add( symbol ); + } + + r.Add( endNoteReference ); + + return r; + } + + #endregion + } +} diff --git a/Xceed.Document.NET/Src/Font.cs b/Xceed.Document.NET/Src/Font.cs index 16861de1..90ceb40e 100644 --- a/Xceed.Document.NET/Src/Font.cs +++ b/Xceed.Document.NET/Src/Font.cs @@ -18,25 +18,52 @@ COMMUNITY LICENSE AGREEMENT (for non-commercial use) as published at namespace Xceed.Document.NET { - public sealed class Font + public sealed class Font + { + public Font( string name ) { - public Font(string name) - { - if (string.IsNullOrEmpty(name)) - throw new ArgumentNullException(nameof(name)); - - Name = name; - } - - public string Name - { - get; - private set; - } - - public override string ToString() - { - return Name; - } + if( string.IsNullOrEmpty( name ) ) + throw new ArgumentNullException( nameof( name ) ); + + Name = name; + } + + public string Name + { + get; + private set; + } + + public override string ToString() + { + return Name; + } + + public static bool operator ==( Font x, Font y ) + { + return object.Equals( x, y ); + } + + public static bool operator !=( Font x, Font y ) + { + return !object.Equals( x, y ); + } + + public override bool Equals( object obj ) + { + if( obj == null ) + return false; + if( ReferenceEquals( obj, this ) ) + return true; + if( obj.GetType() != this.GetType() ) + return false; + Font rhs = obj as Font; + return this.Name == rhs.Name; + } + + public override int GetHashCode() + { + return this.Name.GetHashCode(); } + } } diff --git a/Xceed.Document.NET/Src/Footnote.cs b/Xceed.Document.NET/Src/Footnote.cs new file mode 100644 index 00000000..7de026ae --- /dev/null +++ b/Xceed.Document.NET/Src/Footnote.cs @@ -0,0 +1,69 @@ +/*************************************************************************************** + + DocX – DocX is the community edition of Xceed Words for .NET + + Copyright (C) 2009-2020 Xceed Software Inc. + + This program is provided to you under the terms of the XCEED SOFTWARE, INC. + COMMUNITY LICENSE AGREEMENT (for non-commercial use) as published at + https://github.com/xceedsoftware/DocX/blob/master/license.md + + For more features and fast professional support, + pick up Xceed Words for .NET at https://xceed.com/xceed-words-for-net/ + + *************************************************************************************/ + + +using System.IO.Packaging; +using System.Xml.Linq; + +namespace Xceed.Document.NET +{ + public class Footnote : Note + { + #region Constructor + + internal Footnote( Document document, PackagePart part, XElement xml ) : base( document, part, xml ) + { + } + + #endregion + + #region Overrides + + internal override string GetNoteRefType() + { + return "footnoteRef"; + } + + internal override XElement CreateReferenceRunCore( bool customMarkFollows, XElement symbol, Formatting noteNumberFormatting ) + { + var rPr = ( noteNumberFormatting != null ) + ? noteNumberFormatting.Xml + : new XElement( XName.Get( "rPr", Document.w.NamespaceName ) ); + + rPr.Add( new XElement( XName.Get( "rStyle", Document.w.NamespaceName ), + new XAttribute( XName.Get( "val", Document.w.NamespaceName ), "FootnoteReference" ) ) ); + + var r = new XElement( XName.Get( "r", Document.w.NamespaceName ) ); + r.Add( rPr ); + + var footNoteReference = new XElement( XName.Get( "footnoteReference", Document.w.NamespaceName ), + new XAttribute( XName.Get( "id", Document.w.NamespaceName ), this.Id ) ); + if( customMarkFollows ) + { + footNoteReference.SetAttributeValue( XName.Get( "customMarkFollows", Document.w.NamespaceName ), "1" ); + } + if( symbol != null ) + { + footNoteReference.Add( symbol ); + } + + r.Add( footNoteReference ); + + return r; + } + + #endregion + } +} diff --git a/Xceed.Document.NET/Src/Formatting.cs b/Xceed.Document.NET/Src/Formatting.cs index 32dd812c..7d4b177f 100644 --- a/Xceed.Document.NET/Src/Formatting.cs +++ b/Xceed.Document.NET/Src/Formatting.cs @@ -162,17 +162,24 @@ public double? Size { double? temp = value * 2; - if( temp - (int)temp == 0 ) + // Accepting only whole number or half number. + if( temp - (int)temp != 0 ) { - if( value > 0 && value < 1639 ) + if( value.HasValue ) { - _size = value; + value = Math.Round( value.Value ); } - else - throw new ArgumentException( "Size", "Value must be in the range 0 - 1638" ); + } + + if( value == 0 ) + return; + + if( value > 0 && value < 1639 ) + { + _size = value; } else - throw new ArgumentException( "Size", "Value must be either a whole or half number, examples: 32, 32.5" ); + throw new ArgumentException( "Size", "Value must be in the range 0 - 1638" ); } } diff --git a/Xceed.Document.NET/Src/HelperFunctions.cs b/Xceed.Document.NET/Src/HelperFunctions.cs index 4b9abc0e..d793444e 100644 --- a/Xceed.Document.NET/Src/HelperFunctions.cs +++ b/Xceed.Document.NET/Src/HelperFunctions.cs @@ -37,7 +37,7 @@ internal enum ResourceType NumberingDecimal, Numbering, Styles, - Theme + Theme, } internal static class HelperFunctions @@ -386,7 +386,7 @@ internal static XDocument DecompressXMLResource( string manifest_resource_name ) // XDocument to load the compressed Xml resource into. XDocument document; - // Get a reference to the executing assembly. + //// Get a reference to the executing assembly. Assembly assembly = Assembly.GetExecutingAssembly(); // Open a Stream to the embedded resource. @@ -412,51 +412,27 @@ internal static string GetResources( ResourceType resType ) { case ResourceType.DefaultStyle: { -#if NETSTANDARD - return "Xceed.Document.NETStandard.Resources.default_styles.xml.gz"; -#else return "Xceed.Document.NET.Resources.default_styles.xml.gz"; -#endif } case ResourceType.Numbering: { -#if NETSTANDARD - return "Xceed.Document.NETStandard.Resources.numbering.xml.gz"; -#else return "Xceed.Document.NET.Resources.numbering.xml.gz"; -#endif } case ResourceType.NumberingBullet: { -#if NETSTANDARD - return "Xceed.Document.NETStandard.Resources.numbering.default_bullet_abstract.xml.gz"; -#else return "Xceed.Document.NET.Resources.numbering.default_bullet_abstract.xml.gz"; -#endif } case ResourceType.NumberingDecimal: { -#if NETSTANDARD - return "Xceed.Document.NETStandard.Resources.numbering.default_decimal_abstract.xml.gz"; -#else return "Xceed.Document.NET.Resources.numbering.default_decimal_abstract.xml.gz"; -#endif } case ResourceType.Styles: { -#if NETSTANDARD - return "Xceed.Document.NETStandard.Resources.styles.xml.gz"; -#else return "Xceed.Document.NET.Resources.styles.xml.gz"; -#endif } case ResourceType.Theme: { -#if NETSTANDARD - return "Xceed.Document.NETStandard.Resources.theme.xml.gz"; -#else return "Xceed.Document.NET.Resources.theme.xml.gz"; -#endif } } @@ -495,7 +471,7 @@ internal static XElement CreateEdit( EditType t, DateTime edit_time, object cont { if( t == EditType.del ) { - foreach( object o in (IEnumerable)content ) + foreach( object o in ( IEnumerable )content ) { if( o is XElement ) { @@ -578,6 +554,15 @@ internal static XElement CreateTableCell( double w = 2310 ) + + + + + + + + + internal static void RenumberIDs( Document document ) @@ -773,13 +758,13 @@ internal static XDocument AddDefaultNumberingXml( Package package ) return numberingDoc; } - internal static List CreateItemInList( List list, - string listText, + internal static List CreateItemInList( List list, + string listText, int level = 0, ListItemType listType = ListItemType.Numbered, - int? startNumber = null, - bool trackChanges = false, - bool continueNumbering = false, + int? startNumber = null, + bool trackChanges = false, + bool continueNumbering = false, Formatting formatting = null ) { if( list.NumId == 0 ) @@ -826,7 +811,7 @@ internal static List CreateItemInList( List list, if( startNumber == null ) list.AddItem( new Paragraph( list.Document, newSection, 0, ContainerType.Paragraph ) ); else - list.AddItemWithStartValue( new Paragraph( list.Document, newSection, 0, ContainerType.Paragraph ), (int)startNumber ); + list.AddItemWithStartValue( new Paragraph( list.Document, newSection, 0, ContainerType.Paragraph ), ( int )startNumber ); } return list; } @@ -982,6 +967,33 @@ internal static XElement GetAbstractNum( Document document, string numId ) return abstractNumNode; } + internal static IEnumerable GetAbstractNumLevelNodes( Document document, string numId ) + { + var abstractNum = GetAbstractNum( document, numId ); + + if( abstractNum != null ) + { + var levelNodes = abstractNum.Elements( XName.Get( "lvl", Document.w.NamespaceName ) ); + if( levelNodes != null ) + { + return levelNodes; + } + } + + return null; + } + + internal static XElement GetNumberingNumNode( Document document, string numId ) + { + if( document == null ) + return null; + if( numId == null ) + return null; + + var numNodes = document._numbering.Root.Elements( XName.Get( "num", Document.w.NamespaceName ) ); + return numNodes?.SingleOrDefault( node => node.Attribute( Document.w + "numId" ).Value.Equals( numId ) ) ?? null; + } + internal static string GetAbstractNumIdValue( Document document, string numId ) { if( document == null ) @@ -1157,7 +1169,7 @@ internal static Border GetBorderFromXml( XElement xml ) var bdrStyle = xml.Attribute( XName.Get( "val", Document.w.NamespaceName ) ); if( bdrStyle != null ) { - borderStyle = (BorderStyle)Enum.Parse( typeof( BorderStyle ), "Tcbs_" + bdrStyle.Value ); + borderStyle = ( BorderStyle )Enum.Parse( typeof( BorderStyle ), "Tcbs_" + bdrStyle.Value ); } return new Border( borderStyle, borderSize, borderSpace, borderColor ); diff --git a/Xceed.Document.NET/Src/Hyperlink.cs b/Xceed.Document.NET/Src/Hyperlink.cs index a18eb800..986855e8 100644 --- a/Xceed.Document.NET/Src/Hyperlink.cs +++ b/Xceed.Document.NET/Src/Hyperlink.cs @@ -226,23 +226,18 @@ internal Hyperlink( Document document, XElement instrText, List runs ) this.instrText = instrText; this.runs = runs; - try + int start = instrText.Value.IndexOf( "HYPERLINK \"" ); + if( start != -1 ) + start += "HYPERLINK \"".Length; + int end = instrText.Value.IndexOf( "\"", Math.Max( 0, start )); + if( start != -1 && end != -1 ) { - int start = instrText.Value.IndexOf( "HYPERLINK \"" ); - if( start != -1 ) - start += "HYPERLINK \"".Length; - int end = instrText.Value.IndexOf( "\"", Math.Max( 0, start )); - if( start != -1 && end != -1 ) - { - this.uri = new Uri( instrText.Value.Substring( start, end - start ), UriKind.Absolute ); + this.uri = new Uri( instrText.Value.Substring( start, end - start ), UriKind.Absolute ); - StringBuilder sb = new StringBuilder(); - HelperFunctions.GetTextRecursive( new XElement( XName.Get( "temp", Document.w.NamespaceName ), runs ), ref sb ); - this.text = sb.ToString(); - } + StringBuilder sb = new StringBuilder(); + HelperFunctions.GetTextRecursive( new XElement( XName.Get( "temp", Document.w.NamespaceName ), runs ), ref sb ); + this.text = sb.ToString(); } - - catch( Exception e ) { throw e; } } #endregion diff --git a/Xceed.Document.NET/Src/Image.cs b/Xceed.Document.NET/Src/Image.cs index b4567b58..09be4963 100644 --- a/Xceed.Document.NET/Src/Image.cs +++ b/Xceed.Document.NET/Src/Image.cs @@ -134,6 +134,19 @@ public Picture CreatePicture( float height, float width ) return Paragraph.CreatePicture( _document, _id, string.Empty, string.Empty, width, height ); } + public void Remove() + { + if( _pr.Package != null ) + { + _pr.Package.DeletePart( _pr.TargetUri ); + } + + if( _document.PackagePart != null ) + { + _document.PackagePart.DeleteRelationship( _id ); + } + } + #endregion } } diff --git a/Xceed.Document.NET/Src/List.cs b/Xceed.Document.NET/Src/List.cs index 8d8dfb91..f2c234c0 100644 --- a/Xceed.Document.NET/Src/List.cs +++ b/Xceed.Document.NET/Src/List.cs @@ -18,6 +18,8 @@ COMMUNITY LICENSE AGREEMENT (for non-commercial use) as published at using System.Linq; using System.Xml.Linq; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; namespace Xceed.Document.NET { @@ -26,6 +28,20 @@ namespace Xceed.Document.NET /// public class List : InsertBeforeOrAfter { + + #region Private properties + + private static Random _random = new Random(); + + #endregion + + #region Private Members + + private ListItemType? _listType; + + + #endregion + #region Public Properties /// @@ -51,12 +67,21 @@ public int NumId /// public ListItemType? ListType { - get; private set; + get + { + return _listType; + } } - #endregion - #region Internal Properties + + + + + + + + public int AbstractNumId { @@ -75,7 +100,6 @@ internal List( Document document, XElement xml ) : base( document, xml ) { Items = new List(); - ListType = null; } #endregion @@ -100,12 +124,12 @@ public void AddItem( Paragraph paragraph ) if( this.CanAddListItem( paragraph ) ) { this.NumId = numId; - if( this.ListType == null ) + if( _listType == null ) { var listItemType = HelperFunctions.GetListItemType( paragraph, this.Document ); if( listItemType != null ) { - this.ListType = listItemType.Equals( "bullet" ) ? ListItemType.Bulleted : ListItemType.Numbered; + _listType = listItemType.Equals( "bullet" ) ? ListItemType.Bulleted : ListItemType.Numbered; } } this.Items.Add( paragraph ); @@ -148,42 +172,54 @@ public bool CanAddListItem( Paragraph paragraph ) return false; } + + public bool ContainsLevel( int ilvl ) { return Items.Any( i => i.ParagraphNumberProperties.Descendants().First( el => el.Name.LocalName == "ilvl" ).Value == ilvl.ToString() ); } + public void Remove() + { + // Remove AbstractNum and Num from numbering.xml. + var abstractNumId = this.GetAbstractNum( this.NumId ); + if( abstractNumId != null ) + { + abstractNumId.Remove(); + } + + var numNode = this.Document._numbering.Descendants() + .Where( n => n.Name.LocalName == "num" ) + .FirstOrDefault( node => node.Attribute( Document.w + "numId" ).Value.Equals( this.NumId.ToString() ) ); + if( numNode != null ) + { + numNode.Remove(); + } + + // Remove listItems from document. + this.Items.ForEach( paragraph => paragraph.Remove( false ) ); + } + #endregion #region Internal Methods internal void CreateNewNumberingNumId( int level = 0, ListItemType listType = ListItemType.Numbered, int? startNumber = null, bool continueNumbering = false ) { - ValidateDocXNumberingPartExists(); - if( Document._numbering.Root == null ) + int numId, abstractNumId; + XElement abstractNumTemplate; + SetAbstractNumTemplate( listType, out numId, out abstractNumId, out abstractNumTemplate ); + + // When documents contains many lists, generate different "nsid" for each abstractNumTemplate. + // Each "nsid" value should be unique. + var nsid = abstractNumTemplate.Element( Document.w + "nsid" ); + var val = nsid.Attribute( Document.w + "val" ); + if( val != null ) { - throw new InvalidOperationException( "Numbering section did not instantiate properly." ); + var newNSidVal = GetRandomHexNumber(); + nsid.SetAttributeValue( Document.w + "val", newNSidVal ); } - ListType = listType; - - var numId = GetMaxNumId() + 1; - var abstractNumId = GetMaxAbstractNumId() + 1; - - XDocument listTemplate; - switch( listType ) - { - case ListItemType.Bulleted: - listTemplate = HelperFunctions.DecompressXMLResource( HelperFunctions.GetResources( ResourceType.NumberingBullet ) ); - break; - case ListItemType.Numbered: - listTemplate = HelperFunctions.DecompressXMLResource( HelperFunctions.GetResources( ResourceType.NumberingDecimal ) ); - break; - default: - throw new InvalidOperationException( string.Format( "Unable to deal with ListItemType: {0}.", listType.ToString() ) ); - } - var abstractNumTemplate = listTemplate.Descendants().Single( d => d.Name.LocalName == "abstractNum" ); - abstractNumTemplate.SetAttributeValue( Document.w + "abstractNumId", abstractNumId ); var abstractNumXml = GetAbstractNumXml( abstractNumId, numId, startNumber, level, continueNumbering ); var abstractNumNode = Document._numbering.Root.Descendants().LastOrDefault( xElement => xElement.Name.LocalName == "abstractNum" ); @@ -205,6 +241,24 @@ internal void CreateNewNumberingNumId( int level = 0, ListItemType listType = Li NumId = numId; } + internal static string GetRandomHexNumber() + { + int digits = 8; + byte[] buffer = new byte[ digits / 2 ]; + + _random.NextBytes( buffer ); + string result = string.Concat( buffer.Select( x => x.ToString( "X2" ) ).ToArray() ); + if( digits % 2 == 0 ) + return result; + return result + _random.Next( 16 ).ToString( "X" ); + } + + + + + + + @@ -225,6 +279,94 @@ internal XElement GetAbstractNum( int numId ) #region Private Methods + private XDocument GetListTemplate( ListItemType listType ) + { + switch( listType ) + { + case ListItemType.Bulleted: + return HelperFunctions.DecompressXMLResource( HelperFunctions.GetResources( ResourceType.NumberingBullet ) ); + case ListItemType.Numbered: + return HelperFunctions.DecompressXMLResource( HelperFunctions.GetResources( ResourceType.NumberingDecimal ) ); + default: + throw new InvalidOperationException( string.Format( "Unable to deal with ListItemType: {0}.", listType.ToString() ) ); + } + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + private void SetAbstractNumTemplate( ListItemType listType, out int numId, out int abstractNumId, out XElement abstractNumTemplate ) + { + ValidateDocXNumberingPartExists(); + if( Document._numbering.Root == null ) + { + throw new InvalidOperationException( "Numbering section did not instantiate properly." ); + } + + _listType = listType; + + numId = GetMaxNumId() + 1; + abstractNumId = GetMaxAbstractNumId() + 1; + XDocument listTemplate = GetListTemplate( listType ); + + abstractNumTemplate = listTemplate.Descendants().Single( d => d.Name.LocalName == "abstractNum" ); + abstractNumTemplate.SetAttributeValue( Document.w + "abstractNumId", abstractNumId ); + } + private void UpdateNumberingForLevelStartNumber( int iLevel, int start ) { // Find num node in numbering. @@ -279,6 +421,7 @@ private XElement GetAbstractNumXml( int abstractNumId, int numId, int? startNumb : new XElement( XName.Get( "num", Document.w.NamespaceName ), new XAttribute( Document.w + "numId", numId ), element, levelOverride ); } + /// /// Method to determine the last numId for a list element. /// Also useful for determining the next numId to use for inserting a new list element into the document. @@ -337,4 +480,5 @@ private void ValidateDocXNumberingPartExists() #endregion } + } diff --git a/Xceed.Document.NET/Src/Note.cs b/Xceed.Document.NET/Src/Note.cs new file mode 100644 index 00000000..cce6dcb6 --- /dev/null +++ b/Xceed.Document.NET/Src/Note.cs @@ -0,0 +1,309 @@ +/*************************************************************************************** + + DocX – DocX is the community edition of Xceed Words for .NET + + Copyright (C) 2009-2020 Xceed Software Inc. + + This program is provided to you under the terms of the XCEED SOFTWARE, INC. + COMMUNITY LICENSE AGREEMENT (for non-commercial use) as published at + https://github.com/xceedsoftware/DocX/blob/master/license.md + + For more features and fast professional support, + pick up Xceed Words for .NET at https://xceed.com/xceed-words-for-net/ + + *************************************************************************************/ + + +using System; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Globalization; +using System.IO.Packaging; +using System.Linq; +using System.Xml.Linq; + +namespace Xceed.Document.NET +{ + public abstract class Note : Container, IParagraphContainer + { + #region Constants + + internal static int DefaultCustomCharCode = 61440; //"F000" + + #endregion + + #region Private Members + + private int _id; + private Symbol _customMark; + + #endregion + + #region Public Properties + + #region CustomMark + + public Symbol CustomMark + { + get + { + if( _customMark != null ) + return _customMark; + + var sym = this.Xml.Descendants( XName.Get( "sym", Document.w.NamespaceName ) ).FirstOrDefault(); + if( sym != null ) + { + _customMark = new Symbol(); + + var font = sym.GetAttribute( XName.Get( "font", Document.w.NamespaceName ) ); + if( !string.IsNullOrEmpty( font ) ) + { + _customMark.Font = new Font( font ); + } + + var code = sym.GetAttribute( XName.Get( "char", Document.w.NamespaceName ) ); + if( !string.IsNullOrEmpty( code ) ) + { + // Do code - "F000". + _customMark.Code = int.Parse( code, NumberStyles.HexNumber ) - DefaultCustomCharCode; + } + + _customMark.PropertyChanged += this.CustomMark_PropertyChanged; + + return _customMark; + } + + return null; + } + + set + { + if( _customMark != null ) + { + _customMark.PropertyChanged -= this.CustomMark_PropertyChanged; + } + + _customMark = value; + + if( _customMark != null ) + { + _customMark.PropertyChanged += this.CustomMark_PropertyChanged; + } + + this.UpdateCustomMarkXml(); + } + } + + #endregion //CustomMark + + #region Paragraphs + + public override ReadOnlyCollection Paragraphs + { + get + { + var paragraphs = base.Paragraphs; + foreach( var paragraph in paragraphs ) + { + paragraph.PackagePart = this.PackagePart; + } + return paragraphs; + } + } + + #endregion // Paragraphs + + #endregion + + #region Internal Properties + + #region Id + + internal int Id + { + get + { + return _id; + } + } + + #endregion //Id + + #endregion + + #region Constructor + + internal Note( Document document, PackagePart part, XElement xml ) : base( document, xml ) + { + this.PackagePart = part; + + var id = this.Xml.Attribute( XName.Get( "id", Document.w.NamespaceName ) ); + _id = ( id != null ) ? Int32.Parse( id.Value ) : 0; + } + + #endregion + + #region Internal Methods + + internal abstract string GetNoteRefType(); + + internal abstract XElement CreateReferenceRunCore( bool customMarkFollows, XElement symbol, Formatting noteNumberFormatting ); + + internal XElement CreateReferenceRun( Formatting noteNumberFormatting ) + { + var customMarkFollows = false; + XElement symbol = null; + + if( this.CustomMark != null ) + { + customMarkFollows = true; + + var font = this.CustomMark.Font.Name; + var code = this.CustomMark.HexCode; + symbol = XElement.Parse( string.Format( @"", font, code ) ); + } + + // Create new reference for paragraph. + var referenceRun = this.CreateReferenceRunCore( customMarkFollows, symbol, noteNumberFormatting ); + + // When number formatting is used, update the number at the beginning of the note. + if( noteNumberFormatting != null ) + { + var rPr = this.Xml.Descendants( XName.Get( "rPr", Document.w.NamespaceName ) ).FirstOrDefault(); + if( rPr != null ) + { + foreach( var numberFormattingElement in noteNumberFormatting.Xml.Elements() ) + { + var runElement = rPr.Element( numberFormattingElement.Name ); + // run doesn't contains the property, add it. + if( runElement == null ) + { + rPr.Add( numberFormattingElement ); + } + else + { + runElement.Remove(); + rPr.Add( numberFormattingElement ); + } + } + } + } + + return referenceRun; + } + + #endregion + + #region Private Methods + + private void UpdateCustomMarkXml() + { + if( _customMark != null ) + { + var sym = new XElement( XName.Get( "sym", Document.w.NamespaceName ) ); + sym.SetAttributeValue( XName.Get( "char", Document.w.NamespaceName ), _customMark.HexCode ); + sym.SetAttributeValue( XName.Get( "font", Document.w.NamespaceName ), _customMark.Font.Name ); + + var noteRef = this.Xml.Descendants( XName.Get( this.GetNoteRefType(), Document.w.NamespaceName ) ).FirstOrDefault(); + if( noteRef != null ) + { + noteRef.ReplaceWith( sym ); + } + else + { + var currentSym = this.Xml.Descendants( XName.Get( "sym", Document.w.NamespaceName ) ).FirstOrDefault(); + if( currentSym != null ) + { + currentSym.ReplaceWith( sym ); + } + } + } + else + { + var currentSym = this.Xml.Descendants( XName.Get( "sym", Document.w.NamespaceName ) ).FirstOrDefault(); + if( currentSym != null ) + { + currentSym.ReplaceWith( new XElement( XName.Get( this.GetNoteRefType(), Document.w.NamespaceName ) ) ); + } + } + } + + #endregion + + #region Event Handlers + + private void CustomMark_PropertyChanged( object sender, PropertyChangedEventArgs e ) + { + this.UpdateCustomMarkXml(); + } + + #endregion + } + + + public class Symbol : INotifyPropertyChanged + { + #region Private Members + + private int _code; + private Font _font; + + #endregion + + #region Public Properties + + public int Code + { + get + { + return _code; + } + set + { + _code = value; + OnPropertyChanged( "Code" ); + } + } + + public Font Font + { + get + { + return _font; + } + set + { + _font = value; + OnPropertyChanged( "Font" ); + } + } + + #endregion + + #region Internal Properties + + internal string HexCode + { + get + { + // Do "F000" + this.code. + return ( Footnote.DefaultCustomCharCode + this.Code ).ToString( "X" ); + } + } + + #endregion + + #region INotifyPropertyChanged + + public event PropertyChangedEventHandler PropertyChanged; + protected void OnPropertyChanged( string propertyName ) + { + if( PropertyChanged != null ) + { + PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) ); + } + } + + #endregion + } +} diff --git a/Xceed.Document.NET/Src/NoteProperties.cs b/Xceed.Document.NET/Src/NoteProperties.cs new file mode 100644 index 00000000..17659846 --- /dev/null +++ b/Xceed.Document.NET/Src/NoteProperties.cs @@ -0,0 +1,73 @@ +/*************************************************************************************** + + DocX – DocX is the community edition of Xceed Words for .NET + + Copyright (C) 2009-2020 Xceed Software Inc. + + This program is provided to you under the terms of the XCEED SOFTWARE, INC. + COMMUNITY LICENSE AGREEMENT (for non-commercial use) as published at + https://github.com/xceedsoftware/DocX/blob/master/license.md + + For more features and fast professional support, + pick up Xceed Words for .NET at https://xceed.com/xceed-words-for-net/ + + *************************************************************************************/ + + +using System.ComponentModel; + +namespace Xceed.Document.NET +{ + public class NoteProperties : INotifyPropertyChanged + { + #region Private Members + + private NoteNumberFormat _numberFormat = NoteNumberFormat.number; + private int _numberStart = 1; + + #endregion + + #region Public Properties + + public NoteNumberFormat NumberFormat + { + get + { + return _numberFormat; + } + set + { + _numberFormat = value; + OnPropertyChanged( "NumberFormat" ); + } + } + + public int NumberStart + { + get + { + return _numberStart; + } + set + { + _numberStart = value; + OnPropertyChanged( "NumberStart" ); + } + } + + #endregion + + #region INotifyPropertyChanged + + public event PropertyChangedEventHandler PropertyChanged; + protected void OnPropertyChanged( string propertyName ) + { + if( PropertyChanged != null ) + { + PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) ); + } + } + + #endregion + } +} diff --git a/Xceed.Document.NET/Src/Paragraph.cs b/Xceed.Document.NET/Src/Paragraph.cs index ec9e64f8..a2c55326 100644 --- a/Xceed.Document.NET/Src/Paragraph.cs +++ b/Xceed.Document.NET/Src/Paragraph.cs @@ -91,6 +91,22 @@ internal bool? IsListItemBacker get; set; } + internal int OutlineLevel + { + get + { + var pPr = GetOrCreate_pPr(); + var outlineLvl = pPr.Element( XName.Get( "outlineLvl", Document.w.NamespaceName ) ); + if( outlineLvl != null ) + { + var val = outlineLvl.Attribute( XName.Get( "val", Document.w.NamespaceName ) ); + return ( val != null ) ? Int32.Parse( val.Value ) : 0; + } + + return 0; + } + } + #endregion #region Public Properties @@ -104,6 +120,9 @@ internal bool? IsListItemBacker + + + public ContainerType ParentContainer { get; set; @@ -258,7 +277,7 @@ public string StyleName { get { - return this.StyleId; + return this.StyleId; } set { @@ -860,7 +879,7 @@ public bool IsListItem get { IsListItemBacker = IsListItemBacker ?? ( ParagraphNumberProperties != null ); - return (bool)IsListItemBacker; + return ( bool )IsListItemBacker; } } @@ -1339,7 +1358,7 @@ public Paragraph InsertHyperlink( Hyperlink h, int index = 0 ) Xml.AddFirst( h.Xml ); // Extract the picture back out of the DOM. - h_xml = (XElement)Xml.FirstNode; + h_xml = ( XElement )Xml.FirstNode; } else { @@ -1352,7 +1371,7 @@ public Paragraph InsertHyperlink( Hyperlink h, int index = 0 ) Xml.Add( h.Xml ); // Extract the picture back out of the DOM. - h_xml = (XElement)Xml.LastNode; + h_xml = ( XElement )Xml.LastNode; } else { @@ -1362,16 +1381,16 @@ public Paragraph InsertHyperlink( Hyperlink h, int index = 0 ) // Replace the origional run. run.Xml.ReplaceWith ( - splitRun[ 0 ], + splitRun[0], h.Xml, - splitRun[ 1 ] + splitRun[1] ); // Get the first run effected by this Insert run = GetFirstRunEffectedByEdit( index ); // The picture has to be the next element, extract it back out of the DOM. - h_xml = (XElement)run.Xml.NextNode; + h_xml = ( XElement )run.Xml.NextNode; } } @@ -1583,7 +1602,7 @@ public void Remove( bool trackChanges ) List temp = new List(); for( int i = 0; i < elements.Count(); i++ ) { - XElement e = elements[ i ]; + XElement e = elements[i]; if( e.Name.LocalName != "del" ) { @@ -1905,6 +1924,7 @@ public void InsertText( int index, string value, bool trackChanges = false, Form // Timestamp to mark the start of insert var now = DateTime.Now; var insert_datetime = new DateTime( now.Year, now.Month, now.Day, now.Hour, now.Minute, 0, DateTimeKind.Utc ); + var reCalculateIds = false; // Get the first run effected by this Insert var run = this.GetFirstRunEffectedByEdit( index ); @@ -1916,6 +1936,7 @@ public void InsertText( int index, string value, bool trackChanges = false, Form if( trackChanges ) { insert = CreateEdit( EditType.ins, insert_datetime, insert ); + reCalculateIds = true; } this.Xml.Add( insert ); } @@ -1990,6 +2011,7 @@ public void InsertText( int index, string value, bool trackChanges = false, Form if( trackChanges ) { insert = CreateEdit( EditType.ins, insert_datetime, newRuns ); + reCalculateIds = true; } // Split this Edit at the point you want to insert @@ -1998,9 +2020,9 @@ public void InsertText( int index, string value, bool trackChanges = false, Form // Replace the origional run parentElement.ReplaceWith ( - splitEdit[ 0 ], + splitEdit[0], insert, - splitEdit[ 1 ] + splitEdit[1] ); break; @@ -2012,6 +2034,7 @@ splitEdit[ 1 ] if( trackChanges && !parentElement.Name.LocalName.Equals( "ins" ) ) { insert = CreateEdit( EditType.ins, insert_datetime, newRuns ); + reCalculateIds = true; } // Split this run at the point you want to insert var splitRun = Run.SplitRun( run, index ); @@ -2019,9 +2042,9 @@ splitEdit[ 1 ] // Replace the origional run run.Xml.ReplaceWith ( - splitRun[ 0 ], + splitRun[0], insert, - splitRun[ 1 ] + splitRun[1] ); break; @@ -2031,7 +2054,10 @@ splitRun[ 1 ] _runs = this.Xml.Elements( XName.Get( "r", Document.w.NamespaceName ) ).ToList(); - HelperFunctions.RenumberIDs( Document ); + if( reCalculateIds ) + { + HelperFunctions.RenumberIDs( Document ); + } } /// @@ -2330,6 +2356,25 @@ select e.Attribute( XName.Get( "embed", "http://schemas.openxmlformats.org/offic + + + + + + + + + public Paragraph AppendNote( Note note, Formatting noteNumberFormatting = null ) + { + if( note != null ) + { + // Append the note to the paragraph and format the number in the paragraph. + this.Xml.Add( note.CreateReferenceRun( noteNumberFormatting ) ); + } + + return this; + } + public Paragraph NextParagraph { get @@ -2376,7 +2421,8 @@ public Paragraph AppendEquation( String equation, Alignment align = Alignment.ce case Alignment.right: alignString = "right"; break; - default: alignString = "center"; + default: + alignString = "center"; break; } @@ -2500,14 +2546,14 @@ public Paragraph InsertPicture( Picture p, int index = 0 ) firstRun.AddBeforeSelf( p.Xml ); // Extract the picture back out of the DOM. - p_xml = (XElement)firstRun.PreviousNode; + p_xml = ( XElement )firstRun.PreviousNode; } else { Xml.AddFirst( p.Xml ); // Extract the picture back out of the DOM. - p_xml = (XElement)Xml.FirstNode; + p_xml = ( XElement )Xml.FirstNode; } } else @@ -2520,7 +2566,7 @@ public Paragraph InsertPicture( Picture p, int index = 0 ) Xml.Add( p.Xml ); // Extract the picture back out of the DOM. - p_xml = (XElement)Xml.LastNode; + p_xml = ( XElement )Xml.LastNode; } else { @@ -2528,13 +2574,13 @@ public Paragraph InsertPicture( Picture p, int index = 0 ) var splitRun = Run.SplitRun( run, index ); // Replace the origional run. - run.Xml.ReplaceWith( splitRun[ 0 ], p.Xml, splitRun[ 1 ] ); + run.Xml.ReplaceWith( splitRun[0], p.Xml, splitRun[1] ); // Get the first run effected by this Insert run = GetFirstRunEffectedByEdit( index ); // The picture has to be the next element, extract it back out of the DOM. - p_xml = (XElement)run.Xml.NextNode; + p_xml = ( XElement )run.Xml.NextNode; } } @@ -2558,8 +2604,9 @@ select e.Attribute( XName.Get( "embed", "http://schemas.openxmlformats.org/offic /// Specifies the alignment of the Tab stop. /// Specifies the horizontal position of the tab stop. /// Specifies the character used to fill in the space created by a tab. + /// The TabStopPosition index to insert at. /// The modified Paragraph. - public Paragraph InsertTabStopPosition( Alignment alignment, float position, TabStopPositionLeader leader = TabStopPositionLeader.none ) + public Paragraph InsertTabStopPosition( Alignment alignment, float position, TabStopPositionLeader leader = TabStopPositionLeader.none, int index = -1 ) { var pPr = GetOrCreate_pPr(); var tabs = pPr.Element( XName.Get( "tabs", Document.w.NamespaceName ) ); @@ -2614,7 +2661,15 @@ public Paragraph InsertTabStopPosition( Alignment alignment, float position, Tab } newTab.SetAttributeValue( XName.Get( "leader", Document.w.NamespaceName ), leaderString ); - tabs.Add( newTab ); + var tabsList = tabs.Elements().ToList(); + if( (index >= 0) && (index < tabsList.Count()) ) + { + tabsList[index].AddBeforeSelf( newTab ); + } + else + { + tabs.Add( newTab ); + } return this; } @@ -2821,8 +2876,8 @@ public Paragraph UnderlineStyle( UnderlineStyle underlineStyle ) /// public Paragraph FontSize( double fontSize ) { - double tempSize = (int)fontSize * 2; - if( tempSize - (int)tempSize == 0 ) + double tempSize = ( int )fontSize * 2; + if( tempSize - ( int )tempSize == 0 ) { if( !( fontSize > 0 && fontSize < 1639 ) ) throw new ArgumentException( "Size", "Value must be in the range 0 - 1638" ); @@ -3226,7 +3281,7 @@ public Paragraph Spacing( double spacing ) { spacing *= 20; - if( spacing - (int)spacing == 0 ) + if( spacing - ( int )spacing == 0 ) { if( !( spacing > -1585 && spacing < 1585 ) ) throw new ArgumentException( "Spacing", "Value must be in the range: -1584 - 1584" ); @@ -3505,6 +3560,7 @@ public void RemoveText( int index, int count, bool trackChanges = false, bool re // The number of characters processed so far int processed = 0; + var reCalculateIds = false; do { @@ -3521,16 +3577,17 @@ public void RemoveText( int index, int count, bool trackChanges = false, bool re var min = Math.Min( count - processed, run.Xml.ElementsAfterSelf().Sum( e => GetElementTextLength( e ) ) ); var splitEditAfter = this.SplitEdit( parentElement, index + min, EditType.del ); - var temp = this.SplitEdit( splitEditBefore[ 1 ], index + min, EditType.del )[ 1 ]; + var temp = this.SplitEdit( splitEditBefore[1], index + min, EditType.del )[1]; var middle = Paragraph.CreateEdit( EditType.del, remove_datetime, temp.Elements() ); processed += Paragraph.GetElementTextLength( middle as XElement ); + reCalculateIds = true; if( !trackChanges ) { middle = null; } - parentElement.ReplaceWith( splitEditBefore[ 0 ], middle, splitEditAfter[ 0 ] ); + parentElement.ReplaceWith( splitEditBefore[0], middle, splitEditAfter[0] ); processed += Paragraph.GetElementTextLength( middle as XElement ); break; @@ -3558,15 +3615,16 @@ public void RemoveText( int index, int count, bool trackChanges = false, bool re var min = Math.Min( index + ( count - processed ), run.EndIndex ); var splitRunAfter = Run.SplitRun( run, min, EditType.del ); - var middle = Paragraph.CreateEdit( EditType.del, remove_datetime, new List() { Run.SplitRun( new Run( Document, splitRunBefore[ 1 ], run.StartIndex + GetElementTextLength( splitRunBefore[ 0 ] ) ), min, EditType.del )[ 0 ] } ); + var middle = Paragraph.CreateEdit( EditType.del, remove_datetime, new List() { Run.SplitRun( new Run( Document, splitRunBefore[1], run.StartIndex + GetElementTextLength( splitRunBefore[0] ) ), min, EditType.del )[0] } ); processed = processed + Paragraph.GetElementTextLength( middle as XElement ); + reCalculateIds = true; if( !trackChanges ) { middle = null; } - run.Xml.ReplaceWith( splitRunBefore[ 0 ], middle, splitRunAfter[ 1 ] ); + run.Xml.ReplaceWith( splitRunBefore[0], middle, splitRunAfter[1] ); } else { @@ -3595,7 +3653,10 @@ public void RemoveText( int index, int count, bool trackChanges = false, bool re _runs = this.Xml.Elements( XName.Get( "r", Document.w.NamespaceName ) ).ToList(); - HelperFunctions.RenumberIDs( Document ); + if( reCalculateIds ) + { + HelperFunctions.RenumberIDs( Document ); + } } @@ -3747,13 +3808,13 @@ public void ReplaceText( string searchValue, int lastcap = 0; for( int k = 0; k < m.Groups.Count; k++ ) { - var g = m.Groups[ k ]; + var g = m.Groups[k]; if( ( g == null ) || ( g.Value == "" ) ) continue; newValue = newValue.Replace( "$" + k.ToString(), g.Value ); lastcap = k; } - newValue = newValue.Replace( "$+", m.Groups[ lastcap ].Value ); + newValue = newValue.Replace( "$+", m.Groups[lastcap].Value ); } if( m.Index > 0 ) { @@ -3816,7 +3877,16 @@ public void ReplaceText( string findPattern, Func regexMatchHand // Replace text when formatting matches. if( formattingMatch ) { - var newValue = regexMatchHandler.Invoke( match.Groups[ 1 ].Value ); + int lastcap = 0; + for( int k = 0; k < match.Groups.Count; k++ ) + { + var g = match.Groups[k]; + if( ( g == null ) || ( g.Value == "" ) ) + continue; + lastcap = k; + } + + var newValue = regexMatchHandler.Invoke( match.Groups[lastcap].Value ); this.InsertText( match.Index + match.Value.Length, newValue, trackChanges, newFormatting ); this.RemoveText( match.Index, match.Value.Length, trackChanges, removeEmptyParagraph ); } @@ -3882,15 +3952,15 @@ public void ReplaceTextWithObject( string searchValue, { if( objectToAdd is Picture ) { - this.InsertPicture( (Picture)objectToAdd, m.Index + m.Length ); + this.InsertPicture( ( Picture )objectToAdd, m.Index + m.Length ); } else if( objectToAdd is Hyperlink ) { - this.InsertHyperlink( (Hyperlink)objectToAdd, m.Index + m.Length ); + this.InsertHyperlink( ( Hyperlink )objectToAdd, m.Index + m.Length ); } else if( objectToAdd is Table ) { - this.InsertTableAfterSelf( (Table)objectToAdd ); + this.InsertTableAfterSelf( ( Table )objectToAdd ); } else { @@ -4072,9 +4142,9 @@ public void InsertPageNumber( PageNumberFormat pnf, int index = 0 ) var splitEdit = SplitEdit( r.Xml, index, EditType.ins ); r.Xml.ReplaceWith ( - splitEdit[ 0 ], + splitEdit[0], fldSimple, - splitEdit[ 1 ] + splitEdit[1] ); } } @@ -4185,9 +4255,9 @@ public void InsertPageCount( PageNumberFormat pnf, int index = 0 ) XElement[] splitEdit = SplitEdit( r.Xml, index, EditType.ins ); r.Xml.ReplaceWith ( - splitEdit[ 0 ], + splitEdit[0], fldSimple, - splitEdit[ 1 ] + splitEdit[1] ); } } @@ -4262,7 +4332,7 @@ public void SetLineSpacing( LineSpacingType spacingType, float spacingFloat ) var spacingTypeAttribute = ( spacingType == LineSpacingType.Before ) ? "before" : ( spacingType == LineSpacingType.After ) ? "after" : "line"; - spacing.SetAttributeValue( XName.Get( spacingTypeAttribute, Document.w.NamespaceName ), ( int )( spacingFloat * 20f ) ); + spacing.SetAttributeValue( XName.Get( spacingTypeAttribute, Document.w.NamespaceName ), (int)( spacingFloat * 20f ) ); } /// @@ -4369,7 +4439,7 @@ public IEnumerable GetBookmarks() { var bookmarkStart = this.Document.Xml.Descendants( XName.Get( "bookmarkStart", Document.w.NamespaceName ) ) .Where( x => x.Attribute( XName.Get( "id", Document.w.NamespaceName ) )?.Value == id ) - .FirstOrDefault(); + .LastOrDefault(); if( bookmarks != null ) { @@ -4417,7 +4487,6 @@ public void InsertAtBookmark( string toInsert, string bookmarkName, Formatting f var run = HelperFunctions.FormatInput( toInsert, ( formatting != null ) ? formatting.Xml : null ); refPosition.AddBeforeSelf( run ); _runs = this.Xml.Elements( XName.Get( "r", Document.w.NamespaceName ) ).ToList(); - HelperFunctions.RenumberIDs( Document ); } } @@ -4441,14 +4510,14 @@ public void ReplaceAtBookmark( string text, string bookmarkName, Formatting form { var bookmarkEnds = this.Xml.Descendants( XName.Get( "bookmarkEnd", Document.w.NamespaceName ) ); if( bookmarkEnds.Count() > 1 ) - throw new InvalidDataException("Unsupported exception: Paragraph do not contains the expected bookmarkStart and contains more than 1 bookmarkEnd."); + throw new InvalidDataException( "Unsupported exception: Paragraph do not contains the expected bookmarkStart and contains more than 1 bookmarkEnd." ); if( bookmarkEnds.Count() == 0 ) throw new InvalidDataException( "Unsupported exception: Paragraph do not contains a bookmarkStart or a bookmarkEnd." ); bookmarkStartId = bookmarkEnds.First().Attribute( XName.Get( "id", Document.w.NamespaceName ) ).Value; nextNode = this.Xml.Element( XName.Get( "r", Document.w.NamespaceName ) ); bookmarkStart = this.Xml.Element( XName.Get( "pPr", Document.w.NamespaceName ) ); - } + } XElement nextXElement = null; while( nextNode != null ) @@ -4530,7 +4599,16 @@ public void RemoveBookmark( string bookmarkName ) var bookmarkEndXml = this.Xml.Descendants( XName.Get( "bookmarkEnd", Document.w.NamespaceName ) ) .Where( x => x.Attribute( XName.Get( "id", Document.w.NamespaceName ) )?.Value == bookmarkStartId ) .FirstOrDefault(); - Debug.Assert( bookmarkEndXml != null, "Can't find bookmark end."); + // bookmarkEnd is not part of this paragraph, look for it in Document. + if( bookmarkEndXml == null ) + { + bookmarkEndXml = this.Document.Xml.Descendants( XName.Get( "bookmarkEnd", Document.w.NamespaceName ) ) + .Where( x => x.Attribute( XName.Get( "id", Document.w.NamespaceName ) )?.Value == bookmarkStartId ) + .FirstOrDefault(); + } + + if( bookmarkEndXml == null ) + return; bookmarkStartXml.Remove(); bookmarkEndXml.Remove(); @@ -4580,7 +4658,7 @@ public Paragraph KeepLinesTogether( bool keepLinesTogether = true ) } return this; - } + } [Obsolete( "Instead use : InsertHorizontalLine( HorizontalBorderPosition position, BorderStyle borderStyle, int size, int space, Color? color )" )] public void InsertHorizontalLine( HorizontalBorderPosition position = HorizontalBorderPosition.bottom, string lineType = "single", int size = 6, int space = 1, string color = "auto" ) @@ -4777,6 +4855,16 @@ internal XElement GetOrCreate_pPr_ind() return ind; } + internal int GetTabStopPositionsCount() + { + var pPr = this.GetOrCreate_pPr(); + var tabs = pPr.Element( XName.Get( "tabs", Document.w.NamespaceName ) ); + if( tabs == null ) + return 0; + + return tabs.Elements().Count(); + } + internal void RemoveHyperlinkRecursive( XElement xml, int index, ref int count, ref bool found ) { if( xml.Name.LocalName.Equals( "hyperlink", StringComparison.CurrentCultureIgnoreCase ) ) @@ -4828,8 +4916,8 @@ static internal Picture CreatePicture( Document document, string id, string name // ooxml uses image size in EMU : // image in inches(in) is : pt / 72 // image in EMU is : in * 914400 - cx = Convert.ToInt64( img.Width * (72f / img.HorizontalResolution) * Picture.EmusInPixel); - cy = Convert.ToInt64( img.Height * (72f / img.VerticalResolution) * Picture.EmusInPixel); + cx = Convert.ToInt64( img.Width * ( 72f / img.HorizontalResolution ) * Picture.EmusInPixel ); + cy = Convert.ToInt64( img.Height * ( 72f / img.VerticalResolution ) * Picture.EmusInPixel ); } } @@ -4837,17 +4925,9 @@ static internal Picture CreatePicture( Document document, string id, string name ( string.Format( @" - - - - left - - - top - + - @@ -4905,7 +4985,7 @@ internal static XElement CreateEdit( EditType t, DateTime edit_time, object cont { if( t == EditType.del ) { - foreach( object o in (IEnumerable)content ) + foreach( object o in ( IEnumerable )content ) { if( o is XElement ) { @@ -5053,11 +5133,11 @@ internal XElement[] SplitEdit( XElement edit, int index, EditType type ) XElement[] splitRun = Run.SplitRun( run, index, type ); - XElement splitLeft = new XElement( edit.Name, edit.Attributes(), run.Xml.ElementsBeforeSelf(), splitRun[ 0 ] ); + 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() ); + XElement splitRight = new XElement( edit.Name, edit.Attributes(), splitRun[1], run.Xml.ElementsAfterSelf() ); if( GetElementTextLength( splitRight ) == 0 ) splitRight = null; @@ -5077,8 +5157,10 @@ internal string GetOrGenerateRel( Picture p ) // Search for a relationship with a TargetUri that points at this Image. string id = null; - foreach( var r in this.PackagePart.GetRelationshipsByType( Document.RelationshipImage ) ) + var imageRelationships = this.PackagePart.GetRelationshipsByType( Document.RelationshipImage ); + for( int i = imageRelationships.Count() - 1; i >= 0; --i ) { + var r = imageRelationships.ElementAt( i ); if( string.Equals( r.TargetUri.OriginalString, image_uri_string, StringComparison.Ordinal ) ) { id = r.Id; @@ -5147,13 +5229,13 @@ internal void ApplyTextFormattingProperty( XName textFormatPropName, string valu if( content as XAttribute != null ) { // Add or Update the attribute to the last element - if( lastElement.Attribute( ( (XAttribute)( content ) ).Name ) == null ) + if( lastElement.Attribute( ( ( XAttribute )( content ) ).Name ) == null ) { lastElement.Add( content ); } else { - lastElement.Attribute( ( (XAttribute)( content ) ).Name ).Value = ( (XAttribute)( content ) ).Value; + lastElement.Attribute( ( ( XAttribute )( content ) ).Name ).Value = ( ( XAttribute )( content ) ).Value; } } return; @@ -5185,13 +5267,13 @@ internal void ApplyTextFormattingProperty( XName textFormatPropName, string valu { foreach( object property in fontProperties ) { - if( last.Attribute( ( (XAttribute)( property ) ).Name ) == null ) + if( last.Attribute( ( ( XAttribute )( property ) ).Name ) == null ) { last.Add( property ); } else { - last.Attribute( ( (XAttribute)( property ) ).Name ).Value = ( (XAttribute)( property ) ).Value; + last.Attribute( ( ( XAttribute )( property ) ).Name ).Value = ( ( XAttribute )( property ) ).Value; } } } @@ -5199,13 +5281,13 @@ internal void ApplyTextFormattingProperty( XName textFormatPropName, string valu if( content as XAttribute != null )//If content is an attribute { - if( last.Attribute( ( (XAttribute)( content ) ).Name ) == null ) + if( last.Attribute( ( ( XAttribute )( content ) ).Name ) == null ) { last.Add( content ); //Add this attribute if element doesn't have it } else { - last.Attribute( ( (XAttribute)( content ) ).Name ).Value = ( (XAttribute)( content ) ).Value; //Apply value only if element already has it + last.Attribute( ( ( XAttribute )( content ) ).Name ).Value = ( ( XAttribute )( content ) ).Value; //Apply value only if element already has it } } else @@ -5243,7 +5325,7 @@ internal bool IsLineSpacingRuleExactlyOrAtLeast() var lineRule = spacing.Attribute( XName.Get( "lineRule", Document.w.NamespaceName ) ); if( lineRule != null ) { - return ( (lineRule.Value == "exact") || ( lineRule.Value == "atLeast" ) ); + return ( ( lineRule.Value == "exact" ) || ( lineRule.Value == "atLeast" ) ); } } @@ -5310,12 +5392,60 @@ internal bool IsInTOC() if( ( sdt != null ) && ( sdt.Name == XName.Get( "sdt", Document.w.NamespaceName ) ) ) { return ( sdt.Descendants( XName.Get( "docPartGallery", Document.w.NamespaceName ) ) - .FirstOrDefault( x => x.Attribute( XName.Get( "val", Document.w.NamespaceName ) ).Value == "Table of Contents" ) != null ); + .FirstOrDefault( x => x.Attribute( XName.Get( "val", Document.w.NamespaceName ) ).Value == "Table of Contents" ) != null ); } } return false; } + internal bool IsInTOCVisible() + { + if( this.IsInTOC() ) + { + var sdtContent = this.Xml.Parent; + if( ( sdtContent != null ) && ( sdtContent.Name == XName.Get( "sdtContent", Document.w.NamespaceName ) ) ) + { + if( this.StyleId.StartsWith( "TOC" ) ) + { + var styleDigit = this.StyleId.Where( c => char.IsDigit( c ) ); + // TOCHeading + if( styleDigit.Count() <= 0 ) + return true; + + var sdt = sdtContent.Parent; + if( ( sdt != null ) && ( sdt.Name == XName.Get( "sdt", Document.w.NamespaceName ) ) ) + { + var tocSwitches = sdt.Descendants( XName.Get( "instrText", Document.w.NamespaceName ) ).FirstOrDefault( instrText => instrText.Value.Contains( "TOC" ) ); + if( tocSwitches != null ) + { + var includedHeadingStylesIndex = tocSwitches.Value.IndexOf( "o" ); + if( includedHeadingStylesIndex >= 0 ) + { + var bound1 = tocSwitches.Value.IndexOf( "\"", includedHeadingStylesIndex ) + 1; + var bound2 = tocSwitches.Value.IndexOf( "\"", bound1 ); + var includedHeadingStyles = tocSwitches.Value.Substring( bound1, bound2 - bound1 ); + + var maxStyle = int.Parse( includedHeadingStyles[includedHeadingStyles.Length - 1].ToString() ); + int counter = 1; + var styleDigitValue = int.Parse( string.Concat( styleDigit ) ); + while( counter <= maxStyle ) + { + if( styleDigitValue == counter ) + return true; + ++counter; + } + + return false; + } + } + } + } + } + } + + return true; + } + internal List GetSdtContentRuns() { List result = null; @@ -5345,7 +5475,7 @@ internal List GetSdtContentRuns() internal bool IsInSdt() { - return (this.GetParentSdt() != null); + return ( this.GetParentSdt() != null ); } internal XElement GetParentSdt() @@ -5430,11 +5560,14 @@ private void ApplyFormattingFrom( ref Formatting newFormatting, Formatting sourc private void RebuildDocProperties() { - docProperties = - ( - from xml in Xml.Descendants( XName.Get( "fldSimple", Document.w.NamespaceName ) ) - select new DocProperty( Document, xml ) - ).ToList(); + if( this.Xml != null ) + { + docProperties = + ( + from xml in Xml.Descendants( XName.Get( "fldSimple", Document.w.NamespaceName ) ) + select new DocProperty( Document, xml ) + ).ToList(); + } } private XElement GetParagraphNumberProperties() @@ -5485,15 +5618,16 @@ private List GetPictures( string localName, string localNameEquals, str ( from p in Xml.Descendants() where ( p.Name.LocalName == localName ) - let id = + let ids = ( from e in p.Descendants() where e.Name.LocalName.Equals( localNameEquals ) select e.Attribute( XName.Get( attributeName, "http://schemas.openxmlformats.org/officeDocument/2006/relationships" ) ).Value - ).SingleOrDefault() - where id != null - let img = new Image( this.Document, this.PackagePart.GetRelationship( id ) ) - select new Picture( this.Document, p, img ) { PackagePart = this.PackagePart } + ) + where (ids != null) && (ids.Count() > 0) + from id in ids + select new Picture( this.Document, p, new Image( this.Document, this.PackagePart.GetRelationship( id ) ) ) { PackagePart = this.PackagePart } + ).ToList(); return pictures; @@ -5516,15 +5650,15 @@ select e.Attribute( XName.Get( attributeName, "http://schemas.openxmlformats.org + + private void ReplaceAtBookmark_Core( string text, XElement bookmark, Formatting formatting = null ) { - var xElementList = HelperFunctions.FormatInput( text, (formatting != null) ? formatting.Xml : null ); + var xElementList = HelperFunctions.FormatInput( text, ( formatting != null ) ? formatting.Xml : null ); bookmark.AddAfterSelf( xElementList ); _runs = this.Xml.Elements( XName.Get( "r", Document.w.NamespaceName ) ).ToList(); - - HelperFunctions.RenumberIDs( this.Document ); } private void AddParagraphStyleIfNotPresent( string wantedParagraphStyleId ) @@ -5543,7 +5677,7 @@ private void AddParagraphStyleIfNotPresent( string wantedParagraphStyleId ) } // Check if this Paragraph StyleName exists in _styles. - var paragraphStyleExist = HelperFunctions.GetParagraphStyleFromStyleId( this.Document, wantedParagraphStyleId ) != null; + var paragraphStyleExist = HelperFunctions.GetParagraphStyleFromStyleId( this.Document, wantedParagraphStyleId ) != null; // This Paragraph StyleId doesn't exists in _styles, add it. if( !paragraphStyleExist ) @@ -5723,11 +5857,11 @@ static internal XElement[] SplitRun( Run r, int index, EditType type = EditType. Text t = r.GetFirstTextEffectedByEdit( index, type ); XElement[] splitText = Text.SplitText( t, index ); - XElement splitLeft = new XElement( r.Xml.Name, r.Xml.Attributes(), r.Xml.Element( XName.Get( "rPr", Document.w.NamespaceName ) ), t.Xml.ElementsBeforeSelf().Where( n => n.Name.LocalName != "rPr" ), splitText[ 0 ] ); + XElement splitLeft = new XElement( r.Xml.Name, r.Xml.Attributes(), r.Xml.Element( XName.Get( "rPr", Document.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.Xml.Name, r.Xml.Attributes(), r.Xml.Element( XName.Get( "rPr", Document.w.NamespaceName ) ), splitText[ 1 ], t.Xml.ElementsAfterSelf().Where( n => n.Name.LocalName != "rPr" ) ); + XElement splitRight = new XElement( r.Xml.Name, r.Xml.Attributes(), r.Xml.Element( XName.Get( "rPr", Document.w.NamespaceName ) ), splitText[1], t.Xml.ElementsAfterSelf().Where( n => n.Name.LocalName != "rPr" ) ); if( Paragraph.GetElementTextLength( splitRight ) == 0 ) splitRight = null; diff --git a/Xceed.Document.NET/Src/Picture.cs b/Xceed.Document.NET/Src/Picture.cs index a057fb48..5e528849 100644 --- a/Xceed.Document.NET/Src/Picture.cs +++ b/Xceed.Document.NET/Src/Picture.cs @@ -306,24 +306,34 @@ internal Picture( Document document, XElement i, Image image ) : base( document, { _img = image; - var imageId = - ( - from e in Xml.Descendants() - where e.Name.LocalName.Equals( "blip" ) - select e.Attribute( XName.Get( "embed", "http://schemas.openxmlformats.org/officeDocument/2006/relationships" ) ).Value - ).SingleOrDefault(); - - _id = ( imageId != null ) - ? imageId + _id = ( image != null ) + ? image.Id : ( from e in Xml.Descendants() where e.Name.LocalName.Equals( "imagedata" ) select e.Attribute( XName.Get( "id", "http://schemas.openxmlformats.org/officeDocument/2006/relationships" ) ).Value ).FirstOrDefault(); + var pic = this.Xml.Descendants( XName.Get( "pic", Document.pic.NamespaceName ) ).FirstOrDefault( p => + { + var id = + ( + from e in p.Descendants() + where e.Name.LocalName.Equals( "blip" ) + select e.Attribute( XName.Get( "embed", "http://schemas.openxmlformats.org/officeDocument/2006/relationships" ) ).Value + ); + + return (id.FirstOrDefault() == _id); + } ); + + if( pic == null ) + { + pic = this.Xml; + } + var nameToFind = ( - from e in Xml.Descendants() + from e in pic.Descendants() let a = e.Attribute( XName.Get( "name" ) ) where ( a != null ) select a.Value @@ -332,7 +342,7 @@ select a.Value _name = ( nameToFind != null ) ? nameToFind : ( - from e in Xml.Descendants() + from e in pic.Descendants() let a = e.Attribute( XName.Get( "title" ) ) where ( a != null ) select a.Value @@ -340,7 +350,7 @@ select a.Value _descr = ( - from e in Xml.Descendants() + from e in pic.Descendants() let a = e.Attribute( XName.Get( "descr" ) ) where ( a != null ) select a.Value @@ -348,7 +358,7 @@ select a.Value _cx = ( - from e in Xml.Descendants() + from e in pic.Descendants() let a = e.Attribute( XName.Get( "cx" ) ) where ( a != null ) select long.Parse( a.Value ) @@ -358,7 +368,7 @@ select long.Parse( a.Value ) { var style = ( - from e in Xml.Descendants() + from e in pic.Descendants() let a = e.Attribute( XName.Get( "style" ) ) where ( a != null ) select a @@ -380,7 +390,7 @@ select a _cy = ( - from e in Xml.Descendants() + from e in pic.Descendants() let a = e.Attribute( XName.Get( "cy" ) ) where ( a != null ) select long.Parse( a.Value ) @@ -390,7 +400,7 @@ select long.Parse( a.Value ) { var style = ( - from e in Xml.Descendants() + from e in pic.Descendants() let a = e.Attribute( XName.Get( "style" ) ) where ( a != null ) select a @@ -412,14 +422,14 @@ select a _xfrm = ( - from d in Xml.Descendants() + from d in pic.Descendants() where d.Name.LocalName.Equals( "xfrm" ) select d ).FirstOrDefault(); _prstGeom = ( - from d in Xml.Descendants() + from d in pic.Descendants() where d.Name.LocalName.Equals( "prstGeom" ) select d ).FirstOrDefault(); diff --git a/Xceed.Document.NET/Src/Section.cs b/Xceed.Document.NET/Src/Section.cs index 346dabd8..51d857dd 100644 --- a/Xceed.Document.NET/Src/Section.cs +++ b/Xceed.Document.NET/Src/Section.cs @@ -22,7 +22,7 @@ COMMUNITY LICENSE AGREEMENT (for non-commercial use) as published at using System.Text.RegularExpressions; using System.Xml.Linq; using System.Linq; -using System.Globalization; +using System.ComponentModel; namespace Xceed.Document.NET { @@ -38,8 +38,10 @@ public class Section : Container #region Private Members private static float _pageSizeMultiplier = 20.0f; + private NoteProperties _footnoteProperties; + private NoteProperties _endnoteProperties; - #endregion +#endregion #region Properties @@ -77,6 +79,74 @@ public Footers Footers internal set; } + public NoteProperties FootnoteProperties + { + get + { + if( _footnoteProperties != null ) + return _footnoteProperties; + + _footnoteProperties = this.GetNoteProperties( "footnotePr" ); + if( _footnoteProperties != null ) + { + _footnoteProperties.PropertyChanged += this.FootnoteProperties_PropertyChanged; + } + + return _footnoteProperties; + } + + set + { + if( _footnoteProperties != null ) + { + _footnoteProperties.PropertyChanged -= this.FootnoteProperties_PropertyChanged; + } + + _footnoteProperties = value; + + if( _footnoteProperties != null ) + { + _footnoteProperties.PropertyChanged += this.FootnoteProperties_PropertyChanged; + } + + this.SetNoteProperties( "footnotePr", _footnoteProperties ); + } + } + + public NoteProperties EndnoteProperties + { + get + { + if( _endnoteProperties != null ) + return _endnoteProperties; + + _endnoteProperties = this.GetNoteProperties( "endnotePr" ); + if( _endnoteProperties != null ) + { + _endnoteProperties.PropertyChanged += this.EndnoteProperties_PropertyChanged; + } + + return _endnoteProperties; + } + + set + { + if( _endnoteProperties != null ) + { + _endnoteProperties.PropertyChanged -= this.EndnoteProperties_PropertyChanged; + } + + _endnoteProperties = value; + + if( _endnoteProperties != null ) + { + _endnoteProperties.PropertyChanged += this.EndnoteProperties_PropertyChanged; + } + + this.SetNoteProperties( "endnotePr", _endnoteProperties ); + } + } + public Headers Headers { get; @@ -901,6 +971,98 @@ private Section InsertSection( bool trackChanges, bool isPageBreak ) return this.Document.Sections.FirstOrDefault( section => section.Xml == sctPr ); } + private NoteProperties GetNoteProperties( string propertiesType ) + { + var notePr = this.Xml.Element( XName.Get( propertiesType, Document.w.NamespaceName ) ); + if( notePr != null ) + { + var numberFormat = NoteNumberFormat.number; + var numberStart = 1; + + var numFmt = notePr.Element( XName.Get( "numFmt", Document.w.NamespaceName ) ); + if( numFmt != null ) + { + var val = numFmt.GetAttribute( XName.Get( "val", Document.w.NamespaceName ) ); + if( !string.IsNullOrEmpty( val ) ) + { + NoteNumberFormat enumNumberFormat; + numberFormat = Enum.TryParse( val, out enumNumberFormat ) ? enumNumberFormat : NoteNumberFormat.number; + } + } + + var numStart = notePr.Element( XName.Get( "numStart", Document.w.NamespaceName ) ); + if( numStart != null ) + { + var val = numStart.GetAttribute( XName.Get( "val", Document.w.NamespaceName ) ); + if( !string.IsNullOrEmpty( val ) ) + { + numberStart = int.Parse( val ); + } + } + + return new NoteProperties() + { + NumberFormat = numberFormat, + NumberStart = numberStart + }; + } + + return null; + } + + private void SetNoteProperties( string propertiesType, NoteProperties newValue ) + { + if( newValue != null ) + { + var notePr = this.Xml.Element( XName.Get( propertiesType, Document.w.NamespaceName ) ); + if( notePr == null ) + { + this.Xml.Add( new XElement( XName.Get( propertiesType, Document.w.NamespaceName ) ) ); + notePr = this.Xml.Element( XName.Get( propertiesType, Document.w.NamespaceName ) ); + } + + var numFmt = notePr.Element( XName.Get( "numFmt", Document.w.NamespaceName ) ); + if( numFmt == null ) + { + notePr.Add( new XElement( XName.Get( "numFmt", Document.w.NamespaceName ) ) ); + numFmt = notePr.Element( XName.Get( "numFmt", Document.w.NamespaceName ) ); + } + + numFmt.SetAttributeValue( XName.Get( "val", Document.w.NamespaceName ), newValue.NumberFormat ); + + var numStart = notePr.Element( XName.Get( "numStart", Document.w.NamespaceName ) ); + if( numStart == null ) + { + notePr.Add( new XElement( XName.Get( "numStart", Document.w.NamespaceName ) ) ); + numStart = notePr.Element( XName.Get( "numStart", Document.w.NamespaceName ) ); + } + + numStart.SetAttributeValue( XName.Get( "val", Document.w.NamespaceName ), newValue.NumberStart ); + } + else + { + var notePr = this.Xml.Element( XName.Get( propertiesType, Document.w.NamespaceName ) ); + if( notePr != null ) + { + notePr.Remove(); + } + } + } + #endregion + + #region Event Handlers + + private void FootnoteProperties_PropertyChanged( object sender, PropertyChangedEventArgs e ) + { + this.SetNoteProperties( "footnotePr", _footnoteProperties ); + } + + private void EndnoteProperties_PropertyChanged( object sender, PropertyChangedEventArgs e ) + { + this.SetNoteProperties( "endnotePr", _endnoteProperties ); + } + +#endregion } } diff --git a/Xceed.Document.NET/Src/Table.cs b/Xceed.Document.NET/Src/Table.cs index 722f29d3..7182ee17 100644 --- a/Xceed.Document.NET/Src/Table.cs +++ b/Xceed.Document.NET/Src/Table.cs @@ -2740,7 +2740,7 @@ public List Cells { List cells = ( - from c in Xml.Elements( XName.Get( "tc", Document.w.NamespaceName ) ) + from c in Xml.Descendants( XName.Get( "tc", Document.w.NamespaceName ) ) select new Cell( this, Document, c ) ).ToList(); @@ -2966,13 +2966,14 @@ public void MergeCells( int startIndex, int endIndex ) int gridSpanSum = 0; // Foreach each Cell between startIndex and endIndex inclusive. - foreach( Cell c in Cells.Where( ( z, i ) => i > startIndex && i <= endIndex ) ) + var cellsToMerge = this.Cells.Where( ( z, i ) => i > startIndex && i <= endIndex ); + foreach( var c in cellsToMerge ) { - XElement tcPr = c.Xml.Element( XName.Get( "tcPr", Document.w.NamespaceName ) ); - XElement gridSpan = tcPr?.Element( XName.Get( "gridSpan", Document.w.NamespaceName ) ); + var tcPr = c.Xml.Element( XName.Get( "tcPr", Document.w.NamespaceName ) ); + var gridSpan = tcPr?.Element( XName.Get( "gridSpan", Document.w.NamespaceName ) ); if( gridSpan != null ) { - XAttribute val = gridSpan.Attribute( XName.Get( "val", Document.w.NamespaceName ) ); + var val = gridSpan.Attribute( XName.Get( "val", Document.w.NamespaceName ) ); int value; if( val != null && HelperFunctions.TryParseInt( val.Value, out value ) ) @@ -2980,7 +2981,9 @@ public void MergeCells( int startIndex, int endIndex ) } // Add this cells Pragraph to the merge start Cell. - Cells[ startIndex ].Xml.Add( c.Xml.Elements( XName.Get( "p", Document.w.NamespaceName ) ) ); + this.Cells[ startIndex ].Xml.Add( c.Xml.Elements( XName.Get( "p", Document.w.NamespaceName ) ) ); + + this.Cells[startIndex].Width += c.Width; // Remove this Cell. c.Xml.Remove(); @@ -3920,6 +3923,9 @@ public int RowSpan for( var i = rowIndex + 1; i < rows.Count; ++i ) { + if( cellIndex >= rows[i].Cells.Count ) + break; + var cell = rows[ i ].Cells[ cellIndex ]; var cell_tcPr = cell.Xml.Element( XName.Get( "tcPr", Document.w.NamespaceName ) ); var cell_vMerge = cell_tcPr?.Element( XName.Get( "vMerge", Document.w.NamespaceName ) ); diff --git a/Xceed.Document.NET/Src/TableOfContents.cs b/Xceed.Document.NET/Src/TableOfContents.cs index 5b1ecf7d..a3d8f9e3 100644 --- a/Xceed.Document.NET/Src/TableOfContents.cs +++ b/Xceed.Document.NET/Src/TableOfContents.cs @@ -34,6 +34,8 @@ public class TableOfContents : DocumentElement private const string HeaderStyle = "TOCHeading"; private const int RightTabPos = 9010; + private static float DefaultIndentationLeft = 11f * 20f; + #endregion #region Internal Methods @@ -66,6 +68,8 @@ internal static TableOfContents CreateTableOfContents( Document document, string + + @@ -135,6 +139,36 @@ private static void InitElement( string elementName, Document document, string h var xml = XElement.Load( reader ); document._styles.Root.Add( xml ); } + if( !TableOfContents.HasStyle( document, "TOC5", "paragraph" ) ) + { + var reader = XmlReader.Create( new StringReader( string.Format( XmlTemplates.TableOfContentsElementStyleBase, "TOC5", "toc 5", XmlTemplates.TableOfContentsElementDefaultIndentation * 4 ) ) ); + var xml = XElement.Load( reader ); + document._styles.Root.Add( xml ); + } + if( !TableOfContents.HasStyle( document, "TOC6", "paragraph" ) ) + { + var reader = XmlReader.Create( new StringReader( string.Format( XmlTemplates.TableOfContentsElementStyleBase, "TOC6", "toc 6", XmlTemplates.TableOfContentsElementDefaultIndentation * 5 ) ) ); + var xml = XElement.Load( reader ); + document._styles.Root.Add( xml ); + } + if( !TableOfContents.HasStyle( document, "TOC7", "paragraph" ) ) + { + var reader = XmlReader.Create( new StringReader( string.Format( XmlTemplates.TableOfContentsElementStyleBase, "TOC7", "toc 7", XmlTemplates.TableOfContentsElementDefaultIndentation * 6 ) ) ); + var xml = XElement.Load( reader ); + document._styles.Root.Add( xml ); + } + if( !TableOfContents.HasStyle( document, "TOC8", "paragraph" ) ) + { + var reader = XmlReader.Create( new StringReader( string.Format( XmlTemplates.TableOfContentsElementStyleBase, "TOC8", "toc 8", XmlTemplates.TableOfContentsElementDefaultIndentation * 7 ) ) ); + var xml = XElement.Load( reader ); + document._styles.Root.Add( xml ); + } + if( !TableOfContents.HasStyle( document, "TOC9", "paragraph" ) ) + { + var reader = XmlReader.Create( new StringReader( string.Format( XmlTemplates.TableOfContentsElementStyleBase, "TOC9", "toc 9", XmlTemplates.TableOfContentsElementDefaultIndentation * 8 ) ) ); + var xml = XElement.Load( reader ); + document._styles.Root.Add( xml ); + } if( !TableOfContents.HasStyle( document, "Hyperlink", "character" ) ) { var reader = XmlReader.Create( new StringReader( string.Format( XmlTemplates.TableOfContentsHyperLinkStyleBase ) ) ); @@ -226,6 +260,9 @@ private static string BuildSwitchString( IDictionaryfull false bin\Debug\ - TRACE;DEBUG + TRACE;DEBUG;NETFRAMEWORK prompt 4 @@ -53,7 +53,7 @@ none true bin\Release\ - TRACE + TRACE;NETFRAMEWORK prompt 4 AllRules.ruleset @@ -100,12 +100,14 @@ + + @@ -113,6 +115,8 @@ + + @@ -157,6 +161,9 @@ + + + public override void Save( string password = "" ) { - ValidatePasswordProtection( password ); + if( this.IsPackageClosed( _package ) ) { @@ -316,12 +368,6 @@ public override void Save( string password = "" ) return; } - // Save the main document - using( TextWriter tw = new StreamWriter( new PackagePartStream( this.PackagePart.GetStream( FileMode.Create, FileAccess.Write ) ) ) ) - { - _mainDoc.Save( tw, SaveOptions.None ); - } - if( ( _settings == null ) ) { using( TextReader textReader = new StreamReader( _settingsPart.GetStream() ) ) @@ -330,6 +376,14 @@ public override void Save( string password = "" ) } } + ValidatePasswordProtection( password ); + + // Save the main document + using( TextWriter tw = new StreamWriter( new PackagePartStream( this.PackagePart.GetStream( FileMode.Create, FileAccess.Write ) ) ) ) + { + _mainDoc.Save( tw, SaveOptions.None ); + } + // Save the header/footer content. this.SaveHeadersFooters(); @@ -387,36 +441,11 @@ public override void Save( string password = "" ) } } - // Close the document so that it can be saved in .NETStandard. + // Close the document so that it can be saved in .NETStandard/NET5. _package.Close(); #region Save this document back to a file or stream, that was specified by the user at save time. - if( _filename != null ) - { - var saveFileName = _filename.EndsWith( ".docx" ) || _filename.EndsWith( ".doc" ) ? _filename : _filename + ".docx"; - using( FileStream fs = new FileStream( saveFileName, FileMode.Create ) ) - { - if( _memoryStream.CanSeek ) - { - // Write to the beginning of the stream - _memoryStream.Position = 0; - HelperFunctions.CopyStream( _memoryStream, fs ); - } - else - fs.Write( _memoryStream.ToArray(), 0, ( int )_memoryStream.Length ); - } - } - else if( _stream.CanSeek ) //Check if stream can be seeked to support System.Web.HttpResponseStream. - { - // Set the length of this stream to 0 - _stream.SetLength( 0 ); - - // Write to the beginning of the stream - _stream.Position = 0; - - _memoryStream.WriteTo( _stream ); - _memoryStream.Flush(); - } + this.WriteToFileOrStream(); #endregion } @@ -447,6 +476,18 @@ public override Xceed.Document.NET.Document Copy() } } + + + + + + + + + + + + #endregion #region Internal Methods @@ -583,7 +624,7 @@ internal void ValidatePasswordProtection( string password ) if( this.IsPasswordProtected ) { if( _settings == null ) - throw new NullReferenceException(); + throw new NullReferenceException(); var documentProtection = _settings.Descendants( XName.Get( "documentProtection", w.NamespaceName ) ).FirstOrDefault(); @@ -610,6 +651,93 @@ internal void ValidatePasswordProtection( string password ) #region Private Method + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + private void WriteToFileOrStream() + { + if( _filename != null ) + { + var saveFileName = _filename.EndsWith( ".docx" ) || _filename.EndsWith( ".doc" ) ? _filename : _filename + ".docx"; + using( var fs = new FileStream( saveFileName, FileMode.Create ) ) + { + if( _memoryStream.CanSeek ) + { + // Write to the beginning of the stream + _memoryStream.Position = 0; + HelperFunctions.CopyStream( _memoryStream, fs ); + } + else + fs.Write( _memoryStream.ToArray(), 0, (int)_memoryStream.Length ); + } + } + else if( _stream.CanSeek ) //Check if stream can be seeked to support System.Web.HttpResponseStream. + { + // Set the length of this stream to 0 + _stream.SetLength( 0 ); + + // Write to the beginning of the stream + _stream.Position = 0; + + _memoryStream.WriteTo( _stream ); + _memoryStream.Flush(); + } + } + private bool IsPackageClosed( Package package ) { if( package == null ) diff --git a/Xceed.Words.NET/Xceed.Words.NET.csproj b/Xceed.Words.NET/Xceed.Words.NET.csproj index 2dd97ece..8dafac4f 100644 --- a/Xceed.Words.NET/Xceed.Words.NET.csproj +++ b/Xceed.Words.NET/Xceed.Words.NET.csproj @@ -20,7 +20,7 @@ full false bin\Debug\ - DEBUG;TRACE + TRACE;DEBUG;NETFRAMEWORK prompt 4 @@ -30,7 +30,7 @@ pdbonly true bin\Release\ - TRACE + TRACE;NETFRAMEWORK prompt 4 @@ -39,6 +39,8 @@ + +