You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817
  1. /*************************************************************************************
  2. DocX – DocX is the community edition of Xceed Words for .NET
  3. Copyright (C) 2009-2016 Xceed Software Inc.
  4. This program is provided to you under the terms of the Microsoft Public
  5. License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
  6. For more features and fast professional support,
  7. pick up Xceed Words for .NET at https://xceed.com/xceed-words-for-net/
  8. ***********************************************************************************/
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Linq;
  12. using System.Text;
  13. using System.IO.Packaging;
  14. using System.Xml.Linq;
  15. using System.IO;
  16. using System.Reflection;
  17. using System.IO.Compression;
  18. using System.Security.Principal;
  19. using System.Globalization;
  20. using System.Xml;
  21. namespace Xceed.Words.NET
  22. {
  23. internal static class HelperFunctions
  24. {
  25. public const string DOCUMENT_DOCUMENTTYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml";
  26. public const string TEMPLATE_DOCUMENTTYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml";
  27. public const string SETTING_DOCUMENTTYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml";
  28. public const string MACRO_DOCUMENTTYPE = "application/vnd.ms-word.document.macroEnabled.main+xml";
  29. internal static readonly char[] RestrictedXmlCharacters = new char[]
  30. {
  31. '\x1','\x2','\x3','\x4','\x5','\x6','\x7','\x8','\xb','\xc','\xe','\xf',
  32. '\x10','\x11','\x12','\x13','\x14','\x15','\x16','\x17','\x18','\x19','\x1a','\x1b','\x1c','\x1e','\x1f',
  33. '\x7f','\x80','\x81','\x82','\x83','\x84','\x86','\x87','\x88','\x89','\x8a','\x8b','\x8c','\x8d','\x8e','\x8f',
  34. '\x90','\x91','\x92','\x93','\x94','\x95','\x96','\x97','\x98','\x99','\x9a','\x9b','\x9c','\x9d','\x9e','\x9f'
  35. };
  36. internal static void CreateRelsPackagePart( DocX Document, Uri uri )
  37. {
  38. PackagePart pp = Document._package.CreatePart( uri, DocX.ContentTypeApplicationRelationShipXml, CompressionOption.Maximum );
  39. using( TextWriter tw = new StreamWriter( new PackagePartStream( pp.GetStream() ) ) )
  40. {
  41. XDocument d = new XDocument
  42. (
  43. new XDeclaration( "1.0", "UTF-8", "yes" ),
  44. new XElement( XName.Get( "Relationships", DocX.rel.NamespaceName ) )
  45. );
  46. var root = d.Root;
  47. d.Save( tw );
  48. }
  49. }
  50. internal static int GetSize( XElement Xml )
  51. {
  52. switch( Xml.Name.LocalName )
  53. {
  54. case "tab":
  55. return (Xml.Parent.Name.LocalName != "tabs" ) ? 1 : 0;
  56. case "br":
  57. return 1;
  58. case "t":
  59. goto case "delText";
  60. case "delText":
  61. return Xml.Value.Length;
  62. case "tr":
  63. goto case "br";
  64. case "tc":
  65. goto case "br";
  66. default:
  67. return 0;
  68. }
  69. }
  70. internal static string GetText( XElement e )
  71. {
  72. StringBuilder sb = new StringBuilder();
  73. GetTextRecursive( e, ref sb );
  74. return sb.ToString();
  75. }
  76. internal static void GetTextRecursive( XElement Xml, ref StringBuilder sb )
  77. {
  78. sb.Append( ToText( Xml ) );
  79. if( Xml.HasElements )
  80. foreach( XElement e in Xml.Elements() )
  81. GetTextRecursive( e, ref sb );
  82. }
  83. internal static List<FormattedText> GetFormattedText( XElement e )
  84. {
  85. List<FormattedText> alist = new List<FormattedText>();
  86. GetFormattedTextRecursive( e, ref alist );
  87. return alist;
  88. }
  89. internal static void GetFormattedTextRecursive( XElement Xml, ref List<FormattedText> alist )
  90. {
  91. FormattedText ft = ToFormattedText( Xml );
  92. FormattedText last = null;
  93. if( ft != null )
  94. {
  95. if( alist.Count() > 0 )
  96. last = alist.Last();
  97. if( last != null && last.CompareTo( ft ) == 0 )
  98. {
  99. // Update text of last entry.
  100. last.text += ft.text;
  101. }
  102. else
  103. {
  104. if( last != null )
  105. ft.index = last.index + last.text.Length;
  106. alist.Add( ft );
  107. }
  108. }
  109. if( Xml.HasElements )
  110. foreach( XElement e in Xml.Elements() )
  111. GetFormattedTextRecursive( e, ref alist );
  112. }
  113. internal static FormattedText ToFormattedText( XElement e )
  114. {
  115. // The text representation of e.
  116. String text = ToText( e );
  117. if( text == String.Empty )
  118. return null;
  119. // e is a w:t element, it must exist inside a w:r element or a w:tabs, lets climb until we find it.
  120. while( (e != null) && !e.Name.Equals( XName.Get( "r", DocX.w.NamespaceName ) ) && !e.Name.Equals( XName.Get( "tabs", DocX.w.NamespaceName ) ) )
  121. e = e.Parent;
  122. FormattedText ft = new FormattedText();
  123. ft.text = text;
  124. ft.index = 0;
  125. ft.formatting = null;
  126. if( e != null )
  127. {
  128. // e is a w:r element, lets find the rPr element.
  129. XElement rPr = e.Element( XName.Get( "rPr", DocX.w.NamespaceName ) );
  130. // Return text with formatting.
  131. if( rPr != null )
  132. ft.formatting = Formatting.Parse( rPr );
  133. }
  134. return ft;
  135. }
  136. internal static string ToText( XElement e )
  137. {
  138. switch( e.Name.LocalName )
  139. {
  140. case "tab":
  141. // Do not add "\t" for TabStopPositions defined in "tabs".
  142. return ((e.Parent != null) && e.Parent.Name.Equals( XName.Get( "tabs", DocX.w.NamespaceName ) )) ? "" : "\t";
  143. case "br":
  144. return "\n";
  145. case "t":
  146. goto case "delText";
  147. case "delText":
  148. {
  149. return e.Value;
  150. }
  151. case "tr":
  152. goto case "br";
  153. case "tc":
  154. goto case "tab";
  155. default:
  156. return "";
  157. }
  158. }
  159. internal static XElement CloneElement( XElement element )
  160. {
  161. return new XElement
  162. (
  163. element.Name,
  164. element.Attributes(),
  165. element.Nodes().Select
  166. (
  167. n =>
  168. {
  169. XElement e = n as XElement;
  170. if( e != null )
  171. return CloneElement( e );
  172. return n;
  173. }
  174. )
  175. );
  176. }
  177. internal static PackagePart GetMainDocumentPart( Package package )
  178. {
  179. return package.GetParts().Single( p => p.ContentType.Equals( DOCUMENT_DOCUMENTTYPE, StringComparison.CurrentCultureIgnoreCase ) ||
  180. p.ContentType.Equals( TEMPLATE_DOCUMENTTYPE, StringComparison.CurrentCultureIgnoreCase ) ||
  181. p.ContentType.Equals( MACRO_DOCUMENTTYPE, StringComparison.CurrentCultureIgnoreCase ) );
  182. }
  183. internal static PackagePart CreateOrGetSettingsPart( Package package )
  184. {
  185. PackagePart settingsPart;
  186. var settingsUri = new Uri( "/word/settings.xml", UriKind.Relative );
  187. if( !package.PartExists( settingsUri ) )
  188. {
  189. settingsPart = package.CreatePart( settingsUri, HelperFunctions.SETTING_DOCUMENTTYPE, CompressionOption.Maximum );
  190. var mainDocPart = GetMainDocumentPart( package );
  191. mainDocPart.CreateRelationship( settingsUri, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" );
  192. var settings = XDocument.Parse
  193. ( @"<?xml version='1.0' encoding='utf-8' standalone='yes'?>
  194. <w:settings xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:r='http://schemas.openxmlformats.org/officeDocument/2006/relationships' xmlns:m='http://schemas.openxmlformats.org/officeDocument/2006/math' xmlns:v='urn:schemas-microsoft-com:vml' xmlns:w10='urn:schemas-microsoft-com:office:word' xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' xmlns:sl='http://schemas.openxmlformats.org/schemaLibrary/2006/main'>
  195. <w:zoom w:percent='100' />
  196. <w:defaultTabStop w:val='720' />
  197. <w:characterSpacingControl w:val='doNotCompress' />
  198. <w:compat />
  199. <w:rsids>
  200. <w:rsidRoot w:val='00217F62' />
  201. <w:rsid w:val='001915A3' />
  202. <w:rsid w:val='00217F62' />
  203. <w:rsid w:val='00A906D8' />
  204. <w:rsid w:val='00AB5A74' />
  205. <w:rsid w:val='00F071AE' />
  206. </w:rsids>
  207. <m:mathPr>
  208. <m:mathFont m:val='Cambria Math' />
  209. <m:brkBin m:val='before' />
  210. <m:brkBinSub m:val='--' />
  211. <m:smallFrac m:val='off' />
  212. <m:dispDef />
  213. <m:lMargin m:val='0' />
  214. <m:rMargin m:val='0' />
  215. <m:defJc m:val='centerGroup' />
  216. <m:wrapIndent m:val='1440' />
  217. <m:intLim m:val='subSup' />
  218. <m:naryLim m:val='undOvr' />
  219. </m:mathPr>
  220. <w:themeFontLang w:val='en-IE' w:bidi='ar-SA' />
  221. <w:clrSchemeMapping w:bg1='light1' w:t1='dark1' w:bg2='light2' w:t2='dark2' w:accent1='accent1' w:accent2='accent2' w:accent3='accent3' w:accent4='accent4' w:accent5='accent5' w:accent6='accent6' w:hyperlink='hyperlink' w:followedHyperlink='followedHyperlink' />
  222. <w:shapeDefaults>
  223. <o:shapedefaults v:ext='edit' spidmax='2050' />
  224. <o:shapelayout v:ext='edit'>
  225. <o:idmap v:ext='edit' data='1' />
  226. </o:shapelayout>
  227. </w:shapeDefaults>
  228. <w:decimalSymbol w:val='.' />
  229. <w:listSeparator w:val=',' />
  230. </w:settings>"
  231. );
  232. var themeFontLang = settings.Root.Element( XName.Get( "themeFontLang", DocX.w.NamespaceName ) );
  233. themeFontLang.SetAttributeValue( XName.Get( "val", DocX.w.NamespaceName ), CultureInfo.CurrentCulture );
  234. // Save the settings document.
  235. using( TextWriter tw = new StreamWriter( new PackagePartStream( settingsPart.GetStream() ) ) )
  236. {
  237. settings.Save( tw );
  238. }
  239. }
  240. else
  241. {
  242. settingsPart = package.GetPart( settingsUri );
  243. }
  244. return settingsPart;
  245. }
  246. internal static void CreateCustomPropertiesPart( DocX document )
  247. {
  248. var customPropertiesPart = document._package.CreatePart( new Uri( "/docProps/custom.xml", UriKind.Relative ), "application/vnd.openxmlformats-officedocument.custom-properties+xml", CompressionOption.Maximum );
  249. var customPropDoc = new XDocument
  250. (
  251. new XDeclaration( "1.0", "UTF-8", "yes" ),
  252. new XElement
  253. (
  254. XName.Get( "Properties", DocX.customPropertiesSchema.NamespaceName ),
  255. new XAttribute( XNamespace.Xmlns + "vt", DocX.customVTypesSchema )
  256. )
  257. );
  258. using( TextWriter tw = new StreamWriter( new PackagePartStream( customPropertiesPart.GetStream( FileMode.Create, FileAccess.Write ) ) ) )
  259. {
  260. customPropDoc.Save( tw, SaveOptions.None );
  261. }
  262. document._package.CreateRelationship( customPropertiesPart.Uri, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties" );
  263. }
  264. internal static XDocument DecompressXMLResource( string manifest_resource_name )
  265. {
  266. // XDocument to load the compressed Xml resource into.
  267. XDocument document;
  268. // Get a reference to the executing assembly.
  269. Assembly assembly = Assembly.GetExecutingAssembly();
  270. // Open a Stream to the embedded resource.
  271. Stream stream = assembly.GetManifestResourceStream( manifest_resource_name );
  272. // Decompress the embedded resource.
  273. using( GZipStream zip = new GZipStream( stream, CompressionMode.Decompress ) )
  274. {
  275. // Load this decompressed embedded resource into an XDocument using a TextReader.
  276. using( TextReader sr = new StreamReader( zip ) )
  277. {
  278. document = XDocument.Load( sr );
  279. }
  280. }
  281. // Return the decompressed Xml as an XDocument.
  282. return document;
  283. }
  284. /// <summary>
  285. /// If this document does not contain a /word/styles.xml add the default one generated by Microsoft Word.
  286. /// </summary>
  287. /// <param name="package"></param>
  288. /// <returns></returns>
  289. internal static XDocument AddDefaultStylesXml( Package package )
  290. {
  291. XDocument stylesDoc;
  292. // Create the main document part for this package
  293. var word_styles = package.CreatePart( new Uri( "/word/styles.xml", UriKind.Relative ), "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", CompressionOption.Maximum );
  294. stylesDoc = HelperFunctions.DecompressXMLResource( "Xceed.Words.NET.Resources.default_styles.xml.gz" );
  295. var lang = stylesDoc.Root.Element( XName.Get( "docDefaults", DocX.w.NamespaceName ) ).Element( XName.Get( "rPrDefault", DocX.w.NamespaceName ) ).Element( XName.Get( "rPr", DocX.w.NamespaceName ) ).Element( XName.Get( "lang", DocX.w.NamespaceName ) );
  296. lang.SetAttributeValue( XName.Get( "val", DocX.w.NamespaceName ), CultureInfo.CurrentCulture );
  297. // Save /word/styles.xml
  298. using( TextWriter tw = new StreamWriter( new PackagePartStream( word_styles.GetStream( FileMode.Create, FileAccess.Write ) ) ) )
  299. {
  300. stylesDoc.Save( tw, SaveOptions.None );
  301. }
  302. var mainDocumentPart = GetMainDocumentPart( package );
  303. mainDocumentPart.CreateRelationship( word_styles.Uri, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" );
  304. return stylesDoc;
  305. }
  306. internal static XElement CreateEdit( EditType t, DateTime edit_time, object content )
  307. {
  308. if( t == EditType.del )
  309. {
  310. foreach( object o in ( IEnumerable<XElement> )content )
  311. {
  312. if( o is XElement )
  313. {
  314. XElement e = ( o as XElement );
  315. IEnumerable<XElement> ts = e.DescendantsAndSelf( XName.Get( "t", DocX.w.NamespaceName ) );
  316. for( int i = 0; i < ts.Count(); i++ )
  317. {
  318. XElement text = ts.ElementAt( i );
  319. text.ReplaceWith( new XElement( DocX.w + "delText", text.Attributes(), text.Value ) );
  320. }
  321. }
  322. }
  323. }
  324. return
  325. (
  326. new XElement( DocX.w + t.ToString(),
  327. new XAttribute( DocX.w + "id", 0 ),
  328. new XAttribute( DocX.w + "author", WindowsIdentity.GetCurrent().Name ),
  329. new XAttribute( DocX.w + "date", edit_time ),
  330. content )
  331. );
  332. }
  333. internal static XElement CreateTable( int rowCount, int columnCount )
  334. {
  335. if( ( rowCount <= 0 ) || ( columnCount <= 0 ) )
  336. {
  337. throw new ArgumentOutOfRangeException( "Row and Column count must be greater than 0." );
  338. }
  339. int[] columnWidths = new int[columnCount];
  340. for (int i = 0; i < columnCount; i++)
  341. {
  342. columnWidths[i] = 2310;
  343. }
  344. return CreateTable(rowCount, columnWidths);
  345. }
  346. internal static XElement CreateTable( int rowCount, int[] columnWidths )
  347. {
  348. var newTable = new XElement( XName.Get( "tbl", DocX.w.NamespaceName ),
  349. new XElement( XName.Get( "tblPr", DocX.w.NamespaceName ),
  350. new XElement( XName.Get( "tblStyle", DocX.w.NamespaceName ), new XAttribute( XName.Get( "val", DocX.w.NamespaceName ), "TableGrid" ) ),
  351. new XElement( XName.Get( "tblW", DocX.w.NamespaceName ), new XAttribute( XName.Get( "w", DocX.w.NamespaceName ), "5000" ), new XAttribute( XName.Get( "type", DocX.w.NamespaceName ), "auto" ) ),
  352. new XElement( XName.Get( "tblLook", DocX.w.NamespaceName ), new XAttribute( XName.Get( "val", DocX.w.NamespaceName ), "04A0" ) ) ) );
  353. for( int i = 0; i < rowCount; i++ )
  354. {
  355. var row = new XElement( XName.Get( "tr", DocX.w.NamespaceName ) );
  356. for( int j = 0; j < columnWidths.Length; j++ )
  357. {
  358. var cell = HelperFunctions.CreateTableCell();
  359. row.Add( cell );
  360. }
  361. newTable.Add( row );
  362. }
  363. return newTable;
  364. }
  365. /// <summary>
  366. /// Create and return a cell of a table
  367. /// </summary>
  368. internal static XElement CreateTableCell( double w = 2310 )
  369. {
  370. return new XElement( XName.Get( "tc", DocX.w.NamespaceName ),
  371. new XElement( XName.Get( "tcPr", DocX.w.NamespaceName ),
  372. new XElement( XName.Get( "tcW", DocX.w.NamespaceName ),
  373. new XAttribute( XName.Get( "w", DocX.w.NamespaceName ), w ),
  374. new XAttribute( XName.Get( "type", DocX.w.NamespaceName ), "dxa" ) ) ),
  375. new XElement( XName.Get( "p", DocX.w.NamespaceName ), new XElement( XName.Get( "pPr", DocX.w.NamespaceName ) ) ) );
  376. }
  377. internal static void RenumberIDs( DocX document )
  378. {
  379. IEnumerable<XAttribute> trackerIDs =
  380. ( from d in document._mainDoc.Descendants()
  381. where d.Name.LocalName == "ins" || d.Name.LocalName == "del"
  382. select d.Attribute( XName.Get( "id", "http://schemas.openxmlformats.org/wordprocessingml/2006/main" ) ) );
  383. for( int i = 0; i < trackerIDs.Count(); i++ )
  384. trackerIDs.ElementAt( i ).Value = i.ToString();
  385. }
  386. internal static Paragraph GetFirstParagraphEffectedByInsert( DocX document, int index )
  387. {
  388. // This document contains no Paragraphs and insertion is at index 0
  389. if( document.Paragraphs.Count() == 0 && index == 0 )
  390. return null;
  391. foreach( Paragraph p in document.Paragraphs )
  392. {
  393. if( p._endIndex >= index )
  394. return p;
  395. }
  396. throw new ArgumentOutOfRangeException();
  397. }
  398. internal static List<XElement> FormatInput( string text, XElement rPr )
  399. {
  400. var newRuns = new List<XElement>();
  401. var tabRun = new XElement( DocX.w + "tab" );
  402. var breakRun = new XElement( DocX.w + "br" );
  403. var sb = new StringBuilder();
  404. if( string.IsNullOrEmpty( text ) )
  405. {
  406. return newRuns; //I dont wanna get an exception if text == null, so just return empy list
  407. }
  408. char lastCharacter = '\0';
  409. foreach( char c in text )
  410. {
  411. switch( c )
  412. {
  413. case '\t':
  414. if( sb.Length > 0 )
  415. {
  416. var t = new XElement( DocX.w + "t", sb.ToString() );
  417. Xceed.Words.NET.Text.PreserveSpace( t );
  418. newRuns.Add( new XElement( DocX.w + "r", rPr, t ) );
  419. sb = new StringBuilder();
  420. }
  421. newRuns.Add( new XElement( DocX.w + "r", rPr, tabRun ) );
  422. break;
  423. case '\n':
  424. if( lastCharacter == '\r' )
  425. break;
  426. if( sb.Length > 0 )
  427. {
  428. var t = new XElement( DocX.w + "t", sb.ToString() );
  429. Xceed.Words.NET.Text.PreserveSpace( t );
  430. newRuns.Add( new XElement( DocX.w + "r", rPr, t ) );
  431. sb = new StringBuilder();
  432. }
  433. newRuns.Add( new XElement( DocX.w + "r", rPr, breakRun ) );
  434. break;
  435. case '\r':
  436. if( sb.Length > 0 )
  437. {
  438. var t = new XElement( DocX.w + "t", sb.ToString() );
  439. Xceed.Words.NET.Text.PreserveSpace( t );
  440. newRuns.Add( new XElement( DocX.w + "r", rPr, t ) );
  441. sb = new StringBuilder();
  442. }
  443. newRuns.Add( new XElement( DocX.w + "r", rPr, breakRun ) );
  444. break;
  445. default:
  446. if( !RestrictedXmlCharacters.Contains( c ) )
  447. sb.Append( c );
  448. break;
  449. }
  450. lastCharacter = c;
  451. }
  452. if( sb.Length > 0 )
  453. {
  454. var t = new XElement( DocX.w + "t", sb.ToString() );
  455. Xceed.Words.NET.Text.PreserveSpace( t );
  456. newRuns.Add( new XElement( DocX.w + "r", rPr, t ) );
  457. }
  458. return newRuns;
  459. }
  460. internal static XElement[] SplitParagraph( Paragraph p, int index )
  461. {
  462. // In this case edit dosent really matter, you have a choice.
  463. Run r = p.GetFirstRunEffectedByEdit( index, EditType.ins );
  464. XElement[] split;
  465. XElement before, after;
  466. if( r.Xml.Parent.Name.LocalName == "ins" )
  467. {
  468. split = p.SplitEdit( r.Xml.Parent, index, EditType.ins );
  469. before = new XElement( p.Xml.Name, p.Xml.Attributes(), r.Xml.Parent.ElementsBeforeSelf(), split[ 0 ] );
  470. after = new XElement( p.Xml.Name, p.Xml.Attributes(), r.Xml.Parent.ElementsAfterSelf(), split[ 1 ] );
  471. }
  472. else if( r.Xml.Parent.Name.LocalName == "del" )
  473. {
  474. split = p.SplitEdit( r.Xml.Parent, index, EditType.del );
  475. before = new XElement( p.Xml.Name, p.Xml.Attributes(), r.Xml.Parent.ElementsBeforeSelf(), split[ 0 ] );
  476. after = new XElement( p.Xml.Name, p.Xml.Attributes(), r.Xml.Parent.ElementsAfterSelf(), split[ 1 ] );
  477. }
  478. else
  479. {
  480. split = Run.SplitRun( r, index );
  481. before = new XElement( p.Xml.Name, p.Xml.Attributes(), r.Xml.ElementsBeforeSelf(), split[ 0 ] );
  482. after = new XElement( p.Xml.Name, p.Xml.Attributes(), split[ 1 ], r.Xml.ElementsAfterSelf() );
  483. }
  484. if( before.Elements().Count() == 0 )
  485. before = null;
  486. if( after.Elements().Count() == 0 )
  487. after = null;
  488. return new XElement[] { before, after };
  489. }
  490. /// <!--
  491. /// Bug found and fixed by trnilse. To see the change,
  492. /// please compare this release to the previous release using TFS compare.
  493. /// -->
  494. internal static bool IsSameFile( Stream streamOne, Stream streamTwo )
  495. {
  496. int file1byte, file2byte;
  497. if( streamOne.Length != streamTwo.Length )
  498. {
  499. // Return false to indicate files are different
  500. return false;
  501. }
  502. // Read and compare a byte from each file until either a
  503. // non-matching set of bytes is found or until the end of
  504. // file1 is reached.
  505. do
  506. {
  507. // Read one byte from each file.
  508. file1byte = streamOne.ReadByte();
  509. file2byte = streamTwo.ReadByte();
  510. }
  511. while( ( file1byte == file2byte ) && ( file1byte != -1 ) );
  512. // Return the success of the comparison. "file1byte" is
  513. // equal to "file2byte" at this point only if the files are
  514. // the same.
  515. streamOne.Position = 0;
  516. streamTwo.Position = 0;
  517. return ( ( file1byte - file2byte ) == 0 );
  518. }
  519. /// <summary>
  520. /// Add the default numbering.xml if it is missing from this document
  521. /// </summary>
  522. internal static XDocument AddDefaultNumberingXml(Package package)
  523. {
  524. XDocument numberingDoc;
  525. var numberingPart = package.CreatePart(new Uri("/word/numbering.xml", UriKind.Relative), "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", CompressionOption.Maximum);
  526. numberingDoc = DecompressXMLResource("Xceed.Words.NET.Resources.numbering.xml.gz");
  527. using( TextWriter tw = new StreamWriter( new PackagePartStream( numberingPart.GetStream( FileMode.Create, FileAccess.Write ) ) ) )
  528. {
  529. numberingDoc.Save( tw, SaveOptions.None );
  530. }
  531. var mainDocPart = GetMainDocumentPart( package );
  532. mainDocPart.CreateRelationship(numberingPart.Uri, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering");
  533. return numberingDoc;
  534. }
  535. internal static List CreateItemInList(List list, string listText, int level = 0, ListItemType listType = ListItemType.Numbered, int? startNumber = null, bool trackChanges = false, bool continueNumbering = false)
  536. {
  537. if (list.NumId == 0)
  538. {
  539. list.CreateNewNumberingNumId(level, listType, startNumber, continueNumbering);
  540. }
  541. if (listText != null)
  542. {
  543. var newSection = new XElement
  544. (
  545. XName.Get("p", DocX.w.NamespaceName),
  546. new XElement(XName.Get("pPr", DocX.w.NamespaceName),
  547. new XElement(XName.Get("numPr", DocX.w.NamespaceName),
  548. new XElement(XName.Get("ilvl", DocX.w.NamespaceName), new XAttribute(DocX.w + "val", level)),
  549. new XElement(XName.Get("numId", DocX.w.NamespaceName), new XAttribute(DocX.w + "val", list.NumId)))),
  550. new XElement(XName.Get("r", DocX.w.NamespaceName), new XElement(XName.Get("t", DocX.w.NamespaceName), listText))
  551. );
  552. if (trackChanges)
  553. newSection = CreateEdit(EditType.ins, DateTime.Now, newSection);
  554. if (startNumber == null)
  555. list.AddItem(new Paragraph(list.Document, newSection, 0, ContainerType.Paragraph));
  556. else
  557. list.AddItemWithStartValue(new Paragraph(list.Document, newSection, 0, ContainerType.Paragraph), (int)startNumber);
  558. }
  559. return list;
  560. }
  561. internal static UnderlineStyle GetUnderlineStyle(string styleName)
  562. {
  563. switch (styleName)
  564. {
  565. case "single":
  566. return UnderlineStyle.singleLine;
  567. case "double":
  568. return UnderlineStyle.doubleLine;
  569. case "thick":
  570. return UnderlineStyle.thick;
  571. case "dotted":
  572. return UnderlineStyle.dotted;
  573. case "dottedHeavy":
  574. return UnderlineStyle.dottedHeavy;
  575. case "dash":
  576. return UnderlineStyle.dash;
  577. case "dashedHeavy":
  578. return UnderlineStyle.dashedHeavy;
  579. case "dashLong":
  580. return UnderlineStyle.dashLong;
  581. case "dashLongHeavy":
  582. return UnderlineStyle.dashLongHeavy;
  583. case "dotDash":
  584. return UnderlineStyle.dotDash;
  585. case "dashDotHeavy":
  586. return UnderlineStyle.dashDotHeavy;
  587. case "dotDotDash":
  588. return UnderlineStyle.dotDotDash;
  589. case "dashDotDotHeavy":
  590. return UnderlineStyle.dashDotDotHeavy;
  591. case "wave":
  592. return UnderlineStyle.wave;
  593. case "wavyHeavy":
  594. return UnderlineStyle.wavyHeavy;
  595. case "wavyDouble":
  596. return UnderlineStyle.wavyDouble;
  597. case "words":
  598. return UnderlineStyle.words;
  599. default:
  600. return UnderlineStyle.none;
  601. }
  602. }
  603. internal static bool ContainsEveryChildOf(XElement elementWanted, XElement elementToValidate, MatchFormattingOptions formattingOptions)
  604. {
  605. foreach (XElement subElement in elementWanted.Elements())
  606. {
  607. if (!elementToValidate.Elements(subElement.Name).Where(bElement => bElement.GetAttribute(XName.Get("val", DocX.w.NamespaceName)) == subElement.GetAttribute(XName.Get("val", DocX.w.NamespaceName))).Any())
  608. return false;
  609. }
  610. if (formattingOptions == MatchFormattingOptions.ExactMatch)
  611. return elementWanted.Elements().Count() == elementToValidate.Elements().Count();
  612. return true;
  613. }
  614. internal static string GetListItemType( Paragraph p, DocX document )
  615. {
  616. var paragraphNumberPropertiesDescendants = p.ParagraphNumberProperties.Descendants();
  617. var ilvlNode = paragraphNumberPropertiesDescendants.FirstOrDefault( el => el.Name.LocalName == "ilvl" );
  618. var ilvlValue = ( ilvlNode != null) ? ilvlNode.Attribute( DocX.w + "val" ).Value : null;
  619. var numIdNode = paragraphNumberPropertiesDescendants.FirstOrDefault( el => el.Name.LocalName == "numId" );
  620. var numIdValue = ( numIdNode != null ) ? numIdNode.Attribute( DocX.w + "val" ).Value : null;
  621. //find num node in numbering
  622. var documentNumberingDescendants = document._numbering.Descendants();
  623. var numNodes = documentNumberingDescendants.Where( n => n.Name.LocalName == "num" );
  624. XElement numNode = numNodes.FirstOrDefault( node => node.Attribute( DocX.w + "numId" ).Value.Equals( numIdValue ) );
  625. if( numNode != null )
  626. {
  627. //Get abstractNumId node and its value from numNode
  628. var abstractNumIdNode = numNode.Descendants().First( n => n.Name.LocalName == "abstractNumId" );
  629. var abstractNumNodeValue = abstractNumIdNode.Attribute( DocX.w + "val" ).Value;
  630. var abstractNumNodes = documentNumberingDescendants.Where( n => n.Name.LocalName == "abstractNum" );
  631. XElement abstractNumNode = abstractNumNodes.FirstOrDefault( node => node.Attribute( DocX.w + "abstractNumId" ).Value.Equals( abstractNumNodeValue ) );
  632. //Find lvl node
  633. var lvlNodes = abstractNumNode.Descendants().Where( n => n.Name.LocalName == "lvl" );
  634. XElement lvlNode = null;
  635. foreach( XElement node in lvlNodes )
  636. {
  637. if( node.Attribute( DocX.w + "ilvl" ).Value.Equals( ilvlValue ) )
  638. {
  639. lvlNode = node;
  640. break;
  641. }
  642. else if( ilvlValue == null )
  643. {
  644. var numStyleNode = node.Descendants().FirstOrDefault( n => n.Name.LocalName == "pStyle" );
  645. if( ( numStyleNode != null) && numStyleNode.GetAttribute( DocX.w + "val" ).Equals( p.StyleName ) )
  646. {
  647. lvlNode = node;
  648. break;
  649. }
  650. }
  651. }
  652. if( lvlNode != null )
  653. {
  654. var numFmtNode = lvlNode.Descendants().First( n => n.Name.LocalName == "numFmt" );
  655. return numFmtNode.Attribute( DocX.w + "val" ).Value;
  656. }
  657. }
  658. return null;
  659. }
  660. internal static string GetListItemStartValue( List list, int level )
  661. {
  662. var abstractNumElement = list.GetAbstractNum( list.NumId );
  663. //Find lvl node
  664. var lvlNodes = abstractNumElement.Descendants().Where( n => n.Name.LocalName == "lvl" );
  665. var lvlNode = lvlNodes.FirstOrDefault( n => n.GetAttribute( DocX.w + "ilvl" ).Equals( level.ToString() ) );
  666. var startNode = lvlNode.Descendants().First( n => n.Name.LocalName == "start" );
  667. return startNode.GetAttribute( DocX.w + "val" );
  668. }
  669. internal static string GetListItemTextFormat( List list, int level )
  670. {
  671. var abstractNumElement = list.GetAbstractNum( list.NumId );
  672. //Find lvl node
  673. var lvlNodes = abstractNumElement.Descendants().Where( n => n.Name.LocalName == "lvl" );
  674. var lvlNode = lvlNodes.FirstOrDefault( n => n.GetAttribute( DocX.w + "ilvl" ).Equals( level.ToString() ) );
  675. var textFormatNode = lvlNode.Descendants().First( n => n.Name.LocalName == "lvlText" );
  676. return textFormatNode.GetAttribute( DocX.w + "val" );
  677. }
  678. internal static XElement GetListItemAlignment( List list, int level )
  679. {
  680. var abstractNumElement = list.GetAbstractNum( list.NumId );
  681. //Find lvl node
  682. var lvlNodes = abstractNumElement.Descendants().Where( n => n.Name.LocalName == "lvl" );
  683. var lvlNode = lvlNodes.FirstOrDefault( n => n.GetAttribute( DocX.w + "ilvl" ).Equals( level.ToString() ) );
  684. var pPr = lvlNode.Descendants().FirstOrDefault( n => n.Name.LocalName == "pPr" );
  685. if( pPr != null )
  686. {
  687. var ind = pPr.Descendants().FirstOrDefault( n => n.Name.LocalName == "ind" );
  688. if( ind != null )
  689. {
  690. return ind;
  691. }
  692. }
  693. return null;
  694. }
  695. }
  696. }