Linq to XML Dalla teoria alla pratica Università di Genova Facoltà di Ingegneria Martedi 16 Dicembre 2008 Venerdi 19 Dicembre 2008 Email: [email protected] Blog: http://blogs.ugidotnet.org/raffaele Profilo: https://mvp.support.microsoft.com/profile/raffaele IL PERCORSO VERSO I LINGUAGGI FUNZIONALI Extension Methods (C# 3.0) • Sono metodi statici che possono essere invocati usando la sintassi del metodo di istanza • Vanno usati solo quando non è possibile fare diversamente – WPF e Linq ne fanno largo uso per estendere classi già scritte • Se nella classe estesa esiste già un metodo con quel nome/parametri, ha la precedenza sull'extension method namespace Raf.Helpers { public static class StringHelper { public static int ToInt32(this string s) { return Int32.Parse(s); } } } using Raf.Helpers; ...... string s = "2007"; int i = s.ToInt32(); Anonymous methods (C# 2.0) • C'erano una volta i delegate button1.Click += new EventHandler(button1_Click); ... private void button1_Click(object sender, EventArgs e) { MessageBox.Show((sender as Control).Name); } • Con C# 2.0 è possibile semplificare il loro uso: button2.Click += delegate(object sender, EventArgs e) { MessageBox.Show("Click!"); }; • Semplificazione ... C# 1.0 MyDlg del = new MyDlg(Method); C# 2.0 MyDlg del = Method; Espressioni Lambda (C# 3.0) • Evoluzione naturale degli Anonymous Methods – Offrono una sintassi concisa e funzionale per scrivere Anonymous Methods "goes to" parametro in ingresso x => x+1 elaborazione • Sono tipiche dei linguaggi funzionali • Rispetto agli Anonymous Methods – I type delle espressioni Lambda possono non essere espliciti (saranno dedotti) – La Lambda può essere sia un'espressione che un blocco ({..}) – Le espressioni possono essere convertite in un albero di espressioni • Le Lambda possono essere usate laddove è atteso un delegate Timer t = new Timer(x => Console.WriteLine("Tic"), null, 0, 1000); Lambda e i tipi "inferred" (dedotti) double seconds = F ( "1:15:30", str => TimeSpan.Parse(str), ts => ts.TotalSeconds); static Z F<X, Y, Z>( X value, Func<X, Y> f1, Func<Y, Z> f2) { return f2(f1(value)); } 1. "1:15:30" è assegnato a value. – 2. Il compilatore deduce che str sia string perché Parse accetta una string TimeSpan.Parse(str) restituisce TimeSpan ed è associato a f1. – 3. Il tipo dedotto X è string Il tipo dedotto di Y è System.TimeSpan ts.TotalSeconds restituisce un double ed è associato a f2. – Il tipo dedotto Z è un double Query Expressions (C# 3.0) • Sintassi di query integrata nel linguaggio simile a SQL – Validata dal compilatore e quindi strong-typed • L'espressione di query inizia con la clausola "from" e termina con una "select" o "group" • Dopo "from" possono esserci un numero di clausole – from causa la generazione di una o più variabili di iterazione su una sequenza o su una join di più sequenze – let causa il calcolo di un valore e introduce un identificatore che rappresenta quel valore – where filtro che esclude dei valori dal risultato – orderby indica l'ordine del risultato – select / group determina la forma del risultato (tipo di oggetto) – join unisce due set di dati secondo un criterio – into permette di riusare il risultato in una nuova query Expression Trees (C# 3.0) • Rendono possibile vedere le Lambda come dati anziché come codice eseguibile • Un'espressione Lambda convertibile ad un tipo D è convertibile ad un expression tree di tipo System.Linq.Expression<D> – È sufficiente che la lambda non abbia block statements • In pratica sono un binary tree “dati” e non più codice – – – – Serializzabile Facilmente costruibile designer! Interpretabile Permettono la conversione delle query nei provider Linq Sintassi in azione type inference della variabile locale var contacts = from c in customers where c.State == "WA" select new { c.Name, c.Phone }; Query expressions Lambda expressions var contacts = customers .Where(c => c.State == "WA") .Select(c => new { c.Name, c.Phone }); Extension methods Anonymous types Object initializers LINQ To Objects Restriction Where Projection Select, SelectMany Ordering OrderBy, ThenBy Grouping GroupBy Joins Join, GroupJoin Quantifiers Any, All Partitioning Take, Skip, TakeWhile, SkipWhile Sets Distinct, Union, Intersect, Except Elements First, Last, Single, ElementAt Aggregation Count, Sum, Min, Max, Average Conversion ToArray, ToList, ToDictionary Casting OfType<T>, Cast<T> Domande ? LINQ TO XML Perché una nuova API? • XML DOM – Molto completa ma anche complessa – Gestione dei namespace non certo banale • SAX – Sequenziale e basato su eventi. Spesso inutilizzabile • Stream (XmlReader / XmlWriter) – Richiede apertura/chiusura di ogni tag. Molto prono ad errori • XPath – Abbastanza potente ma terribilmente difficile da debuggare • Linq to XML – Object oriented (es: supporto operatore +) – Gerarchico (rispetta la natura di XML, più facile da leggere) – Interrogabile con Linq (query potenti e semplici da esprimere) XDocument • Rappresenta il documento XML • I metodi Load (statico) e Save permettono di leggere/scrivere il documento da Stream, XmlReader/Writer, etc. XDocument doc = XDocument.Load(@"c:\Temp\Rss.xml"); • Il metodo statico Parse permette di caricare da stringa • Ha una proprietà Root che restituisce un XElement • Ha una proprietà per dichiarare la declaration – <?xml version="1.0" encoding="utf-8" standalone="yes"?> XDocument doc = new XDocument(); doc.Declaration = new XDeclaration("1.0", "UTF-8", "yes"); • Ha una serie di metodi per aggiungere, eliminare, iterare gli elementi e i suoi children XNamespace • Definisce un namespace XML XNamespace ns = "http://www.unige.it"; • Supporta l'operatore + che restituisce un XName XNamespace unige = "http://www.unige.it"; XElement root = new XElement(unige + "Root", "Hello, world"); <Root xmlns="http://www.unige.it">Hello, world</Root> • XName è semplicemente una classe che combina il namespace e il nome dell'elemento – XName si può creare anche con la forma "{...}..." XElement root = new XElement("{"http://www.unige.it"}Root", "Hello, world"); <Root xmlns="http://www.unige.it">Hello, world</Root> Controllare i namespace • Vogliamo avere controllo sul prefisso dei namespace <unige:Root xmlns:unige="http://www.unige.it" xmlns:dist="http://dist.unige.it"> <dist:Child> <unige:DifferentChild>other content</unige:DifferentChild> </dist:Child> <unige:Child2>c2 content</unige:Child2> <dist:Child3>c3 content</dist:Child3> </unige:Root> XNamespace unige = "http://www.unige.it"; XNamespace dist = "http://dist.unige.it"; XElement root = new XElement(unige + "Root", new XAttribute(XNamespace.Xmlns + "unige", unige), new XAttribute(XNamespace.Xmlns + "dist", dist), new XElement(dist + "Child", new XElement(unige + "DifferentChild", "other content") ), new XElement(unige + "Child2", "c2 content"), new XElement(dist + "Child3", "c3 content") ); XElement • Un elemento semplice XElement el = new XElement(ns + "Saluti", "Hello, world"); <Saluti xmlns=http://IAmRaf.net>Hello, world</Saluti> • Composizione di elementi grazie a "params" XElement el = new XElement(ns + "Saluti", new XAttribute("Language", "English"), "Hello, world"); XElement root = new XElement(ns + "Parole", new XElement(ns + "Saluti", new XAttribute("Language", "English"), "Hello, world")); <Saluti Language="English" xmlns=http://IAmRaf.net> Hello, world </Saluti> <Parole xmlns="http://IAmRaf.net"> <Saluti Language="English">Hello, world</Saluti> </Parole> I FORMATI E LE APPLICAZIONI Il formato binario (.doc, .xls, .ppt) • Disponibili da sempre (sotto NDA) • Oggi sono pubblici • Convertibili in modo automatico e massivo – Compatiblity pack e Migration Planning Manager sono free • Usati solo per compatibilità verso i vecchi Office – Nessun modo ufficiale per convertire verso il formato binario Iniziativa Open Protocols • Pubblicati tutti i protocolli di comunicazione e i formati usati da Windows e gli altri prodotti Microsoft – http://msdn.microsoft.com/en-us/library/cc216514.aspx – SMB, BITS, RDP, RPC/HTTP, ... • SMB ... SaMBa per Linux ricorda qualcosa? – Address Book format, Office Crypto ext., Sharepoint, SMTP extensions (Exchange), ... • Alcuni di questi sono soggetti a brevetti: – http://www.microsoft.com/about/legal/intellectualproperty/protocols Microsoft Office • Versione 97, 2000, XP – Il file binario si può convertire in OOXML con i tool – Il tool è disponibile anche in versione "Shell Extension" • Versione 2003 – Esiste un plugin che gestisce nativamente OOXML • Versione 2007 – Gestisce il formato ECMA di OOXML • Versione 2007 SP2 (primavera 2009) – Gestisce anche ODF, PDF e XPS nativamente • Versione "Office 14" – Gestione del formato ISO di OOXML Open Office • Convertitori attuali – La versione Novell già supporta OOXML • La beta della versione 3.0 supporta OOXML • Attualmente OpenOffice non supporta ODF – http://www.griffinbrown.co.uk/blog/PermaLink.aspx?guid =f0384bed-808b-49a8-8887-ea7cde5caace OOXML per developers • OOXML è un formato file standard ISO che definisce la struttura di documenti di tipo Wordprocessing, Spreadsheet, Presentation – Office 2007 lo usa nativamente (docx, xlsx, pptx e derivati) • OOXML è composto da due livelli di astrazione: – Lo standard OPC • Definisce il formato fisico (ZIP) e la gerarchia dei file contenuti – Lo schema dei file XML che rappresentano il documento IL PACKAGE OPC Il Package OPC è un file ZIP • Open Packaging Conventions (standard ISO) – Il formato file è un normale ZIP – La struttura interna è definita nello standard Come manipolare un OPC • Per leggere/scrivere un file OPC: – Si può gestire "a mano" zip e xml • Qualsiasi piattaforma può manipolare zip e xml – Si possono usare le classi del Framework 3.0 • System.IO.Packaging.Package, etc. – Si usa ad esempio per XPS – Gestisce un 'generico' package – Si può usare l'Office Open XML SDK • DLL managed che include una serie di classi che conoscono le "part" specifiche del package di OOXML Leggere la 'part' principale di un Package con OOXML SDK (1 e 2) using (WordprocessingDocument doc = WordprocessingDocument.Open(_FileName, true)) { MainDocumentPart mainPart = doc.MainDocumentPart; Input using (StreamReader streamReader = new StreamReader(mainPart.GetStream())) { using (XmlReader stream = XmlReader.Create(streamReader)) { _MainDocument = XElement.Load(stream); } } } Output XElement using (SpreadsheetDocument xlPackage = SpreadsheetDocument.Open(_FileName, true)) { WorkbookPart workbook = xlPackage.WorkbookPart; OpenXmlPart part = workbook.GetPartById(Id); using (StreamReader streamReader = new StreamReader(part.GetStream())) { using (XmlReader stream = XmlReader.Create(streamReader)) { _CurrentSheet = XElement.Load(stream); } } // .... } Word Processing Markup Language WORDPROCESSINGML WordprocessingML • La "MainDocumentPart" contiene il corpo del documento • Tramite relazioni OPC alla MainDocumentPart sono collegati altri file XML File binari per le immagini HeaderPart: rappresenta gli header FooterPart: rappresenta i footer Settings part: le impostazioni per il viewer (Word), per la compatibilità con versioni precedenti e la conversione in Html – Numbering: le impostazioni sulla formattazione degli elenchi putntati e numerati – ... altre parts ... – – – – Struttura e query di un documento Informazioni essenziali <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <w:document xmlns:w=http://schemas.openxmlformats.org/wordprocessingml/2006/main <w:body> <w:p> public IEnumerable<XElement> FindOccurrenceOf(string text) <w:r> { <w:t>Hello, world</w:t> var v = </w:r> from b in MainDocument.Descendants(NS_w + "body") from p in b.Elements(NS_w + "p") </w:p> from r in p.Elements(NS_w + "r") </w:body> from t in r.Elements(NS_w + "t") </w:document> where ((string)t).Contains(text) select t; return v; • Ogni tag appartiene ad un namespace } xmlns:ve="http://schemas.openxmlformats.org/markup-compatibility/2006" 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:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" Formattazione • Bold <w:r> <w:rPr> <w:b/> </w:rPr> <w:t>Grassetto/w:t> </w:r> Italic <w:r> <w:rPr> <w:i/> </w:rPr> <w:t>Corsivo</w:t> </w:r> Underline <w:r> <w:rPr> <w:u/> </w:rPr> <w:t>Sottolinea</w:t> </w:r> public IEnumerable<string> GetBoldWords() { IEnumerable<string> words = from b in MainDocument.Descendants(NS_w + "body") from p in b.Elements(NS_w + "p") from r in p.Elements(NS_w + "r") where r.Descendants(NS_w + "rPr").Descendants(NS_w + "b").Count() == 1 select (string)r.Descendants(NS_w + "t").First(); return words; } Highlited <w:r> <w:rPr> <w:highlight w:val="yellow"/> </w:rPr> <w:t>Evidenziato</w:t> </w:r> Formattazione di run o paragrafo • Si possono formattare i paragrafi <pPr/> o i run <rPr/> – Possono esistere più <r> dentro un paragrafo. Questo consente una formattazione differenziata • Colore del testo (Test) – <w:color w:val="FF0000"/> • StrikeThrough (Test) – <w:strike/> • Superscript (Test) – <w:vertAlign w:val="superscript"/> • Subscript (Test) – <w:vertAlign w:val="subscript"/> • Font Size (Test) – <w:sz w:val="40"/> • Dentro <t> si usa 'preserve' per conservare lo spazio a inizio o fine testo – <w:t xml:space="preserve">: </w:t> Formattazione di paragrafo • Giustificazione – <w:jc w:val="right"/> (left, center, both, ...) • Spaziatura – <w:spacing w:after="0" w:line="240" w:lineRule="auto"/> – Conversioni • Per i twips la conversione è (int)(val * 1440) • La spaziatura si converte con (int)(NumLines * 240.0m) • Le specifiche spiegano come, dove, quando • Cambio pagina – <w:r> ... <w:br w:type="page"/> ... </w:r> Costuire un documento con XDocument (Linq to XML) • Creo con Word un documento <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"> <w:body> <w:p> <w:r> <w:t>Simple Text</w:t> </w:r> </w:p> </w:body> </w:document> • "Paste as XML" XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; XElement xml = new XElement(w + "document", new XAttribute(XNamespace.Xmlns + "w", w), new XElement(w + "body", new XElement(w + "p", new XElement(w + "r", new XElement(w + "t", "Simple Text") ) ) ) ); WORDPROCESSING DOCUMENT SpreadSheet Markup Language SPREADSHEETML SpreadSheetML • Il file del WorkBook tiene solo l'elenco dei sheet – – – – • Un documento XML per ogni "Sheet" In ogni Sheet c'è una root <sheetData /> Per ogni sheetData ci sono le <row/> Per ogni row ci sono le <c /> (cells) Cells: – Attributo "r" definisce il reference espresso in stile "A1" (r="B2") – Attributo "s" l'indice nella part degli stili (s="3") – Attributo "t" definisce il data type • b = bool, s = shared string, inlineStr = inline string, n = number • L'elemento child di <c> può essere – Di solito <v> (value) – Se t è inlineStr il child è <is> • <c r="A1" t="inlineStr"> <is><t>inline string</t></is> </c> – Se c'è una formula il child è <f /> • <c r="A2"> <f>A1+1</f> <v>2</v> </c> • Il numero dentro <v /> è una cache ed è omissibile Shared String Part • Una lookup table definisce la lista delle shared string • Dov'è la stringa? – <c r="A3" s="1" t="s"> <v>0</v> </c> • L'editor può fornire suggerimenti quando si digitano le prime lettere di una parola già presente nella tabella • Le shared string sono in una part separata • In alternativa si può usare inlineStr – <c r="C1" t="inlineStr"> <is> <t>inline string</t> </is> </c> <v>0</v> <v>1</v> <sst xmlns="....." count="3" uniqueCount="2"> <si> <t>Rosso</t> </si> <si> <t>Bianco</t> </si> </sst> Caricare le shared string private void LoadSharedStrings(WorkbookPart workbook) { if (_SharedStrings != null) return; SharedStringTablePart Shared = workbook.GetPartsOfType<SharedStringTablePart>().FirstOrDefault(); if (Shared == null) { _SharedStrings = new List<SharedString>(); // lista vuota return; } using (StreamReader streamReader = new StreamReader(Shared.GetStream())) { using (XmlReader stream = XmlReader.Create(streamReader)) { int index = 0; XElement shs = XElement.Load(stream); _SharedStrings = from si in shs.Descendants(NS_ex + "si") from t in si.Descendants(NS_ex + "t") select new SharedString() { Text = t.Value, Index = index++ }; } } } Stili e formattazione (relativi alla 'part' degli Styles) • Dentro <styleSheet /> ci sono due livelli di formattazione – <c r="B2" s="1"> <v>39451.413006267</v> </c> Master style Elemento 0 Stili referenziati dalle celle Elemento 1 <cellStyleXfs count="1"> <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"> <alignment horizontal="left" indent="0" textRotation="0" vertical="bottom" wrapText="0" /> </xf> </cellStyleXfs> <cellXfs count="2"> <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"> <alignment horizontal="left" indent="0" textRotation="0" vertical="bottom" wrapText="0" /> </xf> <xf numFmtId="14" fontId="0" fillId="0" borderId="1" xfId="0"> <alignment horizontal="left" indent="0" textRotation="0" vertical="bottom" wrapText="0" /> </xf> <xf numFmtId="164" fontId="0" fillId="0" borderId="1" xfId="0"> <alignment horizontal="left" indent="0" textRotation="0" vertical="bottom" wrapText="0" /> </xf> </cellXfs> Stili e formattazione • La data è solo un numero formattato in modo data – fontId, fillId, etc. si riferiscono ai vari fragment xml – numFmtId: la formattazione predefinita non necessita del corrispondente <numFmt /> (vedi specs per la lista) <numFmts count="1"> <numFmt numFmtId="164" formatCode="#.##0,000000" /> </numFmts> <fonts count="1"> <font> <sz val="11" /> <color auto="1" /> <name val="Calibri" /> <family val="2" /> <scheme val="minor" /> </font> </fonts> <xf numFmtId="164" fontId="0" fillId="0" borderId="1" xfId="0"> <alignment horizontal="left" indent="0" textRotation="0" vertical="bottom" wrapText="0" /> </xf> Cercare in uno SpreadSheetML public IEnumerable<CellSearch> FindOccurrenceOf(string text) { var inlinesimple = from d in _CurrentSheet.Descendants(NS_ex + "sheetData") from row in d.Descendants(NS_ex + "row") from c in row.Descendants(NS_ex + "c") from istag in c.Descendants(NS_ex + "is") from t in istag.Descendants(NS_ex + "t") where t.Value.Contains(text) select new CellSearch() { Cell = c.Attribute(XName.Get("r")).Value, Text = t.Value }; var inlinerich = from d in _CurrentSheet.Descendants(NS_ex + "sheetData") from row in d.Descendants(NS_ex + "row") from c in row.Descendants(NS_ex + "c") from istag in c.Descendants(NS_ex + "is") from r in c.Descendants(NS_ex + "istag") from t in istag.Descendants(NS_ex + "r") where t.Value.Contains(text) select new CellSearch() { Cell = c.Attribute(XName.Get("r")).Value, Text = t.Value }; var shared = from d in _CurrentSheet.Descendants(NS_ex + "sheetData") from row in d.Descendants(NS_ex + "row") from c in row.Descendants(NS_ex + "c") from v in c.Descendants(NS_ex + "v") where c.Attribute(XName.Get("t")).Value == "s" join ss in _SharedStrings on int.Parse(v.Value) equals ss.Index where ss.Text.Contains(text) select new CellSearch() { Cell = c.Attribute(XName.Get("r")).Value, Text = ss.Text }; var res = inlinesimple.Union(inlinerich).Union(shared); return res; } LINK UTILI Info di base • Home per developers – http://openxmldeveloper.org/ • Introduzione al formato OOXML – http://msdn.microsoft.com/en-us/library/aa338205.aspx • Office OpenXML SDK 1.0 • Documentazione • Download – – • http://msdn.microsoft.com/en-us/library/bb448854.aspx http://www.microsoft.com/downloads/details.aspx?FamilyId=AD0B72FB-4A1D-4C52-BDB57DD7E816D046&displaylang=en Office OpenXML SDK 2.0 CTP (no licenza go-live) – Documentazione • http://msdn.microsoft.com/en-us/library/bb448854(office.14).aspx – Download • • http://www.microsoft.com/downloads/details.aspx?FamilyId=C6E744E5-36E9-45F5-8D8C331DF206E0D0&displaylang=en Poster delle classi di OOXML – http://www.microsoft.com/downloads/details.aspx?familyid=134BCB91-DD7B-4209-AC942699B9366874&displaylang=en Documentazione standard OOXML • Standard ECMA TC45 (Office 2007) – http://www.ecmainternational.org/news/TC45_current_work/TC45_availabl e_docs.htm • Standard ISO/IEC 29500 (Office 14) – http://standards.iso.org/ittf/PubliclyAvailableStandards/in dex.html Conversioni e migrazione • Formato file binario – http://www.microsoft.com/interop/docs/OfficeBinaryFormats. mspx • Office Compatibility Pack – http://www.microsoft.com/downloads/details.aspx?FamilyID=9 41b3470-3ae9-4aee-8f43-c6bb74cd1466&displaylang=en • Office Migration Planning Manager (OMPM) – http://www.microsoft.com/downloads/details.aspx?FamilyID=1 3580cd7-a8bc-40ef-8281-dd2c325a5a81&DisplayLang=en • Come eseguire conversioni automatiche e massive – http://blogs.msdn.com/ericwhite/archive/2008/09/19/bulkconvert-doc-to-docx.aspx Altri convertitori • Convertitore OOXML (WordprocessingML) in XAML (FlowDocument) – http://www.codeplex.com/Word2007ToXaml • Convertitore doc to docx (presto anche gli altri) – http://b2xtranslator.sourceforge.net/ – Creato da DIaLOGIKa con Microsoft • Daisy: Digital Accessible Information System – http://sourceforge.net/projects/openxml-daisy – Altre news: • http://blogs.technet.com/reedblog/archive/2008/01/25/open-xml-to-daisy-v1-is-live.aspx • OOXML <-> ODF – http://odf-converter.sourceforge.net/ • Open Office (ODF) – Sun Plugin • http://www.sun.com/software/star/odf_plugin/index.jsp – Novell lo include nativamente Tools per OOXML • PowerTools (per PowerShell) – Iniziativa su Codeplex per script di PowerShell • http://www.codeplex.com/PowerTools • http://blogs.msdn.com/ericwhite/pages/PowerTools.aspx – Requisiti: Visual Studio express, PowerShell, OpenXML SDK • Altova Tool per OOXML – http://www.altova.com/ dev_portal_ooxml.html • XML "Paste As Linq" – Esempi VS2008 (CSharpSamples.zip) • LinqSamples\PasteXmlAsLinq • Word 2007 Content Control Toolkit (customXml e binding) – http://www.codeplex.com/dbe Bloggers • http://blogs.msdn.com/dmahugh • http://blogs.msdn.com/ericwhite • http://blogs.msdn.com/brian_jones Domande ? Adozione dei formati • Google trends – http://www.google.com/trends?q=.docx%2C+.odt – http://www.google.com/trends?q=.xlsx%2C+.ods I Power Tools per OOXML Accept-OpenXmlChange Add-OpenXmlContent Add-OpenXmlDigitalSignature Add-OpenXmlDocumentIndex Add-OpenXmlDocumentTOA Add-OpenXmlDocumentTOC Add-OpenXmlDocumentTOF Add-OpenXmlPicture Export-OpenXmlSpreadsheet Export-OpenXmlToHtml Export-OpenXmlWordprocessing Get-OpenXmlBackground Get-OpenXmlComment Get-OpenXmlCustomXmlData Get-OpenXmlDigitalSignature Get-OpenXmlDocument Get-OpenXmlFooter Get-OpenXmlHeader Get-OpenXmlStyle Get-OpenXmlTheme Get-OpenXmlWatermark Lock-OpenXmlDocument Remove-OpenXmlComment Remove-OpenXmlDigitalSignature Set-OpenXmlBackground Set-OpenXmlContentFormat Set-OpenXmlContentStyle Set-OpenXmlCustomXmlData Set-OpenXmlFooter Set-OpenXmlHeader Set-OpenXmlStyle Set-OpenXmlTheme Set-OpenXmlWatermark