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.

HelperFunctions.cs 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.IO.Packaging;
  6. using System.Xml.Linq;
  7. using System.IO;
  8. using System.Reflection;
  9. using System.IO.Compression;
  10. using System.Security.Principal;
  11. using System.Globalization;
  12. namespace Novacode
  13. {
  14. internal static class HelperFunctions
  15. {
  16. internal static void CreateRelsPackagePart(DocX Document, Uri uri)
  17. {
  18. PackagePart pp = Document.package.CreatePart(uri, "application/vnd.openxmlformats-package.relationships+xml", CompressionOption.Maximum);
  19. using (TextWriter tw = new StreamWriter(pp.GetStream()))
  20. {
  21. XDocument d = new XDocument
  22. (
  23. new XDeclaration("1.0", "UTF-8", "yes"),
  24. new XElement(XName.Get("Relationships", DocX.rel.NamespaceName))
  25. );
  26. var root = d.Root;
  27. d.Save(tw);
  28. }
  29. }
  30. internal static int GetSize(XElement Xml)
  31. {
  32. switch (Xml.Name.LocalName)
  33. {
  34. case "tab":
  35. return 1;
  36. case "br":
  37. return 1;
  38. case "t":
  39. goto case "delText";
  40. case "delText":
  41. return Xml.Value.Length;
  42. case "tr":
  43. goto case "br";
  44. case "tc":
  45. goto case "br";
  46. default:
  47. return 0;
  48. }
  49. }
  50. internal static string GetText(XElement e)
  51. {
  52. StringBuilder sb = new StringBuilder();
  53. GetTextRecursive(e, ref sb);
  54. return sb.ToString();
  55. }
  56. internal static void GetTextRecursive(XElement Xml, ref StringBuilder sb)
  57. {
  58. sb.Append(ToText(Xml));
  59. if (Xml.HasElements)
  60. foreach (XElement e in Xml.Elements())
  61. GetTextRecursive(e, ref sb);
  62. }
  63. internal static List<FormattedText> GetFormattedText(XElement e)
  64. {
  65. List<FormattedText> alist = new List<FormattedText>();
  66. GetFormattedTextRecursive(e, ref alist);
  67. return alist;
  68. }
  69. internal static void GetFormattedTextRecursive(XElement Xml, ref List<FormattedText> alist)
  70. {
  71. FormattedText ft = ToFormattedText(Xml);
  72. FormattedText last = null;
  73. if (ft != null)
  74. {
  75. if (alist.Count() > 0)
  76. last = alist.Last();
  77. if (last != null && last.CompareTo(ft) == 0)
  78. {
  79. // Update text of last entry.
  80. last.text += ft.text;
  81. }
  82. else
  83. {
  84. if (last != null)
  85. ft.index = last.index + last.text.Length;
  86. alist.Add(ft);
  87. }
  88. }
  89. if (Xml.HasElements)
  90. foreach (XElement e in Xml.Elements())
  91. GetFormattedTextRecursive(e, ref alist);
  92. }
  93. internal static FormattedText ToFormattedText(XElement e)
  94. {
  95. // The text representation of e.
  96. String text = ToText(e);
  97. if (text == String.Empty)
  98. return null;
  99. // e is a w:t element, it must exist inside a w:r element, lets climb until we find it.
  100. while (!e.Name.Equals(XName.Get("r", DocX.w.NamespaceName)))
  101. e = e.Parent;
  102. // e is a w:r element, lets find the rPr element.
  103. XElement rPr = e.Element(XName.Get("rPr", DocX.w.NamespaceName));
  104. FormattedText ft = new FormattedText();
  105. ft.text = text;
  106. ft.index = 0;
  107. ft.formatting = null;
  108. // Return text with formatting.
  109. if (rPr != null)
  110. ft.formatting = Formatting.Parse(rPr);
  111. return ft;
  112. }
  113. internal static string ToText(XElement e)
  114. {
  115. switch (e.Name.LocalName)
  116. {
  117. case "tab":
  118. return "\t";
  119. case "br":
  120. return "\n";
  121. case "t":
  122. goto case "delText";
  123. case "delText":
  124. {
  125. if (e.Parent != null && e.Parent.Name.LocalName == "r")
  126. {
  127. XElement run = e.Parent;
  128. var rPr = run.Elements().FirstOrDefault(a => a.Name.LocalName == "rPr");
  129. if (rPr != null)
  130. {
  131. var caps = rPr.Elements().FirstOrDefault(a => a.Name.LocalName == "caps");
  132. if (caps != null)
  133. return e.Value.ToUpper();
  134. }
  135. }
  136. return e.Value;
  137. }
  138. case "tr":
  139. goto case "br";
  140. case "tc":
  141. goto case "tab";
  142. default: return "";
  143. }
  144. }
  145. internal static XElement CloneElement(XElement element)
  146. {
  147. return new XElement
  148. (
  149. element.Name,
  150. element.Attributes(),
  151. element.Nodes().Select
  152. (
  153. n =>
  154. {
  155. XElement e = n as XElement;
  156. if (e != null)
  157. return CloneElement(e);
  158. return n;
  159. }
  160. )
  161. );
  162. }
  163. internal static PackagePart CreateOrGetSettingsPart(Package package)
  164. {
  165. PackagePart settingsPart;
  166. Uri settingsUri = new Uri("/word/settings.xml", UriKind.Relative);
  167. if (!package.PartExists(settingsUri))
  168. {
  169. settingsPart = package.CreatePart(settingsUri, "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml", CompressionOption.Maximum);
  170. PackagePart mainDocumentPart = package.GetParts().Where
  171. (
  172. p => p.ContentType.Equals
  173. (
  174. "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
  175. StringComparison.CurrentCultureIgnoreCase
  176. )
  177. ).Single();
  178. mainDocumentPart.CreateRelationship(settingsUri, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings");
  179. XDocument settings = XDocument.Parse
  180. (@"<?xml version='1.0' encoding='utf-8' standalone='yes'?>
  181. <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'>
  182. <w:zoom w:percent='100' />
  183. <w:defaultTabStop w:val='720' />
  184. <w:characterSpacingControl w:val='doNotCompress' />
  185. <w:compat />
  186. <w:rsids>
  187. <w:rsidRoot w:val='00217F62' />
  188. <w:rsid w:val='001915A3' />
  189. <w:rsid w:val='00217F62' />
  190. <w:rsid w:val='00A906D8' />
  191. <w:rsid w:val='00AB5A74' />
  192. <w:rsid w:val='00F071AE' />
  193. </w:rsids>
  194. <m:mathPr>
  195. <m:mathFont m:val='Cambria Math' />
  196. <m:brkBin m:val='before' />
  197. <m:brkBinSub m:val='--' />
  198. <m:smallFrac m:val='off' />
  199. <m:dispDef />
  200. <m:lMargin m:val='0' />
  201. <m:rMargin m:val='0' />
  202. <m:defJc m:val='centerGroup' />
  203. <m:wrapIndent m:val='1440' />
  204. <m:intLim m:val='subSup' />
  205. <m:naryLim m:val='undOvr' />
  206. </m:mathPr>
  207. <w:themeFontLang w:val='en-IE' w:bidi='ar-SA' />
  208. <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' />
  209. <w:shapeDefaults>
  210. <o:shapedefaults v:ext='edit' spidmax='2050' />
  211. <o:shapelayout v:ext='edit'>
  212. <o:idmap v:ext='edit' data='1' />
  213. </o:shapelayout>
  214. </w:shapeDefaults>
  215. <w:decimalSymbol w:val='.' />
  216. <w:listSeparator w:val=',' />
  217. </w:settings>"
  218. );
  219. XElement themeFontLang = settings.Root.Element(XName.Get("themeFontLang", DocX.w.NamespaceName));
  220. themeFontLang.SetAttributeValue(XName.Get("val", DocX.w.NamespaceName), CultureInfo.CurrentCulture);
  221. // Save the settings document.
  222. using (TextWriter tw = new StreamWriter(settingsPart.GetStream()))
  223. settings.Save(tw);
  224. }
  225. else
  226. settingsPart = package.GetPart(settingsUri);
  227. return settingsPart;
  228. }
  229. internal static void CreateCustomPropertiesPart(DocX document)
  230. {
  231. PackagePart customPropertiesPart = document.package.CreatePart(new Uri("/docProps/custom.xml", UriKind.Relative), "application/vnd.openxmlformats-officedocument.custom-properties+xml", CompressionOption.Maximum);
  232. XDocument customPropDoc = new XDocument
  233. (
  234. new XDeclaration("1.0", "UTF-8", "yes"),
  235. new XElement
  236. (
  237. XName.Get("Properties", DocX.customPropertiesSchema.NamespaceName),
  238. new XAttribute(XNamespace.Xmlns + "vt", DocX.customVTypesSchema)
  239. )
  240. );
  241. using (TextWriter tw = new StreamWriter(customPropertiesPart.GetStream(FileMode.Create, FileAccess.Write)))
  242. customPropDoc.Save(tw, SaveOptions.None);
  243. document.package.CreateRelationship(customPropertiesPart.Uri, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties");
  244. }
  245. internal static XDocument DecompressXMLResource(string manifest_resource_name)
  246. {
  247. // XDocument to load the compressed Xml resource into.
  248. XDocument document;
  249. // Get a reference to the executing assembly.
  250. Assembly assembly = Assembly.GetExecutingAssembly();
  251. // Open a Stream to the embedded resource.
  252. Stream stream = assembly.GetManifestResourceStream(manifest_resource_name);
  253. // Decompress the embedded resource.
  254. using (GZipStream zip = new GZipStream(stream, CompressionMode.Decompress))
  255. {
  256. // Load this decompressed embedded resource into an XDocument using a TextReader.
  257. using (TextReader sr = new StreamReader(zip))
  258. {
  259. document = XDocument.Load(sr);
  260. }
  261. }
  262. // Return the decompressed Xml as an XDocument.
  263. return document;
  264. }
  265. /// <summary>
  266. /// If this document does not contain a /word/styles.xml add the default one generated by Microsoft Word.
  267. /// </summary>
  268. /// <param name="package"></param>
  269. /// <param name="mainDocumentPart"></param>
  270. /// <returns></returns>
  271. internal static XDocument AddDefaultStylesXml(Package package)
  272. {
  273. XDocument stylesDoc;
  274. // Create the main document part for this package
  275. PackagePart word_styles = package.CreatePart(new Uri("/word/styles.xml", UriKind.Relative), "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", CompressionOption.Maximum);
  276. stylesDoc = HelperFunctions.DecompressXMLResource("Novacode.Resources.default_styles.xml.gz");
  277. XElement 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));
  278. lang.SetAttributeValue(XName.Get("val", DocX.w.NamespaceName), CultureInfo.CurrentCulture);
  279. // Save /word/styles.xml
  280. using (TextWriter tw = new StreamWriter(word_styles.GetStream(FileMode.Create, FileAccess.Write)))
  281. stylesDoc.Save(tw, SaveOptions.None);
  282. PackagePart mainDocumentPart = package.GetParts().Where
  283. (
  284. p => p.ContentType.Equals
  285. (
  286. "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
  287. StringComparison.CurrentCultureIgnoreCase
  288. )
  289. ).Single();
  290. mainDocumentPart.CreateRelationship(word_styles.Uri, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles");
  291. return stylesDoc;
  292. }
  293. static internal XElement CreateEdit(EditType t, DateTime edit_time, object content)
  294. {
  295. if (t == EditType.del)
  296. {
  297. foreach (object o in (IEnumerable<XElement>)content)
  298. {
  299. if (o is XElement)
  300. {
  301. XElement e = (o as XElement);
  302. IEnumerable<XElement> ts = e.DescendantsAndSelf(XName.Get("t", DocX.w.NamespaceName));
  303. for (int i = 0; i < ts.Count(); i++)
  304. {
  305. XElement text = ts.ElementAt(i);
  306. text.ReplaceWith(new XElement(DocX.w + "delText", text.Attributes(), text.Value));
  307. }
  308. }
  309. }
  310. }
  311. return
  312. (
  313. new XElement(DocX.w + t.ToString(),
  314. new XAttribute(DocX.w + "id", 0),
  315. new XAttribute(DocX.w + "author", WindowsIdentity.GetCurrent().Name),
  316. new XAttribute(DocX.w + "date", edit_time),
  317. content)
  318. );
  319. }
  320. internal static XElement CreateTable(int rowCount, int columnCount)
  321. {
  322. XElement newTable =
  323. new XElement
  324. (
  325. XName.Get("tbl", DocX.w.NamespaceName),
  326. new XElement
  327. (
  328. XName.Get("tblPr", DocX.w.NamespaceName),
  329. new XElement(XName.Get("tblStyle", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), "TableGrid")),
  330. 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")),
  331. new XElement(XName.Get("tblLook", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), "04A0"))
  332. )
  333. );
  334. XElement tableGrid = new XElement(XName.Get("tblGrid", DocX.w.NamespaceName));
  335. for (int i = 0; i < columnCount; i++)
  336. tableGrid.Add(new XElement(XName.Get("gridCol", DocX.w.NamespaceName), new XAttribute(XName.Get("w", DocX.w.NamespaceName), "2310")));
  337. newTable.Add(tableGrid);
  338. for (int i = 0; i < rowCount; i++)
  339. {
  340. XElement row = new XElement(XName.Get("tr", DocX.w.NamespaceName));
  341. for (int j = 0; j < columnCount; j++)
  342. {
  343. XElement cell = CreateTableCell();
  344. row.Add(cell);
  345. }
  346. newTable.Add(row);
  347. }
  348. return newTable;
  349. }
  350. /// <summary>
  351. /// Create and return a cell of a table
  352. /// </summary>
  353. internal static XElement CreateTableCell()
  354. {
  355. return new XElement
  356. (
  357. XName.Get("tc", DocX.w.NamespaceName),
  358. new XElement(XName.Get("tcPr", DocX.w.NamespaceName),
  359. new XElement(XName.Get("tcW", DocX.w.NamespaceName),
  360. new XAttribute(XName.Get("w", DocX.w.NamespaceName), "2310"),
  361. new XAttribute(XName.Get("type", DocX.w.NamespaceName), "dxa"))),
  362. new XElement(XName.Get("p", DocX.w.NamespaceName),
  363. new XElement(XName.Get("pPr", DocX.w.NamespaceName)))
  364. );
  365. }
  366. internal static void RenumberIDs(DocX document)
  367. {
  368. IEnumerable<XAttribute> trackerIDs =
  369. (from d in document.mainDoc.Descendants()
  370. where d.Name.LocalName == "ins" || d.Name.LocalName == "del"
  371. select d.Attribute(XName.Get("id", "http://schemas.openxmlformats.org/wordprocessingml/2006/main")));
  372. for (int i = 0; i < trackerIDs.Count(); i++)
  373. trackerIDs.ElementAt(i).Value = i.ToString();
  374. }
  375. static internal Paragraph GetFirstParagraphEffectedByInsert(DocX document, int index)
  376. {
  377. // This document contains no Paragraphs and insertion is at index 0
  378. if (document.paragraphLookup.Keys.Count() == 0 && index == 0)
  379. return null;
  380. foreach (int paragraphEndIndex in document.paragraphLookup.Keys)
  381. {
  382. if (paragraphEndIndex >= index)
  383. return document.paragraphLookup[paragraphEndIndex];
  384. }
  385. throw new ArgumentOutOfRangeException();
  386. }
  387. internal static List<XElement> FormatInput(string text, XElement rPr)
  388. {
  389. List<XElement> newRuns = new List<XElement>();
  390. XElement tabRun = new XElement(DocX.w + "tab");
  391. XElement breakRun = new XElement(DocX.w + "br");
  392. StringBuilder sb = new StringBuilder();
  393. if (string.IsNullOrEmpty(text))
  394. {
  395. return newRuns; //I dont wanna get an exception if text == null, so just return empy list
  396. }
  397. foreach (char c in text)
  398. {
  399. switch (c)
  400. {
  401. case '\t':
  402. if (sb.Length > 0)
  403. {
  404. XElement t = new XElement(DocX.w + "t", sb.ToString());
  405. Novacode.Text.PreserveSpace(t);
  406. newRuns.Add(new XElement(DocX.w + "r", rPr, t));
  407. sb = new StringBuilder();
  408. }
  409. newRuns.Add(new XElement(DocX.w + "r", rPr, tabRun));
  410. break;
  411. case '\n':
  412. if (sb.Length > 0)
  413. {
  414. XElement t = new XElement(DocX.w + "t", sb.ToString());
  415. Novacode.Text.PreserveSpace(t);
  416. newRuns.Add(new XElement(DocX.w + "r", rPr, t));
  417. sb = new StringBuilder();
  418. }
  419. newRuns.Add(new XElement(DocX.w + "r", rPr, breakRun));
  420. break;
  421. default:
  422. sb.Append(c);
  423. break;
  424. }
  425. }
  426. if (sb.Length > 0)
  427. {
  428. XElement t = new XElement(DocX.w + "t", sb.ToString());
  429. Novacode.Text.PreserveSpace(t);
  430. newRuns.Add(new XElement(DocX.w + "r", rPr, t));
  431. }
  432. return newRuns;
  433. }
  434. static internal XElement[] SplitParagraph(Paragraph p, int index)
  435. {
  436. // In this case edit dosent really matter, you have a choice.
  437. Run r = p.GetFirstRunEffectedByEdit(index, EditType.ins);
  438. XElement[] split;
  439. XElement before, after;
  440. if (r.Xml.Parent.Name.LocalName == "ins")
  441. {
  442. split = p.SplitEdit(r.Xml.Parent, index, EditType.ins);
  443. before = new XElement(p.Xml.Name, p.Xml.Attributes(), r.Xml.Parent.ElementsBeforeSelf(), split[0]);
  444. after = new XElement(p.Xml.Name, p.Xml.Attributes(), r.Xml.Parent.ElementsAfterSelf(), split[1]);
  445. }
  446. else if (r.Xml.Parent.Name.LocalName == "del")
  447. {
  448. split = p.SplitEdit(r.Xml.Parent, index, EditType.del);
  449. before = new XElement(p.Xml.Name, p.Xml.Attributes(), r.Xml.Parent.ElementsBeforeSelf(), split[0]);
  450. after = new XElement(p.Xml.Name, p.Xml.Attributes(), r.Xml.Parent.ElementsAfterSelf(), split[1]);
  451. }
  452. else
  453. {
  454. split = Run.SplitRun(r, index);
  455. before = new XElement(p.Xml.Name, p.Xml.Attributes(), r.Xml.ElementsBeforeSelf(), split[0]);
  456. after = new XElement(p.Xml.Name, p.Xml.Attributes(), split[1], r.Xml.ElementsAfterSelf());
  457. }
  458. if (before.Elements().Count() == 0)
  459. before = null;
  460. if (after.Elements().Count() == 0)
  461. after = null;
  462. return new XElement[] { before, after };
  463. }
  464. /// <!--
  465. /// Bug found and fixed by trnilse. To see the change,
  466. /// please compare this release to the previous release using TFS compare.
  467. /// -->
  468. static internal bool IsSameFile(Stream streamOne, Stream streamTwo)
  469. {
  470. int file1byte, file2byte;
  471. if (streamOne.Length != streamOne.Length)
  472. {
  473. // Return false to indicate files are different
  474. return false;
  475. }
  476. // Read and compare a byte from each file until either a
  477. // non-matching set of bytes is found or until the end of
  478. // file1 is reached.
  479. do
  480. {
  481. // Read one byte from each file.
  482. file1byte = streamOne.ReadByte();
  483. file2byte = streamTwo.ReadByte();
  484. }
  485. while ((file1byte == file2byte) && (file1byte != -1));
  486. // Return the success of the comparison. "file1byte" is
  487. // equal to "file2byte" at this point only if the files are
  488. // the same.
  489. streamOne.Position = 0;
  490. streamTwo.Position = 0;
  491. return ((file1byte - file2byte) == 0);
  492. }
  493. }
  494. }