{"id":2792,"date":"2016-03-18T13:50:25","date_gmt":"2016-03-18T13:50:25","guid":{"rendered":"http:\/\/www.ericwhite.com\/home2\/bm8qcmjy\/public_html\/blog\/?page_id=2792"},"modified":"2016-03-18T13:50:25","modified_gmt":"2016-03-18T13:50:25","slug":"handling-invalid-hyperlinks-openxmlpackageexception-in-the-open-xml-sdk","status":"publish","type":"page","link":"https:\/\/www.ericwhite.com\/blog\/handling-invalid-hyperlinks-openxmlpackageexception-in-the-open-xml-sdk\/","title":{"rendered":"Handling Invalid Hyperlinks (OpenXmlPackageException) in the Open XML SDK"},"content":{"rendered":"<p>One of the pernicious problems around the Open XML SDK is that if a document contains a hyperlink that has an invalid <b>Uri<\/b>, then Word, Excel, or PowerPoint will happily open the document, whereas <b>System.IO.Packaging<\/b> (and therefore the Open XML SDK) will throw an exception.<\/p>\n<p>The problem that we face with this is that we can\u2019t fix this in the Open XML SDK; it is thrown from deep inside the classes in <b>System.IO.Packaging<\/b>. &nbsp;This problem occurs because <b>System.IO.Packaging<\/b> creates a <b>Uri <\/b>object for every external relationship, and if the target of that relationship is not a valid <b>Uri<\/b>, then we see the thrown exception. &nbsp;<b>System.IO.Packaging<\/b>&nbsp;has been frozen for quite some time, and it currently isn\u2019t possible to submit this as a bug and expect it to be fixed. &nbsp;This is understandable in that there are hundreds of thousands of programs that rely on these classes, and changing semantics in the smallest way very well might cause compatibility problems.<\/p>\n<p>However, there is a way to work around this in a fairly clean way. &nbsp;I have written a small method that uses the classes in <b>System.IO.Compression <\/b>to open an Open XML document (of any type \u2013 DOCX, XLSX, or PPTX), examine all external relationships and if the relationship does not contain a valid <b>Uri<\/b>, the method calls a callback with the invalid target. &nbsp;You can write this callback to return any valid <b>Uri<\/b> that you want, so long as it is a valid <b>Uri<\/b>. &nbsp;A possible candidate would be the target of <b>http:\/\/broken-link\/<\/b>. &nbsp;The <b>FixInvalidUri <\/b>method then updates the target of the external relationship with the valid <b>Uri<\/b>. &nbsp;You can then open and process the document as usual.<\/p>\n<p>Following is the complete listing of the class <b>UriFixer<\/b>, as well as the code to use it. &nbsp;The approach that you take when using this class is to first attempt to open the document as usual, catching <b>OpenXmlPackageException<\/b>. &nbsp;If that exception is thrown, and if the text of that exception contains \u201cInvalid Hyperlink\u201d, then the code calls <b>UriFixer.FixInvalidUri<\/b>. &nbsp;After calling <b>FixInvalidUri<\/b>, the code then opens the fixed document (or spreadsheet \/ presentation) as usual.<\/p>\n<pre class=\"prettyprint\"><span class=\"kwd\">using<\/span><span class=\"pln\"> <\/span><span class=\"typ\">System<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br><\/span><span class=\"kwd\">using<\/span><span class=\"pln\"> <\/span><span class=\"typ\">System<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Collections<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Generic<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br><\/span><span class=\"kwd\">using<\/span><span class=\"pln\"> <\/span><span class=\"typ\">System<\/span><span class=\"pun\">.<\/span><span class=\"pln\">IO<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br><\/span><span class=\"kwd\">using<\/span><span class=\"pln\"> <\/span><span class=\"typ\">System<\/span><span class=\"pun\">.<\/span><span class=\"pln\">IO<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Compression<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br><\/span><span class=\"kwd\">using<\/span><span class=\"pln\"> <\/span><span class=\"typ\">System<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Linq<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br><\/span><span class=\"kwd\">using<\/span><span class=\"pln\"> <\/span><span class=\"typ\">System<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Text<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br><\/span><span class=\"kwd\">using<\/span><span class=\"pln\"> <\/span><span class=\"typ\">System<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Threading<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Tasks<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br><\/span><span class=\"kwd\">using<\/span><span class=\"pln\"> <\/span><span class=\"typ\">System<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Xml<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br><\/span><span class=\"kwd\">using<\/span><span class=\"pln\"> <\/span><span class=\"typ\">System<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Xml<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Linq<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br><\/span><span class=\"kwd\">using<\/span><span class=\"pln\"> <\/span><span class=\"typ\">DocumentFormat<\/span><span class=\"pun\">.<\/span><span class=\"typ\">OpenXml<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Packaging<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br><br><\/span><span class=\"kwd\">class<\/span><span class=\"pln\"> <\/span><span class=\"typ\">Program<\/span><span class=\"pln\"><br><\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; <\/span><span class=\"kwd\">static<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">void<\/span><span class=\"pln\"> <\/span><span class=\"typ\">Main<\/span><span class=\"pun\">(<\/span><span class=\"kwd\">string<\/span><span class=\"pun\">[]<\/span><span class=\"pln\"> args<\/span><span class=\"pun\">)<\/span><span class=\"pln\"><br>&nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">var<\/span><span class=\"pln\"> fileName <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> <\/span><span class=\"pun\">@<\/span><span class=\"str\">\"..\\..\\..\\Test.docx\"<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">var<\/span><span class=\"pln\"> newFileName <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> <\/span><span class=\"pun\">@<\/span><span class=\"str\">\"..\\..\\..\\Fixed.docx\"<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">var<\/span><span class=\"pln\"> newFileInfo <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">new<\/span><span class=\"pln\"> <\/span><span class=\"typ\">FileInfo<\/span><span class=\"pun\">(<\/span><span class=\"pln\">newFileName<\/span><span class=\"pun\">);<\/span><span class=\"pln\"><br><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">if<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"pln\">newFileInfo<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Exists<\/span><span class=\"pun\">)<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; newFileInfo<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Delete<\/span><span class=\"pun\">();<\/span><span class=\"pln\"><br><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"typ\">File<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Copy<\/span><span class=\"pun\">(<\/span><span class=\"pln\">fileName<\/span><span class=\"pun\">,<\/span><span class=\"pln\"> newFileName<\/span><span class=\"pun\">);<\/span><span class=\"pln\"><br><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"typ\">WordprocessingDocument<\/span><span class=\"pln\"> wDoc<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">try<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">using<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"pln\">wDoc <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> <\/span><span class=\"typ\">WordprocessingDocument<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Open<\/span><span class=\"pun\">(<\/span><span class=\"pln\">newFileName<\/span><span class=\"pun\">,<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">true<\/span><span class=\"pun\">))<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"typ\">ProcessDocument<\/span><span class=\"pun\">(<\/span><span class=\"pln\">wDoc<\/span><span class=\"pun\">);<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">catch<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"typ\">OpenXmlPackageException<\/span><span class=\"pln\"> e<\/span><span class=\"pun\">)<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">if<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"pln\">e<\/span><span class=\"pun\">.<\/span><span class=\"typ\">ToString<\/span><span class=\"pun\">().<\/span><span class=\"typ\">Contains<\/span><span class=\"pun\">(<\/span><span class=\"str\">\"Invalid Hyperlink\"<\/span><span class=\"pun\">))<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">using<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"typ\">FileStream<\/span><span class=\"pln\"> fs <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">new<\/span><span class=\"pln\"> <\/span><span class=\"typ\">FileStream<\/span><span class=\"pun\">(<\/span><span class=\"pln\">newFileName<\/span><span class=\"pun\">,<\/span><span class=\"pln\"> <\/span><span class=\"typ\">FileMode<\/span><span class=\"pun\">.<\/span><span class=\"typ\">OpenOrCreate<\/span><span class=\"pun\">,<\/span><span class=\"pln\"> <\/span><span class=\"typ\">FileAccess<\/span><span class=\"pun\">.<\/span><span class=\"typ\">ReadWrite<\/span><span class=\"pun\">))<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"typ\">UriFixer<\/span><span class=\"pun\">.<\/span><span class=\"typ\">FixInvalidUri<\/span><span class=\"pun\">(<\/span><span class=\"pln\">fs<\/span><span class=\"pun\">,<\/span><span class=\"pln\"> brokenUri <\/span><span class=\"pun\">=&gt;<\/span><span class=\"pln\"> <\/span><span class=\"typ\">FixUri<\/span><span class=\"pun\">(<\/span><span class=\"pln\">brokenUri<\/span><span class=\"pun\">));<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">using<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"pln\">wDoc <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> <\/span><span class=\"typ\">WordprocessingDocument<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Open<\/span><span class=\"pun\">(<\/span><span class=\"pln\">newFileName<\/span><span class=\"pun\">,<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">true<\/span><span class=\"pun\">))<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"typ\">ProcessDocument<\/span><span class=\"pun\">(<\/span><span class=\"pln\">wDoc<\/span><span class=\"pun\">);<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br><br>&nbsp; &nbsp; <\/span><span class=\"kwd\">private<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">static<\/span><span class=\"pln\"> <\/span><span class=\"typ\">Uri<\/span><span class=\"pln\"> <\/span><span class=\"typ\">FixUri<\/span><span class=\"pun\">(<\/span><span class=\"kwd\">string<\/span><span class=\"pln\"> brokenUri<\/span><span class=\"pun\">)<\/span><span class=\"pln\"><br>&nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">return<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">new<\/span><span class=\"pln\"> <\/span><span class=\"typ\">Uri<\/span><span class=\"pun\">(<\/span><span class=\"str\">\"http:\/\/broken-link\/\"<\/span><span class=\"pun\">);<\/span><span class=\"pln\"><br>&nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br><br>&nbsp; &nbsp; <\/span><span class=\"kwd\">private<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">static<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">void<\/span><span class=\"pln\"> <\/span><span class=\"typ\">ProcessDocument<\/span><span class=\"pun\">(<\/span><span class=\"typ\">WordprocessingDocument<\/span><span class=\"pln\"> wDoc<\/span><span class=\"pun\">)<\/span><span class=\"pln\"><br>&nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">var<\/span><span class=\"pln\"> elementCount <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> wDoc<\/span><span class=\"pun\">.<\/span><span class=\"typ\">MainDocumentPart<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Document<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Descendants<\/span><span class=\"pun\">().<\/span><span class=\"typ\">Count<\/span><span class=\"pun\">();<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"typ\">Console<\/span><span class=\"pun\">.<\/span><span class=\"typ\">WriteLine<\/span><span class=\"pun\">(<\/span><span class=\"pln\">elementCount<\/span><span class=\"pun\">);<\/span><span class=\"pln\"><br>&nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br><\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br><br><\/span><span class=\"kwd\">public<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">static<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">class<\/span><span class=\"pln\"> <\/span><span class=\"typ\">UriFixer<\/span><span class=\"pln\"><br><\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; <\/span><span class=\"kwd\">public<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">static<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">void<\/span><span class=\"pln\"> <\/span><span class=\"typ\">FixInvalidUri<\/span><span class=\"pun\">(<\/span><span class=\"typ\">Stream<\/span><span class=\"pln\"> fs<\/span><span class=\"pun\">,<\/span><span class=\"pln\"> <\/span><span class=\"typ\">Func<\/span><span class=\"pun\">&lt;<\/span><span class=\"kwd\">string<\/span><span class=\"pun\">,<\/span><span class=\"pln\"> <\/span><span class=\"typ\">Uri<\/span><span class=\"pun\">&gt;<\/span><span class=\"pln\"> invalidUriHandler<\/span><span class=\"pun\">)<\/span><span class=\"pln\"><br>&nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"typ\">XNamespace<\/span><span class=\"pln\"> relNs <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> <\/span><span class=\"str\">\"http:\/\/schemas.openxmlformats.org\/package\/2006\/relationships\"<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">using<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"typ\">ZipArchive<\/span><span class=\"pln\"> za <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">new<\/span><span class=\"pln\"> <\/span><span class=\"typ\">ZipArchive<\/span><span class=\"pun\">(<\/span><span class=\"pln\">fs<\/span><span class=\"pun\">,<\/span><span class=\"pln\"> <\/span><span class=\"typ\">ZipArchiveMode<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Update<\/span><span class=\"pun\">))<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">foreach<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"kwd\">var<\/span><span class=\"pln\"> entry <\/span><span class=\"kwd\">in<\/span><span class=\"pln\"> za<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Entries<\/span><span class=\"pun\">.<\/span><span class=\"typ\">ToList<\/span><span class=\"pun\">())<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">if<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(!<\/span><span class=\"pln\">entry<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Name<\/span><span class=\"pun\">.<\/span><span class=\"typ\">EndsWith<\/span><span class=\"pun\">(<\/span><span class=\"str\">\".rels\"<\/span><span class=\"pun\">))<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">continue<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">bool<\/span><span class=\"pln\"> replaceEntry <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">false<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"typ\">XDocument<\/span><span class=\"pln\"> entryXDoc <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">null<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">using<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"kwd\">var<\/span><span class=\"pln\"> entryStream <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> entry<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Open<\/span><span class=\"pun\">())<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">try<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; entryXDoc <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> <\/span><span class=\"typ\">XDocument<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Load<\/span><span class=\"pun\">(<\/span><span class=\"pln\">entryStream<\/span><span class=\"pun\">);<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">if<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"pln\">entryXDoc<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Root<\/span><span class=\"pln\"> <\/span><span class=\"pun\">!=<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">null<\/span><span class=\"pln\"> <\/span><span class=\"pun\">&amp;&amp;<\/span><span class=\"pln\"> entryXDoc<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Root<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Name<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Namespace<\/span><span class=\"pln\"> <\/span><span class=\"pun\">==<\/span><span class=\"pln\"> relNs<\/span><span class=\"pun\">)<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">var<\/span><span class=\"pln\"> urisToCheck <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> entryXDoc<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">.<\/span><span class=\"typ\">Descendants<\/span><span class=\"pun\">(<\/span><span class=\"pln\">relNs <\/span><span class=\"pun\">+<\/span><span class=\"pln\"> <\/span><span class=\"str\">\"Relationship\"<\/span><span class=\"pun\">)<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">.<\/span><span class=\"typ\">Where<\/span><span class=\"pun\">(<\/span><span class=\"pln\">r <\/span><span class=\"pun\">=&gt;<\/span><span class=\"pln\"> r<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Attribute<\/span><span class=\"pun\">(<\/span><span class=\"str\">\"TargetMode\"<\/span><span class=\"pun\">)<\/span><span class=\"pln\"> <\/span><span class=\"pun\">!=<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">null<\/span><span class=\"pln\"> <\/span><span class=\"pun\">&amp;&amp;<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"kwd\">string<\/span><span class=\"pun\">)<\/span><span class=\"pln\">r<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Attribute<\/span><span class=\"pun\">(<\/span><span class=\"str\">\"TargetMode\"<\/span><span class=\"pun\">)<\/span><span class=\"pln\"> <\/span><span class=\"pun\">==<\/span><span class=\"pln\"> <\/span><span class=\"str\">\"External\"<\/span><span class=\"pun\">);<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">foreach<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"kwd\">var<\/span><span class=\"pln\"> rel <\/span><span class=\"kwd\">in<\/span><span class=\"pln\"> urisToCheck<\/span><span class=\"pun\">)<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">var<\/span><span class=\"pln\"> target <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"kwd\">string<\/span><span class=\"pun\">)<\/span><span class=\"pln\">rel<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Attribute<\/span><span class=\"pun\">(<\/span><span class=\"str\">\"Target\"<\/span><span class=\"pun\">);<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">if<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"pln\">target <\/span><span class=\"pun\">!=<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">null<\/span><span class=\"pun\">)<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">try<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"typ\">Uri<\/span><span class=\"pln\"> uri <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">new<\/span><span class=\"pln\"> <\/span><span class=\"typ\">Uri<\/span><span class=\"pun\">(<\/span><span class=\"pln\">target<\/span><span class=\"pun\">);<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">catch<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"typ\">UriFormatException<\/span><span class=\"pun\">)<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"typ\">Uri<\/span><span class=\"pln\"> newUri <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> invalidUriHandler<\/span><span class=\"pun\">(<\/span><span class=\"pln\">target<\/span><span class=\"pun\">);<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rel<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Attribute<\/span><span class=\"pun\">(<\/span><span class=\"str\">\"Target\"<\/span><span class=\"pun\">).<\/span><span class=\"typ\">Value<\/span><span class=\"pln\"> <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> newUri<\/span><span class=\"pun\">.<\/span><span class=\"typ\">ToString<\/span><span class=\"pun\">();<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; replaceEntry <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">true<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">catch<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"typ\">XmlException<\/span><span class=\"pun\">)<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">continue<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">if<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"pln\">replaceEntry<\/span><span class=\"pun\">)<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">var<\/span><span class=\"pln\"> fullName <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> entry<\/span><span class=\"pun\">.<\/span><span class=\"typ\">FullName<\/span><span class=\"pun\">;<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; entry<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Delete<\/span><span class=\"pun\">();<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">var<\/span><span class=\"pln\"> newEntry <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> za<\/span><span class=\"pun\">.<\/span><span class=\"typ\">CreateEntry<\/span><span class=\"pun\">(<\/span><span class=\"pln\">fullName<\/span><span class=\"pun\">);<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">using<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"typ\">StreamWriter<\/span><span class=\"pln\"> writer <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> <\/span><span class=\"kwd\">new<\/span><span class=\"pln\"> <\/span><span class=\"typ\">StreamWriter<\/span><span class=\"pun\">(<\/span><span class=\"pln\">newEntry<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Open<\/span><span class=\"pun\">()))<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"kwd\">using<\/span><span class=\"pln\"> <\/span><span class=\"pun\">(<\/span><span class=\"typ\">XmlWriter<\/span><span class=\"pln\"> xmlWriter <\/span><span class=\"pun\">=<\/span><span class=\"pln\"> <\/span><span class=\"typ\">XmlWriter<\/span><span class=\"pun\">.<\/span><span class=\"typ\">Create<\/span><span class=\"pun\">(<\/span><span class=\"pln\">writer<\/span><span class=\"pun\">))<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">{<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; entryXDoc<\/span><span class=\"pun\">.<\/span><span class=\"typ\">WriteTo<\/span><span class=\"pun\">(<\/span><span class=\"pln\">xmlWriter<\/span><span class=\"pun\">);<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; &nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br>&nbsp; &nbsp; <\/span><span class=\"pun\">}<\/span><span class=\"pln\"><br><\/span><span class=\"pun\">}<\/span><\/pre>\n<p>We are considering including this method in the Open XML SDK itself. &nbsp;We would make a few overloads of the <b>WordprocessingDocument.Open<\/b> method, the <b>SpreadsheetDocument.Open<\/b> method, and the <b>PresentationDocument.Open<\/b> method. &nbsp;These overloads would take the callback as an argument, just as in the above example. &nbsp;These new methods would first attempt to open the document in the normal way. &nbsp;If the attempt to open is successful, then these methods would return the newly opened document. &nbsp;<span style=\"font-size:12px;\">However, if <b>System.IO.Packaging <\/b>throws the <b>OpenXmlPackageException<\/b>, and if the document were opened for writing, then the method would open, modify, and save a fixed document. &nbsp;It would then attempt to open again, and return the newly opened document.<\/span><\/p>\n<p>With this approach, the idiom to open the document would be almost identical to the current approach to opening a document. &nbsp;The only difference would be the inclusion of the callback method as an argument.<\/p>\n<p>If the document was opened for read-only access, then the various methods would create a copy of the document in memory, fix the broken <b>Uri<\/b> objects, and then open and return the fixed document (for read-only access).<\/p>\n<p>Please feel free to comment about how this approach would work for you. &nbsp;If we have agreement on this approach, then in a month or two, we will make the change to the open source version of the Open XML SDK.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>One of the pernicious problems around the Open XML SDK is that if a document contains a hyperlink that has an invalid Uri, then Word, Excel, or PowerPoint will happily open the document, whereas System.IO.Packaging (and therefore the Open XML SDK) will throw an exception. The problem that we face with this is that we [&hellip;]<\/p>\n","protected":false},"author":10567,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"_s2mail":"","footnotes":""},"class_list":["post-2792","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/pages\/2792","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/users\/10567"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/comments?post=2792"}],"version-history":[{"count":1,"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/pages\/2792\/revisions"}],"predecessor-version":[{"id":2794,"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/pages\/2792\/revisions\/2794"}],"wp:attachment":[{"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/media?parent=2792"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}