您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

HelperFunctions.cs 37KB

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