Upload to server
uploading
This commit is contained in:
226
omegapro/PowerTools/Classes/BackgroundAccessor.cs
Normal file
226
omegapro/PowerTools/Classes/BackgroundAccessor.cs
Normal file
@@ -0,0 +1,226 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
public partial class WmlDocument : OpenXmlPowerToolsDocument
|
||||
{
|
||||
public string GetBackgroundColor()
|
||||
{
|
||||
return BackgroundAccessor.GetBackgroundColor(this);
|
||||
}
|
||||
public string GetBackgroundImageFileName()
|
||||
{
|
||||
return BackgroundAccessor.GetImageFileName(this);
|
||||
}
|
||||
public void SaveBackgroundImageToFile(string fileName)
|
||||
{
|
||||
BackgroundAccessor.SaveImageToFile(this, fileName);
|
||||
}
|
||||
public WmlDocument SetBackgroundColor(string colorValue)
|
||||
{
|
||||
return (WmlDocument)BackgroundAccessor.SetColor(this, colorValue);
|
||||
}
|
||||
public WmlDocument SetBackgroundImage(string imagePath)
|
||||
{
|
||||
return (WmlDocument)BackgroundAccessor.SetImage(this, imagePath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to background operations
|
||||
/// </summary>
|
||||
public class BackgroundAccessor
|
||||
{
|
||||
private const string defaultBackgroundColor = "FFFFFF";
|
||||
private const string defaultBWMode = "white";
|
||||
private const string defaultTargetScreenSize = "800,600";
|
||||
private const string defaultVmlBackgroundImageId = "_x0000_s1025";
|
||||
private const string defaultImageRecolor = "t";
|
||||
private const string defaultImageType = "frame";
|
||||
|
||||
private static XNamespace ns;
|
||||
private static XNamespace vmlns;
|
||||
private static XNamespace officens;
|
||||
private static XNamespace relationshipsns;
|
||||
|
||||
|
||||
public static string GetBackgroundColor(WmlDocument doc)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
|
||||
XElement backgroundElement = mainDocument.Descendants(W.background).FirstOrDefault();
|
||||
return (backgroundElement == null) ? string.Empty : backgroundElement.Attribute(W.color).Value;
|
||||
}
|
||||
}
|
||||
public static string GetImageFileName(WmlDocument doc)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
|
||||
XElement fillElement = mainDocument.Descendants(W.background).Descendants(VML.fill).FirstOrDefault();
|
||||
if (fillElement != null)
|
||||
{
|
||||
string imageRelationshipId = fillElement.Attribute(R.id).Value;
|
||||
OpenXmlPart imagePart = document.MainDocumentPart.GetPartById(imageRelationshipId);
|
||||
|
||||
// Gets the image name (path stripped)
|
||||
string imagePath = imagePart.Uri.OriginalString;
|
||||
return imagePath.Substring(imagePath.LastIndexOf('/') + 1);
|
||||
}
|
||||
else
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
public static void SaveImageToFile(WmlDocument doc, string fileName)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
|
||||
XElement fillElement = mainDocument.Descendants(W.background).Descendants(VML.fill).FirstOrDefault();
|
||||
if (fillElement != null)
|
||||
{
|
||||
string imageRelationshipId = fillElement.Attribute(R.id).Value;
|
||||
OpenXmlPart imagePart = document.MainDocumentPart.GetPartById(imageRelationshipId);
|
||||
|
||||
// Gets the image name (path stripped)
|
||||
string imagePath = imagePart.Uri.OriginalString;
|
||||
|
||||
// Writes the image outside the package
|
||||
OpenXmlPowerToolsDocument.SavePartAs(imagePart, fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static BackgroundAccessor()
|
||||
{
|
||||
ns = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
||||
vmlns = "urn:schemas-microsoft-com:vml";
|
||||
officens = "urn:schemas-microsoft-com:office:office";
|
||||
relationshipsns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Nodes list with background elements
|
||||
/// </summary>
|
||||
private static XElement BackgroundElement(WordprocessingDocument document)
|
||||
{
|
||||
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
|
||||
XElement result = mainDocument.Descendants(ns + "background").FirstOrDefault();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Nodes list with background elements
|
||||
/// </summary>
|
||||
private static XElement BackgroundFillElement(WordprocessingDocument document)
|
||||
{
|
||||
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
|
||||
XElement result = mainDocument.Descendants(ns + "background").Descendants(vmlns + "fill").FirstOrDefault();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the document background color
|
||||
/// </summary>
|
||||
/// <param name="colorValue">String representation of the hexadecimal RGB color</param>
|
||||
public static OpenXmlPowerToolsDocument SetColor(WmlDocument doc, string colorValue)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
|
||||
|
||||
// If the background element already exists, deletes it
|
||||
XElement backgroundElement = BackgroundElement(document);
|
||||
if (backgroundElement != null)
|
||||
backgroundElement.Remove();
|
||||
|
||||
mainDocument.Root.AddFirst(
|
||||
new XElement(ns + "background",
|
||||
new XAttribute(ns + "color", colorValue)
|
||||
)
|
||||
);
|
||||
|
||||
// Enables background displaying by adding "displayBackgroundShape" tag
|
||||
if (SettingAccessor.DisplayBackgroundShapeElements(document) == null)
|
||||
SettingAccessor.AddBackgroundShapeElement(document);
|
||||
|
||||
document.MainDocumentPart.PutXDocument();
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the document background image
|
||||
/// </summary>
|
||||
/// <param name="imagePath">Path of the background image</param>
|
||||
public static OpenXmlPowerToolsDocument SetImage(WmlDocument doc, string imagePath)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
|
||||
|
||||
// Adds the image to the package
|
||||
ImagePart imagePart = document.MainDocumentPart.AddImagePart(ImagePartType.Bmp);
|
||||
Stream imageStream = new StreamReader(imagePath).BaseStream;
|
||||
byte[] imageBytes = new byte[imageStream.Length];
|
||||
imageStream.Read(imageBytes, 0, imageBytes.Length);
|
||||
imagePart.GetStream().Write(imageBytes, 0, imageBytes.Length);
|
||||
|
||||
// Creates a "background" element relating the image and the document
|
||||
|
||||
// If the background element already exists, deletes it
|
||||
XElement backgroundElement = BackgroundElement(document);
|
||||
if (backgroundElement != null)
|
||||
backgroundElement.Remove();
|
||||
|
||||
// Background element construction
|
||||
mainDocument.Root.Add(
|
||||
new XElement(ns + "background",
|
||||
new XAttribute(ns + "color", defaultBackgroundColor),
|
||||
new XElement(vmlns + "background",
|
||||
new XAttribute(vmlns + "id", defaultVmlBackgroundImageId),
|
||||
new XAttribute(officens + "bwmode", defaultBWMode),
|
||||
new XAttribute(officens + "targetscreensize", defaultTargetScreenSize),
|
||||
new XElement(vmlns + "fill",
|
||||
new XAttribute(relationshipsns + "id", document.MainDocumentPart.GetIdOfPart(imagePart)),
|
||||
new XAttribute("recolor", defaultImageRecolor),
|
||||
new XAttribute("type", defaultImageType)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
// Enables background displaying by adding "displayBackgroundShape" tag
|
||||
if (SettingAccessor.DisplayBackgroundShapeElements(document) == null)
|
||||
SettingAccessor.AddBackgroundShapeElement(document);
|
||||
|
||||
document.MainDocumentPart.PutXDocument();
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
545
omegapro/PowerTools/Classes/ChartsheetAccessor.cs
Normal file
545
omegapro/PowerTools/Classes/ChartsheetAccessor.cs
Normal file
@@ -0,0 +1,545 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Chart types available
|
||||
/// </summary>
|
||||
public enum ChartType
|
||||
{
|
||||
/// <summary>
|
||||
/// Bar
|
||||
/// </summary>
|
||||
Bar,
|
||||
/// <summary>
|
||||
/// Column
|
||||
/// </summary>
|
||||
Column,
|
||||
/// <summary>
|
||||
/// Line
|
||||
/// </summary>
|
||||
Line,
|
||||
/// <summary>
|
||||
/// Area
|
||||
/// </summary>
|
||||
Area,
|
||||
/// <summary>
|
||||
/// Pie
|
||||
/// </summary>
|
||||
Pie
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to chartsheet operations
|
||||
/// </summary>
|
||||
public class ChartsheetAccessor
|
||||
{
|
||||
private const int defaultAnchorPosX = 0;
|
||||
private const int defaultAnchorPosY = 0;
|
||||
private const int defaultAnchorExtX = 8673523;
|
||||
private const int defaultAnchorExtY = 6306705;
|
||||
private const string defaultLegendPosition = "r";
|
||||
|
||||
private static XNamespace ns;
|
||||
private static XNamespace relationshipsns;
|
||||
private static XNamespace sdrns;
|
||||
private static XNamespace drawingns;
|
||||
private static XNamespace chartns;
|
||||
|
||||
static ChartsheetAccessor()
|
||||
{
|
||||
ns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
|
||||
relationshipsns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
||||
sdrns = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing";
|
||||
drawingns = "http://schemas.openxmlformats.org/drawingml/2006/main";
|
||||
chartns = "http://schemas.openxmlformats.org/drawingml/2006/chart";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a chartsheet part from given data
|
||||
/// </summary>
|
||||
/// <param name="chartType">Type of chart to generate</param>
|
||||
/// <param name="values">Values to represent in the chart</param>
|
||||
/// <param name="headerReference">Columns to be used as series</param>
|
||||
/// <param name="categoryReference">Column to be used as category</param>
|
||||
/// <returns>Chartsheet part with contents related</returns>
|
||||
public static ChartsheetPart Create(SpreadsheetDocument parentDocument, ChartType chartType, List<string> values, List<string> headerReference, string categoryReference)
|
||||
{
|
||||
//Creates base content and associates it to a new chartsheet part
|
||||
WorkbookPart workbook = parentDocument.WorkbookPart;
|
||||
ChartsheetPart chartsheetPart = workbook.AddNewPart<ChartsheetPart>();
|
||||
XDocument chartsheetDocument = CreateEmptyChartsheet();
|
||||
chartsheetPart.PutXDocument(chartsheetDocument);
|
||||
|
||||
//Creates a base drawings part and associates it to the chartsheet part
|
||||
DrawingsPart drawingsPart = chartsheetPart.AddNewPart<DrawingsPart>();
|
||||
XDocument drawingDocument = CreateEmptyDrawing();
|
||||
drawingsPart.PutXDocument(drawingDocument);
|
||||
|
||||
//Adds content to chartsheet document to reference drawing document
|
||||
chartsheetDocument
|
||||
.Element(ns + "chartsheet")
|
||||
.Add(
|
||||
new XElement(ns + "drawing",
|
||||
new XAttribute(relationshipsns + "id", chartsheetPart.GetIdOfPart(drawingsPart))
|
||||
)
|
||||
);
|
||||
|
||||
//creates the chart part and associates it to the drawings part
|
||||
ChartPart chartPart = drawingsPart.AddNewPart<ChartPart>();
|
||||
XDocument chartDocument = CreateChart(chartType, values, headerReference, categoryReference);
|
||||
chartPart.PutXDocument(chartDocument);
|
||||
|
||||
//Adds content to drawing document to reference chart document
|
||||
drawingDocument
|
||||
.Descendants(drawingns + "graphicData")
|
||||
.First()
|
||||
.Add(
|
||||
new XAttribute("uri", chartns),
|
||||
new XElement(chartns + "chart",
|
||||
new XAttribute(XNamespace.Xmlns + "c", chartns),
|
||||
new XAttribute(XNamespace.Xmlns + "r", relationshipsns),
|
||||
new XAttribute(relationshipsns + "id", drawingsPart.GetIdOfPart(chartPart))
|
||||
)
|
||||
);
|
||||
|
||||
//Associates the chartsheet part to the workbook part
|
||||
XDocument document = parentDocument.WorkbookPart.GetXDocument();
|
||||
|
||||
int sheetId = document.Root.Element(ns + "sheets").Elements(ns + "sheet").Count() + 1;
|
||||
|
||||
int chartsheetCount =
|
||||
document.Root
|
||||
.Element(ns + "sheets")
|
||||
.Elements(ns + "sheet")
|
||||
.Where(
|
||||
t =>
|
||||
t.Attribute("name").Value.StartsWith("chart")
|
||||
)
|
||||
.Count() + 1;
|
||||
|
||||
//Adds content to workbook document to reference chartsheet document
|
||||
document.Root
|
||||
.Element(ns + "sheets")
|
||||
.Add(
|
||||
new XElement(ns + "sheet",
|
||||
new XAttribute("name", string.Format("chart{0}", chartsheetCount)),
|
||||
new XAttribute("sheetId", sheetId),
|
||||
new XAttribute(relationshipsns + "id", workbook.GetIdOfPart(chartsheetPart))
|
||||
)
|
||||
);
|
||||
|
||||
chartsheetPart.PutXDocument();
|
||||
drawingsPart.PutXDocument();
|
||||
parentDocument.WorkbookPart.PutXDocument();
|
||||
|
||||
return chartsheetPart;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates element structure needed to describe an empty worksheet
|
||||
/// </summary>
|
||||
/// <returns>Document with contents for an empty worksheet</returns>
|
||||
private static XDocument CreateEmptyChartsheet()
|
||||
{
|
||||
XDocument document =
|
||||
new XDocument(
|
||||
new XElement(ns + "chartsheet",
|
||||
new XAttribute("xmlns", ns),
|
||||
new XAttribute(XNamespace.Xmlns + "r", relationshipsns),
|
||||
new XElement(ns + "sheetViews",
|
||||
new XElement(ns + "sheetView",
|
||||
new XAttribute("workbookViewId", 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
return document;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates tags to describe an empty row
|
||||
/// </summary>
|
||||
/// <param name="row"></param>
|
||||
/// <returns></returns>
|
||||
private static XElement CreateEmptyRow(int row)
|
||||
{
|
||||
XElement rowElement =
|
||||
new XElement(ns + "row",
|
||||
new XAttribute("r", row)
|
||||
);
|
||||
return rowElement;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates element structure needed to describe an empty drawing
|
||||
/// </summary>
|
||||
private static XDocument CreateEmptyDrawing()
|
||||
{
|
||||
return new XDocument(
|
||||
new XElement(sdrns + "wsDr",
|
||||
new XAttribute(XNamespace.Xmlns + "xdr", sdrns),
|
||||
new XAttribute(XNamespace.Xmlns + "a", drawingns),
|
||||
new XElement(sdrns + "absoluteAnchor",
|
||||
new XElement(sdrns + "pos",
|
||||
new XAttribute("x", defaultAnchorPosX),
|
||||
new XAttribute("y", defaultAnchorPosY)
|
||||
),
|
||||
new XElement(sdrns + "ext",
|
||||
new XAttribute("cx", defaultAnchorExtX),
|
||||
new XAttribute("cy", defaultAnchorExtY)
|
||||
),
|
||||
new XElement(sdrns + "graphicFrame",
|
||||
new XAttribute("macro", string.Empty),
|
||||
new XElement(sdrns + "nvGraphicFramePr",
|
||||
new XElement(sdrns + "cNvPr",
|
||||
new XAttribute("id", 2),
|
||||
new XAttribute("name", "Chart 1")
|
||||
),
|
||||
new XElement(sdrns + "cNvGraphicFramePr",
|
||||
new XElement(drawingns + "graphicFrameLocks",
|
||||
new XAttribute("noGrp", "1")
|
||||
)
|
||||
)
|
||||
),
|
||||
new XElement(sdrns + "xfrm",
|
||||
new XElement(drawingns + "off",
|
||||
new XAttribute("x", 0),
|
||||
new XAttribute("y", 0)
|
||||
),
|
||||
new XElement(drawingns + "ext",
|
||||
new XAttribute("cx", 0),
|
||||
new XAttribute("cy", 0)
|
||||
)
|
||||
),
|
||||
new XElement(drawingns + "graphic",
|
||||
new XElement(drawingns + "graphicData")
|
||||
)
|
||||
),
|
||||
new XElement(sdrns + "clientData")
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates element structure needed to describe an empty chart
|
||||
/// </summary>
|
||||
private static XDocument CreateEmptyChart()
|
||||
{
|
||||
return new XDocument(
|
||||
new XElement(chartns + "chartSpace",
|
||||
new XAttribute(XNamespace.Xmlns + "c", chartns),
|
||||
new XAttribute(XNamespace.Xmlns + "a", drawingns),
|
||||
new XAttribute(XNamespace.Xmlns + "r", relationshipsns),
|
||||
new XElement(chartns + "chart",
|
||||
new XElement(chartns + "title",
|
||||
new XElement(chartns + "layout")
|
||||
),
|
||||
new XElement(chartns + "plotArea",
|
||||
new XElement(chartns + "layout")
|
||||
),
|
||||
new XElement(chartns + "legend",
|
||||
new XElement(chartns + "legendPos",
|
||||
new XAttribute("val", defaultLegendPosition)
|
||||
),
|
||||
new XElement(chartns + "layout")
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates element structure needed to describe a chart with data related
|
||||
/// </summary>
|
||||
private static XDocument CreateChart(ChartType chartType, IEnumerable<string> seriesReferences, IEnumerable<string> seriesTitles, string category)
|
||||
{
|
||||
XDocument chartDocument = CreateEmptyChart();
|
||||
XElement chartElement =
|
||||
chartDocument
|
||||
.Element(chartns + "chartSpace")
|
||||
.Element(chartns + "chart")
|
||||
.Element(chartns + "plotArea");
|
||||
string categoryAxisId = "28819";
|
||||
string valueAxisId = "28818";
|
||||
|
||||
//Chooses the right chart type
|
||||
switch (chartType)
|
||||
{
|
||||
case ChartType.Bar:
|
||||
chartElement.Add(
|
||||
CreateBarChart(seriesReferences, seriesTitles, category, categoryAxisId, valueAxisId),
|
||||
CreateCategoryAxis(categoryAxisId, valueAxisId),
|
||||
CreateValueAxis(valueAxisId, categoryAxisId)
|
||||
);
|
||||
break;
|
||||
case ChartType.Column:
|
||||
chartElement.Add(
|
||||
CreateColumnChart(seriesReferences, seriesTitles, category, categoryAxisId, valueAxisId),
|
||||
CreateCategoryAxis(categoryAxisId, valueAxisId),
|
||||
CreateValueAxis(valueAxisId, categoryAxisId)
|
||||
);
|
||||
break;
|
||||
case ChartType.Line:
|
||||
chartElement.Add(
|
||||
CreateLineChart(seriesReferences, seriesTitles, category, categoryAxisId, valueAxisId),
|
||||
CreateCategoryAxis(categoryAxisId, valueAxisId),
|
||||
CreateValueAxis(valueAxisId, categoryAxisId)
|
||||
);
|
||||
break;
|
||||
case ChartType.Area:
|
||||
chartElement.Add(
|
||||
CreateAreaChart(seriesReferences, seriesTitles, category, categoryAxisId, valueAxisId),
|
||||
CreateCategoryAxis(categoryAxisId, valueAxisId),
|
||||
CreateValueAxis(valueAxisId, categoryAxisId)
|
||||
);
|
||||
break;
|
||||
case ChartType.Pie:
|
||||
chartElement.Add(
|
||||
CreatePieChart(seriesReferences, seriesTitles, category, categoryAxisId, valueAxisId)
|
||||
);
|
||||
break;
|
||||
}
|
||||
return chartDocument;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates element structure needed to describe a column chart with data related
|
||||
/// </summary>
|
||||
private static XElement CreateColumnChart(IEnumerable<string> seriesReferences, IEnumerable<string> seriesTitles, string category, string categoryAxisId, string valueAxisId)
|
||||
{
|
||||
return new XElement(chartns + "barChart",
|
||||
new XElement(chartns + "barDir",
|
||||
new XAttribute("val", "col")
|
||||
),
|
||||
new XElement(chartns + "grouping",
|
||||
new XAttribute("val", "clustered")
|
||||
),
|
||||
CreateSeries(seriesReferences, seriesTitles, category),
|
||||
seriesReferences.Count() < 1 ?
|
||||
new XElement(chartns + "gapWidth",
|
||||
new XAttribute("val", 100)
|
||||
)
|
||||
: null,
|
||||
new XElement(chartns + "axId",
|
||||
new XAttribute("val", categoryAxisId)
|
||||
),
|
||||
new XElement(chartns + "axId",
|
||||
new XAttribute("val", valueAxisId)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates element structure needed to describe a bar chart with data related
|
||||
/// </summary>
|
||||
private static XElement CreateBarChart(IEnumerable<string> seriesReferences, IEnumerable<string> seriesTitles, string category, string categoryAxisId, string valueAxisId)
|
||||
{
|
||||
return new XElement(chartns + "barChart",
|
||||
new XElement(chartns + "barDir",
|
||||
new XAttribute("val", "bar")
|
||||
),
|
||||
new XElement(chartns + "grouping",
|
||||
new XAttribute("val", "clustered")
|
||||
),
|
||||
CreateSeries(seriesReferences, seriesTitles, category),
|
||||
seriesReferences.Count() < 1 ?
|
||||
new XElement(chartns + "gapWidth",
|
||||
new XAttribute("val", 100)
|
||||
)
|
||||
: null,
|
||||
new XElement(chartns + "axId",
|
||||
new XAttribute("val", categoryAxisId)
|
||||
),
|
||||
new XElement(chartns + "axId",
|
||||
new XAttribute("val", valueAxisId)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates element structure needed to describe a line chart with data related
|
||||
/// </summary>
|
||||
private static XElement CreateLineChart(IEnumerable<string> seriesReferences, IEnumerable<string> seriesTitles, string category, string categoryAxisId, string valueAxisId)
|
||||
{
|
||||
return new XElement(chartns + "lineChart",
|
||||
new XElement(chartns + "grouping",
|
||||
new XAttribute("val", "standard")
|
||||
),
|
||||
CreateSeries(seriesReferences, seriesTitles, category),
|
||||
new XElement(chartns + "marker",
|
||||
new XAttribute("val", 1)
|
||||
),
|
||||
new XElement(chartns + "axId",
|
||||
new XAttribute("val", categoryAxisId)
|
||||
),
|
||||
new XElement(chartns + "axId",
|
||||
new XAttribute("val", valueAxisId)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates element structure needed to describe an area chart with data related
|
||||
/// </summary>
|
||||
private static XElement CreateAreaChart(IEnumerable<string> seriesReferences, IEnumerable<string> seriesTitles, string category, string categoryAxisId, string valueAxisId)
|
||||
{
|
||||
return new XElement(chartns + "areaChart",
|
||||
new XElement(chartns + "grouping",
|
||||
new XAttribute("val", "stacked")
|
||||
),
|
||||
CreateSeries(seriesReferences, seriesTitles, category),
|
||||
new XElement(chartns + "axId",
|
||||
new XAttribute("val", categoryAxisId)
|
||||
),
|
||||
new XElement(chartns + "axId",
|
||||
new XAttribute("val", valueAxisId)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates element structure needed to describe a pie chart with data related
|
||||
/// </summary>
|
||||
private static XElement CreatePieChart(IEnumerable<string> seriesReferences, IEnumerable<string> seriesTitles, string category, string categoryAxisId, string valueAxisId)
|
||||
{
|
||||
return new XElement(chartns + "pieChart",
|
||||
new XElement(chartns + "varyColors",
|
||||
new XAttribute("val", 1)
|
||||
),
|
||||
CreateSeries(seriesReferences, seriesTitles, category),
|
||||
new XElement(chartns + "firstSliceAng",
|
||||
new XAttribute("val", 0)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates element structure needed to describe a data series
|
||||
/// </summary>
|
||||
private static IEnumerable<XElement> CreateSeries(IEnumerable<string> seriesReference, IEnumerable<string> seriesTitles, string category)
|
||||
{
|
||||
List<XElement> seriesList = new List<XElement>();
|
||||
int numSeries = 0;
|
||||
foreach (var series in seriesReference)
|
||||
{
|
||||
seriesList.Add(
|
||||
new XElement(chartns + "ser",
|
||||
new XElement(chartns + "idx",
|
||||
new XAttribute("val", numSeries)
|
||||
),
|
||||
new XElement(chartns + "order",
|
||||
new XAttribute("val", numSeries)
|
||||
),
|
||||
new XElement(chartns + "tx",
|
||||
new XElement(chartns + "strRef",
|
||||
new XElement(chartns + "f", seriesTitles.ElementAt(numSeries))
|
||||
)
|
||||
),
|
||||
new XElement(chartns + "cat",
|
||||
new XElement(chartns + "strRef",
|
||||
new XElement(chartns + "f", category)
|
||||
)
|
||||
),
|
||||
new XElement(chartns + "val",
|
||||
new XElement(chartns + "numRef",
|
||||
new XElement(chartns + "f", series)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
numSeries++;
|
||||
}
|
||||
return seriesList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates element structure needed to describe a category axis
|
||||
/// </summary>
|
||||
private static XElement CreateCategoryAxis(string categoryAxisId, string valueAxisId)
|
||||
{
|
||||
return new XElement(chartns + "catAx",
|
||||
new XElement(chartns + "axId",
|
||||
new XAttribute("val", categoryAxisId)
|
||||
),
|
||||
new XElement(chartns + "scaling",
|
||||
new XElement(chartns + "orientation",
|
||||
new XAttribute("val", "minMax")
|
||||
)
|
||||
),
|
||||
new XElement(chartns + "axPos",
|
||||
new XAttribute("val", "b")
|
||||
),
|
||||
new XElement(chartns + "tickLblPos",
|
||||
new XAttribute("val", "nextTo")
|
||||
),
|
||||
new XElement(chartns + "crossAx",
|
||||
new XAttribute("val", valueAxisId)
|
||||
),
|
||||
new XElement(chartns + "crosses",
|
||||
new XAttribute("val", "autoZero")
|
||||
),
|
||||
new XElement(chartns + "auto",
|
||||
new XAttribute("val", 1)
|
||||
),
|
||||
new XElement(chartns + "lblAlgn",
|
||||
new XAttribute("val", "ctr")
|
||||
),
|
||||
new XElement(chartns + "lblOffset",
|
||||
new XAttribute("val", 100)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates element structure needed to describe a value axis
|
||||
/// </summary>
|
||||
private static XElement CreateValueAxis(string valueAxisId, string categoryAxisId)
|
||||
{
|
||||
return new XElement(chartns + "valAx",
|
||||
new XElement(chartns + "axId",
|
||||
new XAttribute("val", valueAxisId)
|
||||
),
|
||||
new XElement(chartns + "scaling",
|
||||
new XElement(chartns + "orientation",
|
||||
new XAttribute("val", "minMax")
|
||||
)
|
||||
),
|
||||
new XElement(chartns + "axPos",
|
||||
new XAttribute("val", "l")
|
||||
),
|
||||
new XElement(chartns + "majorGridlines"),
|
||||
new XElement(chartns + "numFmt",
|
||||
new XAttribute("formatCode", "General"),
|
||||
new XAttribute("sourceLinked", "1")
|
||||
),
|
||||
new XElement(chartns + "tickLblPos",
|
||||
new XAttribute("val", "nextTo")
|
||||
),
|
||||
new XElement(chartns + "crossAx",
|
||||
new XAttribute("val", categoryAxisId)
|
||||
),
|
||||
new XElement(chartns + "crosses",
|
||||
new XAttribute("val", "autoZero")
|
||||
),
|
||||
new XElement(chartns + "crossBetween",
|
||||
new XAttribute("val", "between")
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
124
omegapro/PowerTools/Classes/CommentAccessor.cs
Normal file
124
omegapro/PowerTools/Classes/CommentAccessor.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
public partial class WmlDocument : OpenXmlPowerToolsDocument
|
||||
{
|
||||
public object GetAllComments(CommentFormat format)
|
||||
{
|
||||
return CommentAccessor.GetAllComments(this, format);
|
||||
}
|
||||
}
|
||||
|
||||
public enum CommentFormat
|
||||
{
|
||||
PlainText,
|
||||
Xml,
|
||||
Docx
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to comment operations
|
||||
/// </summary>
|
||||
public class CommentAccessor
|
||||
{
|
||||
public static object GetAllComments(WmlDocument doc, CommentFormat format)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
IEnumerable<XElement> comments = null;
|
||||
WordprocessingCommentsPart commentsPart = document.MainDocumentPart.WordprocessingCommentsPart;
|
||||
if (commentsPart != null)
|
||||
{
|
||||
XDocument commentsPartDocument = commentsPart.GetXDocument();
|
||||
comments = commentsPartDocument.Element(W.comments).Elements(W.comment);
|
||||
}
|
||||
if (comments != null)
|
||||
{
|
||||
XDocument commentsDocument =
|
||||
new XDocument(
|
||||
new XElement(W.comments,
|
||||
new XAttribute(XNamespace.Xmlns + "w", W.w),
|
||||
comments
|
||||
)
|
||||
);
|
||||
switch (format)
|
||||
{
|
||||
case CommentFormat.PlainText:
|
||||
return commentsDocument.ToString();
|
||||
case CommentFormat.Xml:
|
||||
return commentsDocument;
|
||||
case CommentFormat.Docx:
|
||||
return CreateCommentsDocument(comments);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static OpenXmlPowerToolsDocument CreateCommentsDocument(IEnumerable<XElement> contents)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = OpenXmlMemoryStreamDocument.CreateWordprocessingDocument())
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
PowerToolsExtensions.SetContent(document, contents);
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all reference tags from inside the main part of a wordprocessing document
|
||||
/// </summary>
|
||||
private static IEnumerable<XElement> CommentReferences(WordprocessingDocument document)
|
||||
{
|
||||
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
|
||||
|
||||
IEnumerable<XElement> results =
|
||||
mainDocument.Descendants().Where(
|
||||
tag =>
|
||||
tag.Name == W.commentRangeStart ||
|
||||
tag.Name == W.commentRangeEnd ||
|
||||
(tag.Name == W.r && tag.Descendants(W.commentReference).Count() > 0)
|
||||
);
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all of the comments existing in the document
|
||||
/// </summary>
|
||||
public static OpenXmlPowerToolsDocument RemoveAll(WmlDocument doc)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
//Removes comment-related tags inside the main document part
|
||||
IEnumerable<XElement> commentReferences = CommentReferences(document).ToList();
|
||||
commentReferences.Remove();
|
||||
document.MainDocumentPart.PutXDocument();
|
||||
|
||||
WordprocessingCommentsPart commentsPart = document.MainDocumentPart.WordprocessingCommentsPart;
|
||||
if (commentsPart != null)
|
||||
commentsPart.RemovePart();
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
672
omegapro/PowerTools/Classes/CommentMerger.cs
Normal file
672
omegapro/PowerTools/Classes/CommentMerger.cs
Normal file
@@ -0,0 +1,672 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
public partial class WmlDocument : OpenXmlPowerToolsDocument
|
||||
{
|
||||
public WmlDocument MergeComments(WmlDocument other)
|
||||
{
|
||||
return CommentMerger.MergeComments(this, other);
|
||||
}
|
||||
}
|
||||
|
||||
public class CommentMergerInternalException : Exception
|
||||
{
|
||||
public CommentMergerInternalException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
public class CommentMergerDifferingContentsException : Exception
|
||||
{
|
||||
public CommentMergerDifferingContentsException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
public class CommentMergerUnlockedDocumentException : Exception
|
||||
{
|
||||
public CommentMergerUnlockedDocumentException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
public class CommentMerger
|
||||
{
|
||||
public static WmlDocument MergeComments(WmlDocument document1, WmlDocument document2)
|
||||
{
|
||||
return MergeComments(document1, document2, true);
|
||||
}
|
||||
|
||||
public static WmlDocument MergeComments(WmlDocument document1, WmlDocument document2,
|
||||
bool ensureLocked)
|
||||
{
|
||||
WmlDocument cDoc1 = new WmlDocument(document1);
|
||||
WmlDocument cDoc2 = new WmlDocument(document2);
|
||||
using (OpenXmlMemoryStreamDocument streamDoc1 = new OpenXmlMemoryStreamDocument(cDoc1))
|
||||
using (WordprocessingDocument doc1 = streamDoc1.GetWordprocessingDocument())
|
||||
using (OpenXmlMemoryStreamDocument streamDoc2 = new OpenXmlMemoryStreamDocument(cDoc2))
|
||||
using (WordprocessingDocument doc2 = streamDoc2.GetWordprocessingDocument())
|
||||
{
|
||||
SimplifyMarkupSettings mss = new SimplifyMarkupSettings()
|
||||
{
|
||||
RemoveProof = true,
|
||||
RemoveRsidInfo = true,
|
||||
RemoveGoBackBookmark = true,
|
||||
};
|
||||
MarkupSimplifier.SimplifyMarkup(doc1, mss);
|
||||
MarkupSimplifier.SimplifyMarkup(doc2, mss);
|
||||
|
||||
// If documents don't contain the same content, then don't attempt to merge comments.
|
||||
bool same = DocumentComparer.CompareDocuments(doc1, doc2);
|
||||
if (!same)
|
||||
throw new CommentMergerDifferingContentsException(
|
||||
"Documents do not contain the same content");
|
||||
|
||||
if (doc1.MainDocumentPart.WordprocessingCommentsPart == null &&
|
||||
doc2.MainDocumentPart.WordprocessingCommentsPart == null)
|
||||
return new WmlDocument(document1);
|
||||
if (doc1.MainDocumentPart.WordprocessingCommentsPart != null &&
|
||||
doc2.MainDocumentPart.WordprocessingCommentsPart == null)
|
||||
return new WmlDocument(document1);
|
||||
if (doc1.MainDocumentPart.WordprocessingCommentsPart == null &&
|
||||
doc2.MainDocumentPart.WordprocessingCommentsPart != null)
|
||||
return new WmlDocument(document2);
|
||||
// If either of the documents have no comments, then return the other one.
|
||||
if (! doc1.MainDocumentPart.WordprocessingCommentsPart.GetXDocument().Root
|
||||
.Elements(W.comment).Any())
|
||||
return new WmlDocument(document2);
|
||||
if (! doc2.MainDocumentPart.WordprocessingCommentsPart.GetXDocument().Root
|
||||
.Elements(W.comment).Any())
|
||||
return new WmlDocument(document1);
|
||||
|
||||
if (ensureLocked)
|
||||
{
|
||||
// If either document is not locked (allowing only commenting), don't attempt to
|
||||
// merge comments.
|
||||
if (doc1.ExtendedFilePropertiesPart.GetXDocument().Root
|
||||
.Element(EP.DocSecurity).Value != "8")
|
||||
throw new CommentMergerUnlockedDocumentException(
|
||||
"Document1 is not locked");
|
||||
if (doc2.ExtendedFilePropertiesPart.GetXDocument().Root
|
||||
.Element(EP.DocSecurity).Value != "8")
|
||||
throw new CommentMergerUnlockedDocumentException(
|
||||
"Document2 is not locked");
|
||||
}
|
||||
|
||||
RenumberCommentsInDoc2(doc1, doc2);
|
||||
|
||||
WmlDocument destDoc = new WmlDocument(document1);
|
||||
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(destDoc))
|
||||
{
|
||||
using (WordprocessingDocument destWDoc = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
// Merge the comments part.
|
||||
XDocument commentsPartXDoc = new XDocument(
|
||||
new XElement(W.comments,
|
||||
new XAttribute(XNamespace.Xmlns + "w", W.w),
|
||||
doc1.MainDocumentPart.WordprocessingCommentsPart.GetXDocument().Root.Elements(),
|
||||
doc2.MainDocumentPart.WordprocessingCommentsPart.GetXDocument().Root.Elements()));
|
||||
destWDoc.MainDocumentPart.WordprocessingCommentsPart.PutXDocument(commentsPartXDoc);
|
||||
|
||||
MergeCommentsInPart(doc1.MainDocumentPart, doc2.MainDocumentPart,
|
||||
destWDoc.MainDocumentPart, commentsPartXDoc);
|
||||
}
|
||||
return streamDoc.GetModifiedWmlDocument();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsCommentElement(XElement e)
|
||||
{
|
||||
return e.Name == W.commentRangeStart || e.Name == W.commentRangeEnd ||
|
||||
e.Name == W.commentReference;
|
||||
}
|
||||
|
||||
private static IEnumerable<object> SplitElements(IEnumerable<XElement> elements)
|
||||
{
|
||||
IEnumerable<object> splitElements = elements
|
||||
.Select(e =>
|
||||
{
|
||||
if ((e.Name == W.r && e.Elements(W.t).Any()) ||
|
||||
(e.Name == M.r && e.Elements(M.t).Any()))
|
||||
{
|
||||
var grouped = e.Elements().Where(ce => ce.Name != W.rPr && ce.Name != M.rPr)
|
||||
.GroupAdjacent(ce =>
|
||||
(ce.Name == W.t || ce.Name == M.t));
|
||||
return grouped.Select(g =>
|
||||
{
|
||||
if (g.Key == true)
|
||||
{
|
||||
string s = g.Select(t => (string)t).StringConcatenate();
|
||||
return s.Select(c => new XElement(e.Name,
|
||||
e.Attributes(),
|
||||
e.Elements(W.rPr),
|
||||
e.Elements(M.rPr),
|
||||
new XElement(g.First().Name, c)));
|
||||
}
|
||||
return g.Select(ce => new XElement(e.Name,
|
||||
e.Attributes(),
|
||||
e.Elements(W.rPr),
|
||||
e.Elements(M.rPr),
|
||||
ce));
|
||||
});
|
||||
}
|
||||
return (object)e;
|
||||
});
|
||||
return splitElements;
|
||||
}
|
||||
|
||||
private class RunOrderingInfo
|
||||
{
|
||||
public int Integer;
|
||||
public bool IsRun;
|
||||
|
||||
public RunOrderingInfo(int integer, bool isRun)
|
||||
{
|
||||
Integer = integer;
|
||||
IsRun = isRun;
|
||||
}
|
||||
public RunOrderingInfo(int integer)
|
||||
{
|
||||
Integer = integer;
|
||||
IsRun = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static XName[] RunContentElements = new[]
|
||||
{
|
||||
M.t,
|
||||
W.br,
|
||||
W.cr,
|
||||
W.dayLong,
|
||||
W.dayShort,
|
||||
W.drawing,
|
||||
W.endnoteReference,
|
||||
W.fldChar,
|
||||
W.footnoteReference,
|
||||
W.instrText,
|
||||
W.lastRenderedPageBreak,
|
||||
W.monthLong,
|
||||
W.monthShort,
|
||||
W.noBreakHyphen,
|
||||
W._object,
|
||||
W.ptab,
|
||||
W.ruby,
|
||||
W.softHyphen,
|
||||
W.sym,
|
||||
W.t,
|
||||
W.tab,
|
||||
W.yearLong,
|
||||
W.yearShort,
|
||||
};
|
||||
|
||||
private static void AddAnnotationsToChildren(XElement parent)
|
||||
{
|
||||
int index = 1000;
|
||||
foreach (var ce in parent
|
||||
.Elements().Where(e => e.Name == W.r || e.Name == M.r)
|
||||
.Where(r =>
|
||||
{
|
||||
foreach (var item in RunContentElements)
|
||||
if (r.Element(item) != null)
|
||||
return true;
|
||||
return false;
|
||||
}))
|
||||
{
|
||||
ce.AddAnnotation(new RunOrderingInfo(index, true));
|
||||
index += 1000;
|
||||
}
|
||||
foreach (var ce in parent.Elements().Where(e => e.Annotation<RunOrderingInfo>() == null))
|
||||
{
|
||||
var elementWithAnnoAfter = ce
|
||||
.ElementsAfterSelf()
|
||||
.FirstOrDefault(e => e.Annotation<RunOrderingInfo>() != null);
|
||||
if (elementWithAnnoAfter != null)
|
||||
{
|
||||
int nextIndex = elementWithAnnoAfter.Annotation<RunOrderingInfo>().Integer;
|
||||
var elementsAfter = ce.ElementsAfterSelf()
|
||||
.TakeWhile(ea => ea.Annotation<RunOrderingInfo>() == null);
|
||||
ce.AddAnnotation(new RunOrderingInfo(nextIndex - elementsAfter.Count() - 1));
|
||||
continue;
|
||||
}
|
||||
// The element before must have an annotation.
|
||||
var elementBefore = ce.ElementsBeforeSelfReverseDocumentOrder().FirstOrDefault();
|
||||
if (elementBefore != null)
|
||||
{
|
||||
RunOrderingInfo elementBeforeAnnotation = elementBefore.Annotation<RunOrderingInfo>();
|
||||
ce.AddAnnotation(new RunOrderingInfo(elementBeforeAnnotation.Integer + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static XAttribute GetXmlSpaceAttribute(
|
||||
string textElementValue)
|
||||
{
|
||||
if (textElementValue.Length > 0 &&
|
||||
(textElementValue[0] == ' ' ||
|
||||
textElementValue[textElementValue.Length - 1] == ' '))
|
||||
return new XAttribute(XNamespace.Xml + "space",
|
||||
"preserve");
|
||||
return null;
|
||||
}
|
||||
|
||||
private static XElement MergeElementWithChildrenCommentElements(XElement e1, XElement e2,
|
||||
XDocument commentsPartXDoc)
|
||||
{
|
||||
if (e1.Name.Namespace != W.w)
|
||||
Console.WriteLine("processing mathml");
|
||||
if (e1.Name != e2.Name)
|
||||
throw new CommentMergerInternalException(
|
||||
"attempting to merge elements that do not match up.");
|
||||
|
||||
var split1 = SplitElements(e1.Elements());
|
||||
var split2 = SplitElements(e2.Elements());
|
||||
XElement temp1 = new XElement(e1.Name,
|
||||
e1.Attributes(),
|
||||
split1);
|
||||
XElement temp2 = new XElement(e2.Name,
|
||||
e2.Attributes(),
|
||||
split2);
|
||||
|
||||
// todo may want to remove following test.
|
||||
if (temp1.Elements().Where(e => e.Name == W.r || e.Name == M.r).Where(r =>
|
||||
{
|
||||
foreach (var item in RunContentElements)
|
||||
if (r.Element(item) != null)
|
||||
return true;
|
||||
return false;
|
||||
}).Count() !=
|
||||
temp2.Elements().Where(e => e.Name == W.r || e.Name == M.r).Where(r =>
|
||||
{
|
||||
foreach (var item in RunContentElements)
|
||||
if (r.Element(item) != null)
|
||||
return true;
|
||||
return false;
|
||||
}).Count())
|
||||
throw new CommentMergerInternalException("runs do not line up");
|
||||
|
||||
AddAnnotationsToChildren(temp1);
|
||||
AddAnnotationsToChildren(temp2);
|
||||
|
||||
if (temp1.Elements().Any(e => e.Annotation<RunOrderingInfo>() == null) ||
|
||||
temp2.Elements().Any(e => e.Annotation<RunOrderingInfo>() == null))
|
||||
throw new CommentMergerInternalException("some child does not have annotation");
|
||||
|
||||
var m = temp1
|
||||
.Elements()
|
||||
.Concat(temp2.Elements())
|
||||
.OrderBy(e => e.Annotation<RunOrderingInfo>().Integer)
|
||||
.GroupAdjacent(e => e.Annotation<RunOrderingInfo>().Integer);
|
||||
|
||||
XElement mergedElementWithSplitRuns = new XElement(e1.Name,
|
||||
e1.Attributes(),
|
||||
m.Select(g =>
|
||||
{
|
||||
if (g.First().Annotation<RunOrderingInfo>().IsRun)
|
||||
return (object)g.First();
|
||||
else
|
||||
return g;
|
||||
}));
|
||||
|
||||
// The following query serves to remove duplicate comments, i.e. the same exact
|
||||
// comment from the same person is in each of the documents being merged.
|
||||
var groupedCommentReferences = mergedElementWithSplitRuns.Elements()
|
||||
.GroupAdjacent(e =>
|
||||
{
|
||||
if (e.Name == W.r || e.Name == M.r)
|
||||
{
|
||||
bool onlyOneChild = e.Elements().Where(z => z.Name != W.rPr).Count() == 1;
|
||||
if (onlyOneChild)
|
||||
{
|
||||
XElement child = e.Elements().Where(z => z.Name != W.rPr).First();
|
||||
if (child.Name == W.commentRangeStart ||
|
||||
child.Name == W.commentRangeEnd ||
|
||||
child.Name == W.commentReference)
|
||||
{
|
||||
XElement comment = commentsPartXDoc.Root
|
||||
.Elements(W.comment)
|
||||
.Where(c => (string)c.Attribute(W.id) == (string)child.Attribute(W.id))
|
||||
.FirstOrDefault();
|
||||
string s = child.Name.LocalName + "|" +
|
||||
comment.Attribute(W.author).Value + "|" +
|
||||
comment.Attribute(W.date).Value + "|" +
|
||||
comment.Attribute(W.initials).Value + "|" +
|
||||
comment.Descendants()
|
||||
.Where(d => d.Name == W.t || d.Name == M.t)
|
||||
.Select(t => (string)t).StringConcatenate();
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e.Name != W.commentRangeStart &&
|
||||
e.Name != W.commentRangeEnd &&
|
||||
e.Name != W.commentReference)
|
||||
return "NotAComment";
|
||||
XElement comment2 = commentsPartXDoc.Root
|
||||
.Elements(W.comment)
|
||||
.Where(c => (string)c.Attribute(W.id) == (string)e.Attribute(W.id))
|
||||
.FirstOrDefault();
|
||||
string s2 = e.Name.LocalName + "|" +
|
||||
comment2.Attribute(W.author).Value + "|" +
|
||||
comment2.Attribute(W.date).Value + "|" +
|
||||
comment2.Attribute(W.initials).Value + "|" +
|
||||
comment2.Descendants()
|
||||
.Where(d => d.Name == W.t || d.Name == M.t)
|
||||
.Select(t => (string)t).StringConcatenate();
|
||||
return s2;
|
||||
});
|
||||
|
||||
XElement duplicateCommentsEliminated = new XElement(e1.Name,
|
||||
e1.Attributes(),
|
||||
groupedCommentReferences
|
||||
.Select(g =>
|
||||
{
|
||||
if (g.Key == "NotAComment")
|
||||
return (object)g;
|
||||
return g.First();
|
||||
}));
|
||||
|
||||
var runGroups = duplicateCommentsEliminated.Elements()
|
||||
.GroupAdjacent(r =>
|
||||
{
|
||||
if (r.Name != W.r && r.Name != M.r)
|
||||
return "NotRuns";
|
||||
if (! r.Elements().Where(e => e.Name == W.rPr || e.Name == M.rPr).Any())
|
||||
return "NoRunProperties";
|
||||
XElement frag = new XElement("frag",
|
||||
new XAttribute(XNamespace.Xmlns + "w", W.w),
|
||||
r.Elements().Where(e => e.Name == W.rPr || e.Name == M.rPr));
|
||||
return frag.ToString(
|
||||
SaveOptions.DisableFormatting);
|
||||
});
|
||||
|
||||
XElement newParagraph = new XElement(e1.Name,
|
||||
e1.Attributes(),
|
||||
runGroups.Select(g =>
|
||||
{
|
||||
if (g.Key == "NotRuns")
|
||||
return (object)g;
|
||||
if (g.Key == "NoRunProperties")
|
||||
{
|
||||
XElement newRun = new XElement(g.First().Name,
|
||||
g.First().Attributes(),
|
||||
g.Elements()
|
||||
.GroupAdjacent(c => c.Name)
|
||||
.Select(gc =>
|
||||
{
|
||||
if (gc.Key != W.t && gc.Key != M.t)
|
||||
return (object)gc;
|
||||
string textElementValue =
|
||||
gc.Select(t => (string)t)
|
||||
.StringConcatenate();
|
||||
return new XElement(gc.First().Name.Namespace + "t",
|
||||
GetXmlSpaceAttribute(
|
||||
textElementValue),
|
||||
textElementValue);
|
||||
}));
|
||||
return newRun;
|
||||
}
|
||||
XElement runPropertyFragment = XElement.Parse(
|
||||
g.Key);
|
||||
runPropertyFragment.Elements().Attributes()
|
||||
.Where(a => a.IsNamespaceDeclaration)
|
||||
.Remove();
|
||||
XElement newRunWithProperties = new XElement(g.First().Name.Namespace + "r",
|
||||
runPropertyFragment.Elements(),
|
||||
g.Elements()
|
||||
.Where(e => e.Name != W.rPr && e.Name != M.rPr)
|
||||
.GroupAdjacent(c => c.Name)
|
||||
.Select(gc =>
|
||||
{
|
||||
if (gc.Key != W.t && gc.Key != M.t)
|
||||
return (object)gc;
|
||||
string textElementValue = gc
|
||||
.Select(t => (string)t)
|
||||
.StringConcatenate();
|
||||
return new XElement(gc.Key,
|
||||
GetXmlSpaceAttribute(textElementValue),
|
||||
textElementValue);
|
||||
}));
|
||||
return newRunWithProperties;
|
||||
}
|
||||
));
|
||||
|
||||
return newParagraph;
|
||||
}
|
||||
|
||||
private static object MergeElementTransform(XElement e1, XElement e2, XDocument commentsPartXDoc)
|
||||
{
|
||||
// todo need better message
|
||||
if (e1.Name != e2.Name)
|
||||
throw new CommentMergerDifferingContentsException("Internal error");
|
||||
if (e1.Elements()
|
||||
.Where(e => IsCommentElement(e)).Any() || e2.Elements().Where(e => IsCommentElement(e))
|
||||
.Any())
|
||||
return MergeElementWithChildrenCommentElements(e1, e2, commentsPartXDoc);
|
||||
var zippedChildren = e1.Elements().Zip(e2.Elements(), (p1, p2) => new
|
||||
{
|
||||
Element1 = p1,
|
||||
Element2 = p2,
|
||||
});
|
||||
return new XElement(e1.Name,
|
||||
e1.Attributes(),
|
||||
zippedChildren.Select(z =>
|
||||
{
|
||||
if (z.Element1.Name == W.t && z.Element2.Name == W.t &&
|
||||
z.Element1.Value == z.Element2.Value)
|
||||
return new XElement(W.t,
|
||||
GetXmlSpaceAttribute(z.Element1.Value),
|
||||
z.Element1.Value);
|
||||
if (z.Element1.Name == M.t && z.Element2.Name == M.t &&
|
||||
z.Element1.Value == z.Element2.Value)
|
||||
return new XElement(M.t,
|
||||
GetXmlSpaceAttribute(z.Element1.Value),
|
||||
z.Element1.Value);
|
||||
return MergeElementTransform(z.Element1, z.Element2, commentsPartXDoc);
|
||||
}));
|
||||
}
|
||||
|
||||
private static void MergeCommentsInPart(OpenXmlPart part1, OpenXmlPart part2,
|
||||
OpenXmlPart destinationPart, XDocument commentsPartXDoc)
|
||||
{
|
||||
XDocument xdoc1 = part1.GetXDocument();
|
||||
XDocument xdoc2 = part2.GetXDocument();
|
||||
|
||||
XElement newRootElement = (XElement)MergeElementTransform(xdoc1.Root, xdoc2.Root,
|
||||
commentsPartXDoc);
|
||||
destinationPart.PutXDocument(new XDocument(newRootElement));
|
||||
}
|
||||
|
||||
// todo are there any other elements that need ids fixed? see children of paragraph, run
|
||||
private static void FixIdsInPart(OpenXmlPart part, int nextCommentId, int nextBookmarkId)
|
||||
{
|
||||
if (part == null)
|
||||
return;
|
||||
foreach (var element in part.GetXDocument().Root.Descendants()
|
||||
.Where(e => e.Name == W.commentRangeStart ||
|
||||
e.Name == W.commentRangeEnd ||
|
||||
e.Name == W.commentReference))
|
||||
element.Attribute(W.id).Value = ((int)element.Attribute(W.id) + nextCommentId).ToString();
|
||||
foreach (var element in part.GetXDocument().Root.Descendants()
|
||||
.Where(e => e.Name == W.bookmarkStart || e.Name == W.bookmarkEnd))
|
||||
element.Attribute(W.id).Value = ((int)element.Attribute(W.id) + nextBookmarkId).ToString();
|
||||
}
|
||||
|
||||
private static void RenumberCommentsInDoc2(WordprocessingDocument doc1, WordprocessingDocument doc2)
|
||||
{
|
||||
// Get the XDocuments for the two comments parts.
|
||||
XDocument doc1WordprocessingCommentsXDocument = doc1
|
||||
.MainDocumentPart
|
||||
.WordprocessingCommentsPart
|
||||
.GetXDocument();
|
||||
int commentIdIncrease = doc1WordprocessingCommentsXDocument
|
||||
.Root
|
||||
.Elements(W.comment)
|
||||
.Attributes(W.id)
|
||||
.Select(a => (int)a)
|
||||
.Max() + 1;
|
||||
int nextBookmarkId = new[] { 0 }
|
||||
.Concat(doc1.MainDocumentPart
|
||||
.GetXDocument()
|
||||
.Root
|
||||
.Descendants(W.bookmarkStart)
|
||||
.Attributes(W.id)
|
||||
.Select(a => (int)a))
|
||||
.Max();
|
||||
foreach (var part in doc1.MainDocumentPart.HeaderParts)
|
||||
nextBookmarkId = new[] { nextBookmarkId }
|
||||
.Concat(part
|
||||
.GetXDocument()
|
||||
.Root
|
||||
.Descendants(W.bookmarkStart)
|
||||
.Attributes(W.id)
|
||||
.Select(a => (int)a))
|
||||
.Max();
|
||||
foreach (var part in doc1.MainDocumentPart.FooterParts)
|
||||
nextBookmarkId = new[] { nextBookmarkId }
|
||||
.Concat(part
|
||||
.GetXDocument()
|
||||
.Root
|
||||
.Descendants(W.bookmarkStart)
|
||||
.Attributes(W.id)
|
||||
.Select(a => (int)a))
|
||||
.Max();
|
||||
if (doc1.MainDocumentPart.FootnotesPart != null)
|
||||
nextBookmarkId = new[] { nextBookmarkId }
|
||||
.Concat(doc1
|
||||
.MainDocumentPart
|
||||
.FootnotesPart
|
||||
.GetXDocument()
|
||||
.Root
|
||||
.Descendants(W.bookmarkStart)
|
||||
.Attributes(W.id)
|
||||
.Select(a => (int)a))
|
||||
.Max();
|
||||
if (doc1.MainDocumentPart.EndnotesPart != null)
|
||||
nextBookmarkId = new[] { nextBookmarkId }
|
||||
.Concat(doc1
|
||||
.MainDocumentPart
|
||||
.EndnotesPart
|
||||
.GetXDocument()
|
||||
.Root
|
||||
.Descendants(W.bookmarkStart)
|
||||
.Attributes(W.id)
|
||||
.Select(a => (int)a))
|
||||
.Max();
|
||||
nextBookmarkId++;
|
||||
|
||||
foreach (var element in doc2.MainDocumentPart.WordprocessingCommentsPart.GetXDocument()
|
||||
.Root.Elements(W.comment))
|
||||
element.Attribute(W.id).Value =
|
||||
(((int)element.Attribute(W.id)) + commentIdIncrease).ToString();
|
||||
FixIdsInPart(doc2.MainDocumentPart, commentIdIncrease, nextBookmarkId);
|
||||
foreach (var header in doc2.MainDocumentPart.HeaderParts)
|
||||
FixIdsInPart(header, commentIdIncrease, nextBookmarkId);
|
||||
foreach (var footer in doc2.MainDocumentPart.FooterParts)
|
||||
FixIdsInPart(footer, commentIdIncrease, nextBookmarkId);
|
||||
FixIdsInPart(doc2.MainDocumentPart.FootnotesPart, commentIdIncrease, nextBookmarkId);
|
||||
FixIdsInPart(doc2.MainDocumentPart.EndnotesPart, commentIdIncrease, nextBookmarkId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if false
|
||||
todo here are two m:oMath elements that need merged.
|
||||
|
||||
<m:oMath xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">
|
||||
<m:r>
|
||||
<w:rPr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math" />
|
||||
</w:rPr>
|
||||
<m:t>W</m:t>
|
||||
</m:r>
|
||||
<m:r>
|
||||
<w:rPr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math" />
|
||||
</w:rPr>
|
||||
<m:t>=</m:t>
|
||||
</m:r>
|
||||
<m:f>
|
||||
<m:fPr>
|
||||
<m:ctrlPr>
|
||||
<w:rPr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math" />
|
||||
<w:i />
|
||||
</w:rPr>
|
||||
</m:ctrlPr>
|
||||
</m:fPr>
|
||||
<m:num>
|
||||
<m:r>
|
||||
<w:rPr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math" />
|
||||
</w:rPr>
|
||||
<m:t>1</m:t>
|
||||
</m:r>
|
||||
</m:num>
|
||||
<m:den>
|
||||
<m:r>
|
||||
<w:rPr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math" />
|
||||
</w:rPr>
|
||||
<m:t>μ(1-U)</m:t>
|
||||
</m:r>
|
||||
</m:den>
|
||||
</m:f>
|
||||
</m:oMath>
|
||||
|
||||
|
||||
|
||||
<m:oMath xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">
|
||||
<w:commentRangeStart w:id="6" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" />
|
||||
<m:r>
|
||||
<w:rPr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math" />
|
||||
</w:rPr>
|
||||
<m:t>W</m:t>
|
||||
</m:r>
|
||||
<w:commentRangeEnd w:id="6" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" />
|
||||
<m:r>
|
||||
<m:rPr>
|
||||
<m:sty m:val="p" />
|
||||
</m:rPr>
|
||||
<w:rPr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:rStyle w:val="CommentReference" />
|
||||
</w:rPr>
|
||||
<w:commentReference w:id="6" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" />
|
||||
</m:r>
|
||||
<m:r>
|
||||
<w:rPr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math" />
|
||||
</w:rPr>
|
||||
<m:t>=</m:t>
|
||||
</m:r>
|
||||
<m:f>
|
||||
<m:fPr>
|
||||
<m:ctrlPr>
|
||||
<w:rPr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math" />
|
||||
<w:i />
|
||||
</w:rPr>
|
||||
</m:ctrlPr>
|
||||
</m:fPr>
|
||||
<m:num>
|
||||
<m:r>
|
||||
<w:rPr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math" />
|
||||
</w:rPr>
|
||||
<m:t>1</m:t>
|
||||
</m:r>
|
||||
</m:num>
|
||||
<m:den>
|
||||
<m:r>
|
||||
<w:rPr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math" />
|
||||
</w:rPr>
|
||||
<m:t>μ(1-U)</m:t>
|
||||
</m:r>
|
||||
</m:den>
|
||||
</m:f>
|
||||
</m:oMath>
|
||||
#endif
|
88
omegapro/PowerTools/Classes/ContentFormatAccessor.cs
Normal file
88
omegapro/PowerTools/Classes/ContentFormatAccessor.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to content format operations
|
||||
/// </summary>
|
||||
public class ContentFormatAccessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Inserts Xml markup representing format attributes inside a specific paragraph or paragraph run
|
||||
/// </summary>
|
||||
/// <param name="document">Document to insert formatting Xml tags</param>
|
||||
/// <param name="xpathInsertionPoint">Paragraph or paragraph run to set format</param>
|
||||
/// <param name="content">Formatting tags</param>
|
||||
public static OpenXmlPowerToolsDocument Insert(WmlDocument doc, string xpathInsertionPoint, string content)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
XDocument xDocument = document.MainDocumentPart.GetXDocument();
|
||||
XmlDocument xmlMainDocument = new XmlDocument();
|
||||
xmlMainDocument.Load(xDocument.CreateReader());
|
||||
|
||||
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(new NameTable());
|
||||
namespaceManager.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
|
||||
|
||||
XmlNodeList insertionPoints = xmlMainDocument.SelectNodes(xpathInsertionPoint, namespaceManager);
|
||||
|
||||
if (insertionPoints.Count == 0)
|
||||
throw new ArgumentException("The xpath query did not return a valid location.");
|
||||
|
||||
foreach (XmlNode insertionPoint in insertionPoints)
|
||||
{
|
||||
XmlNode propertiesElement = insertionPoint.SelectSingleNode(@"w:pPr|w:rPr", namespaceManager);
|
||||
if (insertionPoint.Name == "w:p")
|
||||
{
|
||||
// Checks if the rPr or pPr element exists
|
||||
if (propertiesElement == null)
|
||||
{
|
||||
propertiesElement = xmlMainDocument.CreateElement("w", "pPr", namespaceManager.LookupNamespace("w"));
|
||||
insertionPoint.PrependChild(propertiesElement);
|
||||
}
|
||||
}
|
||||
else if (insertionPoint.Name == "w:r")
|
||||
{
|
||||
// Checks if the rPr or pPr element exists
|
||||
if (propertiesElement == null)
|
||||
{
|
||||
propertiesElement = xmlMainDocument.CreateElement("w", "rPr", namespaceManager.LookupNamespace("w"));
|
||||
insertionPoint.PrependChild(propertiesElement);
|
||||
}
|
||||
}
|
||||
|
||||
if (propertiesElement != null)
|
||||
{
|
||||
propertiesElement.InnerXml += content;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Specified xpath query result is not a valid location to place a formatting markup");
|
||||
}
|
||||
|
||||
}
|
||||
XDocument xNewDocument = new XDocument();
|
||||
using (XmlWriter xWriter = xNewDocument.CreateWriter())
|
||||
xmlMainDocument.WriteTo(xWriter);
|
||||
document.MainDocumentPart.PutXDocument(xNewDocument);
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
155
omegapro/PowerTools/Classes/ContentStyleAccessor.cs
Normal file
155
omegapro/PowerTools/Classes/ContentStyleAccessor.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to content style operations
|
||||
/// </summary>
|
||||
public class ContentStyleAccessor
|
||||
{
|
||||
private static XNamespace ns;
|
||||
private const string newStyleNameSuffix = "_1";
|
||||
|
||||
static ContentStyleAccessor()
|
||||
{
|
||||
ns = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tree representing all style hierarchy for a given style
|
||||
/// </summary>
|
||||
/// <param name="styleName">Name of style</param>
|
||||
/// <param name="stylesFile">Styles library</param>
|
||||
/// <returns>Style tree</returns>
|
||||
private Collection<XElement> GetStyleHierarchy(string styleName, string stylesFile)
|
||||
{
|
||||
Collection<XElement> stylesCollection = new Collection<XElement>();
|
||||
XDocument xml = new XDocument();
|
||||
xml = XDocument.Load(stylesFile);
|
||||
GetStyleDefinition(styleName, xml, stylesCollection);
|
||||
|
||||
return stylesCollection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the style definition for the document
|
||||
/// </summary>
|
||||
/// <param name="styleName">Style name</param>
|
||||
/// <param name="xmlStyleDefinitions">Style library</param>
|
||||
/// <param name="stylesCollection">Styles</param>
|
||||
public void GetStyleDefinition(string styleName, XDocument xmlStyleDefinitions, Collection<XElement> stylesCollection)
|
||||
{
|
||||
XName style = ns + "style";
|
||||
XName styleId = ns + "styleId";
|
||||
|
||||
// create a copy of the xmlstyleDefinition variable so the
|
||||
// original xml don't be altered
|
||||
XElement actualStyle =
|
||||
new XElement(
|
||||
xmlStyleDefinitions
|
||||
.Descendants()
|
||||
.Where(
|
||||
tag =>
|
||||
(tag.Name == style) && (tag.Attribute(styleId).Value == styleName)
|
||||
)
|
||||
.ToList()
|
||||
.FirstOrDefault()
|
||||
);
|
||||
|
||||
if (actualStyle != null)
|
||||
{
|
||||
// look in the stylesCollection if the style has already been added
|
||||
IEnumerable<XElement> insertedStyles =
|
||||
stylesCollection.Where
|
||||
(
|
||||
tag =>
|
||||
(tag.Name == style) && (tag.Attribute(styleId).Value == styleName)
|
||||
);
|
||||
|
||||
// if the style has not been inserted
|
||||
if (!(insertedStyles.Count() > 0))
|
||||
{
|
||||
stylesCollection.Add(actualStyle);
|
||||
GetStyleDefinition(getLinkStyleId(actualStyle), xmlStyleDefinitions, stylesCollection);
|
||||
GetStyleDefinition(getNextStyleId(actualStyle), xmlStyleDefinitions, stylesCollection);
|
||||
GetStyleDefinition(getBasedOnStyleId(actualStyle), xmlStyleDefinitions, stylesCollection);
|
||||
|
||||
}
|
||||
// change the name of the style, so there would be no conflict
|
||||
// with the original styles definition
|
||||
actualStyle.Attribute(styleId).Value = actualStyle.Attribute(styleId).Value + newStyleNameSuffix;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the linked style associated to the given style
|
||||
/// </summary>
|
||||
/// <param name="xmlStyle">Style to find link</param>
|
||||
/// <returns>Linked style name</returns>
|
||||
public string getLinkStyleId(XElement xmlStyle)
|
||||
{
|
||||
XName val = ns + "val";
|
||||
string linkStyleId = "";
|
||||
XElement linkStyle = xmlStyle.Descendants(ns + "link").FirstOrDefault();
|
||||
if (linkStyle != null)
|
||||
{
|
||||
linkStyleId = linkStyle.Attribute(val).Value;
|
||||
// change the name of the attribute, because the new added style is being renamed
|
||||
linkStyle.Attribute(val).Value = linkStyle.Attribute(val).Value + newStyleNameSuffix;
|
||||
}
|
||||
return linkStyleId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the style tagged as 'next' associated to the given style
|
||||
/// </summary>
|
||||
/// <param name="xmlStyle">Style to find 'next' element</param>
|
||||
/// <returns>Name of style tagged as 'next</returns>
|
||||
public string getNextStyleId(XElement xmlStyle)
|
||||
{
|
||||
XName val = ns + "val";
|
||||
string nextStyleId = "";
|
||||
XElement nextStyle = xmlStyle.Descendants(ns + "next").FirstOrDefault();
|
||||
if (nextStyle != null)
|
||||
{
|
||||
nextStyleId = nextStyle.Attribute(val).Value;
|
||||
// change the name of the attribute, because the new added style is being renamed
|
||||
nextStyle.Attribute(val).Value = nextStyle.Attribute(val).Value + newStyleNameSuffix;
|
||||
}
|
||||
return nextStyleId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the name of the style tagged as 'basedOn' associated to the given style
|
||||
/// </summary>
|
||||
/// <param name="xmlStyle">Style to find 'basedOn' element</param>
|
||||
/// <returns>Name of style tagged as 'basedOn'</returns>
|
||||
public string getBasedOnStyleId(XElement xmlStyle)
|
||||
{
|
||||
XName val = ns + "val";
|
||||
string basedOnStyleId = "";
|
||||
XElement basedOnStyle = xmlStyle.Descendants(ns + "basedOn").FirstOrDefault();
|
||||
if (basedOnStyle != null)
|
||||
{
|
||||
basedOnStyleId = basedOnStyle.Attribute(val).Value;
|
||||
// change the name of the attribute, because the new added style is being renamed
|
||||
basedOnStyle.Attribute(val).Value = basedOnStyle.Attribute(val).Value + newStyleNameSuffix;
|
||||
}
|
||||
return basedOnStyleId;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
67
omegapro/PowerTools/Classes/CustomXmlAccessor.cs
Normal file
67
omegapro/PowerTools/Classes/CustomXmlAccessor.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Performs operations on document custom xml
|
||||
/// </summary>
|
||||
public class CustomXmlAccessor{
|
||||
/// <summary>
|
||||
/// Searches for a custom Xml part with a given name
|
||||
/// </summary>
|
||||
/// <param name="xmlPartName">Name of custom Xml part</param>
|
||||
/// <returns>XDocument with customXml part loaded</returns>
|
||||
public static XDocument Find(WmlDocument doc, string xmlPartName)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
WordprocessingDocument document = streamDoc.GetWordprocessingDocument();
|
||||
string partName = "/" + xmlPartName;
|
||||
var customXmlPart =
|
||||
document.MainDocumentPart.CustomXmlParts.Where(
|
||||
t => t.Uri.OriginalString.EndsWith(partName, System.StringComparison.OrdinalIgnoreCase)
|
||||
).FirstOrDefault();
|
||||
if (customXmlPart == null)
|
||||
throw new ArgumentException("Part name '" + xmlPartName + "' not found.");
|
||||
return customXmlPart.GetXDocument();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces a previously existing customXml part by another one
|
||||
/// </summary>
|
||||
/// <param name="customXmlDocument">XDocument of part to replace inside the document package</param>
|
||||
/// <param name="partNameOnly">Name of the part.</param>
|
||||
public static OpenXmlPowerToolsDocument SetDocument(WmlDocument doc, XDocument customXmlDocument, string partNameOnly)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
string partName = "/" + partNameOnly;
|
||||
var customXmlPart = document.MainDocumentPart.CustomXmlParts.Where(
|
||||
t => t.Uri.OriginalString.EndsWith(partName, System.StringComparison.OrdinalIgnoreCase)
|
||||
).FirstOrDefault();
|
||||
|
||||
if (customXmlPart == null)
|
||||
customXmlPart = document.MainDocumentPart.AddCustomXmlPart(CustomXmlPartType.CustomXml);
|
||||
customXmlPart.PutXDocument(customXmlDocument);
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
104
omegapro/PowerTools/Classes/DigitalSignatureAccessor.cs
Normal file
104
omegapro/PowerTools/Classes/DigitalSignatureAccessor.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
public class DigitalSignatureAccessor
|
||||
{
|
||||
public static OpenXmlPowerToolsDocument Insert(OpenXmlPowerToolsDocument doc, IEnumerable<string> certificateList)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (Package package = streamDoc.GetPackage())
|
||||
{
|
||||
foreach (string digitalCertificate in certificateList)
|
||||
{
|
||||
X509Certificate x509Certificate = X509Certificate2.CreateFromCertFile(digitalCertificate);
|
||||
PackageDigitalSignatureManager digitalSigntaureManager = new PackageDigitalSignatureManager(package);
|
||||
digitalSigntaureManager.CertificateOption = CertificateEmbeddingOption.InSignaturePart;
|
||||
System.Collections.Generic.List<Uri> partsToSign = new System.Collections.Generic.List<Uri>();
|
||||
//Adds each part to the list, except relationships parts.
|
||||
foreach (PackagePart openPackagePart in package.GetParts())
|
||||
{
|
||||
if (!PackUriHelper.IsRelationshipPartUri(openPackagePart.Uri))
|
||||
partsToSign.Add(openPackagePart.Uri);
|
||||
}
|
||||
List<PackageRelationshipSelector> relationshipSelectors = new List<PackageRelationshipSelector>();
|
||||
//Creates one selector for each package-level relationship, based on id
|
||||
foreach (PackageRelationship relationship in package.GetRelationships())
|
||||
{
|
||||
PackageRelationshipSelector relationshipSelector =
|
||||
new PackageRelationshipSelector(relationship.SourceUri, PackageRelationshipSelectorType.Id, relationship.Id);
|
||||
relationshipSelectors.Add(relationshipSelector);
|
||||
}
|
||||
digitalSigntaureManager.Sign(partsToSign, x509Certificate, relationshipSelectors);
|
||||
}
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests a Digital Signature from a package
|
||||
/// </summary>
|
||||
/// <returns>Digital signatures list</returns>
|
||||
public static Collection<string> GetList(OpenXmlPowerToolsDocument doc)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
// Creates the PackageDigitalSignatureManager
|
||||
PackageDigitalSignatureManager digitalSignatureManager = new PackageDigitalSignatureManager(streamDoc.GetPackage());
|
||||
// Verifies the collection of certificates in the package
|
||||
Collection<string> digitalSignatureDescriptions = new Collection<string>();
|
||||
ReadOnlyCollection<PackageDigitalSignature> digitalSignatures = digitalSignatureManager.Signatures;
|
||||
if (digitalSignatures.Count > 0)
|
||||
{
|
||||
foreach (PackageDigitalSignature signature in digitalSignatures)
|
||||
{
|
||||
if (PackageDigitalSignatureManager.VerifyCertificate(signature.Signer) != X509ChainStatusFlags.NoError)
|
||||
{
|
||||
digitalSignatureDescriptions.Add(string.Format(System.Globalization.CultureInfo.InvariantCulture, "Signature: {0} ({1})", signature.Signer.Subject, PackageDigitalSignatureManager.VerifyCertificate(signature.Signer)));
|
||||
}
|
||||
else
|
||||
digitalSignatureDescriptions.Add("Signature: " + signature.Signer.Subject);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
digitalSignatureDescriptions.Add("No digital signatures found");
|
||||
}
|
||||
return digitalSignatureDescriptions;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RemoveAll
|
||||
/// </summary>
|
||||
public static OpenXmlPowerToolsDocument RemoveAll(OpenXmlPowerToolsDocument doc)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (Package package = streamDoc.GetPackage())
|
||||
{
|
||||
// Creates the PackageDigitalSignatureManager
|
||||
PackageDigitalSignatureManager digitalSignatureManager = new PackageDigitalSignatureManager(package);
|
||||
digitalSignatureManager.RemoveAllSignatures();
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
2170
omegapro/PowerTools/Classes/DocumentBuilder.cs
Normal file
2170
omegapro/PowerTools/Classes/DocumentBuilder.cs
Normal file
File diff suppressed because it is too large
Load Diff
346
omegapro/PowerTools/Classes/DocumentComparer.cs
Normal file
346
omegapro/PowerTools/Classes/DocumentComparer.cs
Normal file
@@ -0,0 +1,346 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
public class DocumentComparerInternalException : Exception
|
||||
{
|
||||
public DocumentComparerInternalException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
public static class DocumentComparer
|
||||
{
|
||||
private enum RunElementType
|
||||
{
|
||||
WordRun,
|
||||
MathRun,
|
||||
Other
|
||||
};
|
||||
|
||||
private enum RunChildElementType
|
||||
{
|
||||
WordTextElement,
|
||||
MathTextElement,
|
||||
Other
|
||||
};
|
||||
|
||||
private static bool CompareRunCollections(IEnumerable<XElement> runCollection1,
|
||||
IEnumerable<XElement> runCollection2)
|
||||
{
|
||||
var runChildElementCollection1 = runCollection1
|
||||
.Elements()
|
||||
.Where(e => e.Name != W.commentRangeStart &&
|
||||
e.Name != W.commentRangeEnd &&
|
||||
e.Name != W.commentReference &&
|
||||
e.Name != W.rPr &&
|
||||
e.Name != W.proofErr &&
|
||||
e.Name != M.sty &&
|
||||
e.Name != M.rPr)
|
||||
.GroupAdjacent(e =>
|
||||
{
|
||||
if (e.Name == W.t)
|
||||
return RunChildElementType.WordTextElement;
|
||||
if (e.Name == M.t)
|
||||
return RunChildElementType.MathTextElement;
|
||||
return RunChildElementType.Other;
|
||||
});
|
||||
var runChildElementCollection2 = runCollection2
|
||||
.Elements()
|
||||
.Where(e => e.Name != W.commentRangeStart &&
|
||||
e.Name != W.commentRangeEnd &&
|
||||
e.Name != W.commentReference &&
|
||||
e.Name != W.rPr &&
|
||||
e.Name != W.proofErr &&
|
||||
e.Name != M.sty &&
|
||||
e.Name != M.rPr)
|
||||
.GroupAdjacent(e =>
|
||||
{
|
||||
if (e.Name == W.t)
|
||||
return RunChildElementType.WordTextElement;
|
||||
if (e.Name == M.t)
|
||||
return RunChildElementType.MathTextElement;
|
||||
return RunChildElementType.Other;
|
||||
});
|
||||
#if false
|
||||
foreach (var item in runChildElementCollection1)
|
||||
{
|
||||
Console.WriteLine(item.Key);
|
||||
foreach (var i2 in item)
|
||||
{
|
||||
Console.WriteLine(i2.Name.LocalName);
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
Console.WriteLine();
|
||||
foreach (var item in runChildElementCollection2)
|
||||
{
|
||||
Console.WriteLine(item.Key);
|
||||
foreach (var i2 in item)
|
||||
{
|
||||
Console.WriteLine(i2.Name.LocalName);
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
Environment.Exit(0);
|
||||
#endif
|
||||
bool different = runChildElementCollection1
|
||||
.Zip(runChildElementCollection2, (c1, c2) =>
|
||||
{
|
||||
if (c1.Key != c2.Key)
|
||||
return false;
|
||||
switch (c1.Key)
|
||||
{
|
||||
case RunChildElementType.WordTextElement:
|
||||
case RunChildElementType.MathTextElement:
|
||||
string string1 = c1.Select(t => (string)t).StringConcatenate();
|
||||
string string2 = c2.Select(t => (string)t).StringConcatenate();
|
||||
if (string1 == string2)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
case RunChildElementType.Other:
|
||||
bool d = c1
|
||||
.Zip(c2, (e1, e2) => new { E1 = e1, E2 = e2 })
|
||||
.Select(z => CompareOpenXmlElements(z.E1, z.E2))
|
||||
.Any(b => b == false);
|
||||
if (d == true)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
};
|
||||
throw new DocumentComparerInternalException(
|
||||
"Should not have reached this code");
|
||||
})
|
||||
.Any(b => b == false);
|
||||
if (different == true)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool CompareOtherCollections(IEnumerable<XElement> collection1,
|
||||
IEnumerable<XElement> collection2)
|
||||
{
|
||||
bool different = collection1
|
||||
.Zip(collection2, (e1, e2) => new { E1 = e1, E2 = e2 })
|
||||
.Select(z => CompareOpenXmlElements(z.E1, z.E2))
|
||||
.Any(b => b == false);
|
||||
if (different == true)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool CompareElementsWithRuns(XElement element1, XElement element2)
|
||||
{
|
||||
// todo why is hyperlink and fldChar in the following list???
|
||||
var groupedCollection1 = element1
|
||||
.Elements()
|
||||
.Where(e => e.Name != W.commentRangeStart &&
|
||||
e.Name != W.commentRangeEnd &&
|
||||
e.Name != W.rsid &&
|
||||
e.Name != W.proofErr &&
|
||||
e.Name != W.fldChar &&
|
||||
e.Name != W.hyperlink &&
|
||||
e.Name != W.bookmarkStart &&
|
||||
e.Name != W.bookmarkEnd)
|
||||
.GroupAdjacent(e =>
|
||||
{
|
||||
if (e.Name == W.r)
|
||||
return RunElementType.WordRun;
|
||||
if (e.Name == M.r)
|
||||
return RunElementType.MathRun;
|
||||
return RunElementType.Other;
|
||||
});
|
||||
var groupedCollection2 = element2
|
||||
.Elements()
|
||||
.Where(e => e.Name != W.commentRangeStart &&
|
||||
e.Name != W.commentRangeEnd &&
|
||||
e.Name != W.rsid &&
|
||||
e.Name != W.proofErr &&
|
||||
e.Name != W.fldChar &&
|
||||
e.Name != W.hyperlink &&
|
||||
e.Name != W.bookmarkStart &&
|
||||
e.Name != W.bookmarkEnd)
|
||||
.GroupAdjacent(e =>
|
||||
{
|
||||
if (e.Name == W.r)
|
||||
return RunElementType.WordRun;
|
||||
if (e.Name == M.r)
|
||||
return RunElementType.MathRun;
|
||||
return RunElementType.Other;
|
||||
});
|
||||
if (groupedCollection1.Count() != groupedCollection2.Count())
|
||||
{
|
||||
#if false
|
||||
// If this test returns false for a document that you think is erroneous,
|
||||
// the following will give clues to the markup that may need to be ignored, or
|
||||
// the markup that indicates that the documents are different.
|
||||
foreach (var item in groupedCollection1)
|
||||
{
|
||||
Console.WriteLine(item.Key);
|
||||
foreach (var item2 in item)
|
||||
{
|
||||
Console.WriteLine(item2.Name.LocalName);
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
Console.WriteLine("==================");
|
||||
foreach (var item in groupedCollection2)
|
||||
{
|
||||
Console.WriteLine(item.Key);
|
||||
foreach (var item2 in item)
|
||||
{
|
||||
Console.WriteLine(item2.Name.LocalName);
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
Environment.Exit(0);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
bool different = groupedCollection1
|
||||
.Zip(groupedCollection2, (c1, c2) =>
|
||||
{
|
||||
if (c1.Key != c2.Key)
|
||||
return false;
|
||||
switch (c1.Key)
|
||||
{
|
||||
case RunElementType.WordRun:
|
||||
case RunElementType.MathRun:
|
||||
return CompareRunCollections(c1, c2);
|
||||
case RunElementType.Other:
|
||||
return CompareOtherCollections(c1, c2);
|
||||
};
|
||||
throw new DocumentComparerInternalException(
|
||||
"Should not have reached this code");
|
||||
})
|
||||
.Any(b => b == false);
|
||||
if (different == true)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns true if the elements are the same, otherwise false.
|
||||
private static bool CompareOpenXmlElements(XElement element1, XElement element2)
|
||||
{
|
||||
if (element1.Name != element2.Name)
|
||||
return false;
|
||||
if (element1
|
||||
.Elements()
|
||||
.Where(e => e.Name == W.r || e.Name == M.r)
|
||||
.Any())
|
||||
{
|
||||
bool rValue = CompareElementsWithRuns(element1, element2);
|
||||
// todo fix
|
||||
if (rValue == true)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
if (element1.Nodes().OfType<XText>().Any() &&
|
||||
!element1.HasElements)
|
||||
{
|
||||
if (element1.Value == element2.Value)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
if (element1.IsEmpty && element2.IsEmpty)
|
||||
return true;
|
||||
var c1 = element1
|
||||
.Elements()
|
||||
.Where(e => e.Name != W.commentRangeStart &&
|
||||
e.Name != W.commentRangeEnd &&
|
||||
e.Name != W.proofErr &&
|
||||
e.Name != W.rsid);
|
||||
var c2 = element2
|
||||
.Elements()
|
||||
.Where(e => e.Name != W.commentRangeStart &&
|
||||
e.Name != W.commentRangeEnd &&
|
||||
e.Name != W.proofErr &&
|
||||
e.Name != W.rsid);
|
||||
if (c1.Count() != c2.Count())
|
||||
return false;
|
||||
var different = c1
|
||||
.Zip(c2, (e1, e2) => new { E1 = e1, E2 = e2 })
|
||||
.Select(p => CompareOpenXmlElements(p.E1, p.E2))
|
||||
.Any(b => b == false);
|
||||
if (different == true)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
// todo
|
||||
// we want overload for this that takes WmlDocument, or whatever new class is.
|
||||
// also want ability to specify multiple options - a settings object - compare headers, footers, etc.
|
||||
//
|
||||
// Returns true if docs are the same, otherwise false.
|
||||
public static bool CompareDocuments(WordprocessingDocument doc1, WordprocessingDocument doc2)
|
||||
{
|
||||
XDocument doc1XDoc = doc1.MainDocumentPart.GetXDocument();
|
||||
XDocument doc2XDoc = doc2.MainDocumentPart.GetXDocument();
|
||||
if (CompareOpenXmlElements(doc1XDoc.Root, doc2XDoc.Root) == false)
|
||||
return false;
|
||||
|
||||
// for the current use of this class, only need to compare the main document parts.
|
||||
#if false
|
||||
if (doc1.MainDocumentPart.HeaderParts.Count() != doc2.MainDocumentPart.HeaderParts.Count())
|
||||
return false;
|
||||
foreach (var pair in doc1
|
||||
.MainDocumentPart
|
||||
.HeaderParts
|
||||
.Zip(doc2.MainDocumentPart.HeaderParts, (d1, d2) =>
|
||||
new
|
||||
{
|
||||
Doc1Header = d1,
|
||||
Doc2Header = d2,
|
||||
}))
|
||||
{
|
||||
if (CompareOpenXmlElements(pair.Doc1Header.GetXDocument().Root,
|
||||
pair.Doc2Header.GetXDocument().Root) == false)
|
||||
return false;
|
||||
}
|
||||
if (doc1.MainDocumentPart.FooterParts.Count() != doc2.MainDocumentPart.FooterParts.Count())
|
||||
return false;
|
||||
foreach (var pair in doc1
|
||||
.MainDocumentPart
|
||||
.FooterParts
|
||||
.Zip(doc2.MainDocumentPart.FooterParts, (d1, d2) =>
|
||||
new
|
||||
{
|
||||
Doc1Footer = d1,
|
||||
Doc2Footer = d2,
|
||||
}))
|
||||
{
|
||||
if (CompareOpenXmlElements(pair.Doc1Footer.GetXDocument().Root,
|
||||
pair.Doc2Footer.GetXDocument().Root) == false)
|
||||
return false;
|
||||
}
|
||||
if ((doc1.MainDocumentPart.FootnotesPart == null) != (doc2.MainDocumentPart.FootnotesPart == null))
|
||||
return false;
|
||||
if (doc1.MainDocumentPart.FootnotesPart != null)
|
||||
{
|
||||
if (CompareOpenXmlElements(doc1.MainDocumentPart.FootnotesPart.GetXDocument().Root,
|
||||
doc2.MainDocumentPart.FootnotesPart.GetXDocument().Root) == false)
|
||||
return false;
|
||||
}
|
||||
if ((doc1.MainDocumentPart.EndnotesPart == null) != (doc2.MainDocumentPart.EndnotesPart == null))
|
||||
return false;
|
||||
if (doc1.MainDocumentPart.EndnotesPart != null)
|
||||
{
|
||||
if (CompareOpenXmlElements(doc1.MainDocumentPart.EndnotesPart.GetXDocument().Root,
|
||||
doc2.MainDocumentPart.EndnotesPart.GetXDocument().Root) == false)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
218
omegapro/PowerTools/Classes/FooterAccessor.cs
Normal file
218
omegapro/PowerTools/Classes/FooterAccessor.cs
Normal file
@@ -0,0 +1,218 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.Collections.Generic;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
public enum FooterType
|
||||
{
|
||||
Default,
|
||||
First,
|
||||
Even
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to footer operations
|
||||
/// </summary>
|
||||
public class FooterAccessor
|
||||
{
|
||||
private static XNamespace ns;
|
||||
private static XNamespace relationshipns;
|
||||
|
||||
static FooterAccessor()
|
||||
{
|
||||
ns = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
||||
relationshipns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Elements tagged as section properties
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable<XElement> containing all the section properties elements found it in the document</returns>
|
||||
private static IEnumerable<XElement> SectionPropertiesElements(WordprocessingDocument document)
|
||||
{
|
||||
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
|
||||
IEnumerable<XElement> results =
|
||||
mainDocument
|
||||
.Descendants(ns + "p")
|
||||
.Elements(ns + "pPr")
|
||||
.Elements(ns + "sectPr");
|
||||
if (results.Count() == 0)
|
||||
results = mainDocument.Root.Elements(ns + "body").Elements(ns + "sectPr");
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Footer reference nodes inside the document
|
||||
/// </summary>
|
||||
/// <param name="type">The footer part type</param>
|
||||
/// <returns>XElement containing the part reference in the document</returns>
|
||||
private static XElement GetFooterReference(WordprocessingDocument document, FooterType type, int sectionIndex)
|
||||
{
|
||||
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
|
||||
XName footerReferenceTag = ns + "footerReference";
|
||||
XName typeTag = ns + "type";
|
||||
string typeName = "";
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case FooterType.First: typeName = "first";
|
||||
break;
|
||||
case FooterType.Even: typeName = "even";
|
||||
break;
|
||||
case FooterType.Default: typeName = "default";
|
||||
break;
|
||||
}
|
||||
|
||||
XElement sectionPropertyElement = SectionPropertiesElements(document).Skip(sectionIndex).FirstOrDefault();
|
||||
if (sectionPropertyElement != null)
|
||||
{
|
||||
return sectionPropertyElement.Descendants().Where(tag => (tag.Name == footerReferenceTag) && (tag.Attribute(typeTag).Value == typeName)).FirstOrDefault();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the specified footer from the document
|
||||
/// </summary>
|
||||
/// <param name="type">The footer part type</param>
|
||||
/// <returns>the XDocument containing the footer</returns>
|
||||
public static XDocument GetFooter(WmlDocument doc, FooterType type, int sectionIndex)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
OpenXmlPart footer = GetFooterPart(document, type, sectionIndex);
|
||||
if (footer != null)
|
||||
return footer.GetXDocument();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The specified footer part from the document
|
||||
/// </summary>
|
||||
/// <param name="type">The footer part type</param>
|
||||
/// <returns>A OpenXmlPart containing the footer part</returns>
|
||||
public static OpenXmlPart GetFooterPart(WordprocessingDocument document, FooterType type, int sectionIndex)
|
||||
{
|
||||
// look in the section properties of the main document part, the respective footer
|
||||
// needed to extract
|
||||
XElement footerReferenceElement = GetFooterReference(document, type, sectionIndex);
|
||||
if (footerReferenceElement != null)
|
||||
{
|
||||
// get the relation id of the footer part to extract from the document
|
||||
string relationshipId = footerReferenceElement.Attribute(relationshipns + "id").Value;
|
||||
return document.MainDocumentPart.GetPartById(relationshipId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified footer in the document
|
||||
/// </summary>
|
||||
/// <param name="type">The footer part type</param>
|
||||
public static void RemoveFooter(WmlDocument doc, FooterType type, int sectionIndex)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
OpenXmlPart footerPart = GetFooterPart(document, type, sectionIndex);
|
||||
footerPart.RemovePart();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a new footer in a document
|
||||
/// </summary>
|
||||
/// <param name="footer">XDocument containing the footer to add in the document</param>
|
||||
/// <param name="type">The footer part type</param>
|
||||
public static OpenXmlPowerToolsDocument SetFooter(WmlDocument doc, XDocument footer, FooterType type, int sectionIndex)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
// Removes the reference in the document.xml and the footer part if those already
|
||||
// exist
|
||||
XElement footerReferenceElement = GetFooterReference(document, type, sectionIndex);
|
||||
if (footerReferenceElement != null)
|
||||
{
|
||||
GetFooterPart(document, type, sectionIndex).RemovePart();
|
||||
footerReferenceElement.Remove();
|
||||
}
|
||||
|
||||
// Add the new footer
|
||||
FooterPart footerPart = document.MainDocumentPart.AddNewPart<FooterPart>();
|
||||
footerPart.PutXDocument(footer);
|
||||
|
||||
// If the document does not have a property section a new one must be created
|
||||
if (SectionPropertiesElements(document).Count() == 0)
|
||||
{
|
||||
AddDefaultSectionProperties(document);
|
||||
}
|
||||
|
||||
// Creates the relationship of the footer inside the section properties in the document
|
||||
string relID = document.MainDocumentPart.GetIdOfPart(footerPart);
|
||||
string kindName = "";
|
||||
switch ((FooterType)type)
|
||||
{
|
||||
case FooterType.First:
|
||||
kindName = "first";
|
||||
break;
|
||||
case FooterType.Even:
|
||||
kindName = "even";
|
||||
break;
|
||||
case FooterType.Default:
|
||||
kindName = "default";
|
||||
break;
|
||||
}
|
||||
|
||||
XElement sectionPropertyElement = SectionPropertiesElements(document).Skip(sectionIndex).FirstOrDefault();
|
||||
if (sectionPropertyElement != null)
|
||||
{
|
||||
sectionPropertyElement.Add(
|
||||
new XElement(ns + "footerReference",
|
||||
new XAttribute(ns + "type", kindName),
|
||||
new XAttribute(relationshipns + "id", relID)));
|
||||
|
||||
if (sectionPropertyElement.Element(ns + "titlePg") == null)
|
||||
sectionPropertyElement.Add(
|
||||
new XElement(ns + "titlePg")
|
||||
);
|
||||
}
|
||||
document.MainDocumentPart.PutXDocument();
|
||||
|
||||
// add in the settings part the EvendAndOddHeaders. this element
|
||||
// allow to see the odd and even footers and headers in the document.
|
||||
SettingAccessor.AddEvenAndOddHeadersElement(document);
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a default sectPr element into the main document
|
||||
/// </summary>
|
||||
private static void AddDefaultSectionProperties(WordprocessingDocument document)
|
||||
{
|
||||
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
|
||||
mainDocument.Descendants(ns + "body").First().Add(
|
||||
new XElement(ns + "sectPr")
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
278
omegapro/PowerTools/Classes/HeaderAccessor.cs
Normal file
278
omegapro/PowerTools/Classes/HeaderAccessor.cs
Normal file
@@ -0,0 +1,278 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.Collections.Generic;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
public enum HeaderType
|
||||
{
|
||||
Default,
|
||||
First,
|
||||
Even
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to header operations
|
||||
/// </summary>
|
||||
public class HeaderAccessor
|
||||
{
|
||||
private const string defaultHeaderType = "default";
|
||||
private static XNamespace ns;
|
||||
private static XNamespace relationshipns;
|
||||
private static XNamespace officens;
|
||||
private static XNamespace vmlns;
|
||||
private static XNamespace wordns;
|
||||
|
||||
static HeaderAccessor() {
|
||||
ns = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
||||
relationshipns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
||||
officens = "urn:schemas-microsoft-com:office:office";
|
||||
vmlns = "urn:schemas-microsoft-com:vml";
|
||||
wordns = "urn:schemas-microsoft-com:office:word";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Elements tagged as section properties
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable<XElement> containing all the section properties elements found it in the document</returns>
|
||||
private static IEnumerable<XElement> SectionPropertiesElements(WordprocessingDocument document)
|
||||
{
|
||||
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
|
||||
IEnumerable<XElement> results =
|
||||
mainDocument
|
||||
.Descendants(ns + "p")
|
||||
.Elements(ns + "pPr")
|
||||
.Elements(ns + "sectPr");
|
||||
if (results.Count() == 0)
|
||||
results = mainDocument.Root.Elements(ns + "body").Elements(ns + "sectPr");
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new header reference in the section properties
|
||||
/// </summary>
|
||||
/// <param name="type">The header part type</param>
|
||||
/// <param name="headerPartId">the header part id</param>
|
||||
public static void AddHeaderReference(WordprocessingDocument document, HeaderType type, string headerPartId, int sectionIndex)
|
||||
{
|
||||
// If the document does not have a property section a new one must be created
|
||||
if (SectionPropertiesElements(document).Count() == 0)
|
||||
{
|
||||
AddDefaultSectionProperties(document);
|
||||
}
|
||||
|
||||
string typeName = "";
|
||||
switch ((HeaderType)type)
|
||||
{
|
||||
case HeaderType.First:
|
||||
typeName = "first";
|
||||
break;
|
||||
case HeaderType.Even:
|
||||
typeName = "even";
|
||||
break;
|
||||
case HeaderType.Default:
|
||||
typeName = "default";
|
||||
break;
|
||||
}
|
||||
|
||||
XElement sectionPropertyElement = SectionPropertiesElements(document).Skip(sectionIndex).FirstOrDefault();
|
||||
if (sectionPropertyElement != null)
|
||||
{
|
||||
sectionPropertyElement.Add(
|
||||
new XElement(ns + "headerReference",
|
||||
new XAttribute(ns + "type", typeName),
|
||||
new XAttribute(relationshipns + "id", headerPartId)));
|
||||
|
||||
if (sectionPropertyElement.Element(ns + "titlePg") == null)
|
||||
sectionPropertyElement.Add(
|
||||
new XElement(ns + "titlePg")
|
||||
);
|
||||
}
|
||||
document.MainDocumentPart.PutXDocument();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new header part in the document
|
||||
/// </summary>
|
||||
/// <param name="type">The footer part type</param>
|
||||
/// <returns>A XDocument contaning the added header</returns>
|
||||
public static XDocument AddNewHeader(WordprocessingDocument document, HeaderType type)
|
||||
{
|
||||
// Creates the new header part
|
||||
HeaderPart newHeaderPart = document.MainDocumentPart.AddNewPart<HeaderPart>();
|
||||
|
||||
XDocument emptyHeader = CreateEmptyHeaderDocument();
|
||||
newHeaderPart.PutXDocument(emptyHeader);
|
||||
|
||||
string newHeaderPartId = document.MainDocumentPart.GetIdOfPart(newHeaderPart);
|
||||
AddHeaderReference(document, type, newHeaderPartId, 0);
|
||||
|
||||
return emptyHeader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an empty header document
|
||||
/// </summary>
|
||||
/// <returns>A XDocument containing the xml of an empty header part </returns>
|
||||
private static XDocument CreateEmptyHeaderDocument()
|
||||
{
|
||||
return new XDocument(
|
||||
new XElement(ns + "hdr",
|
||||
new XAttribute(XNamespace.Xmlns + "w", ns),
|
||||
new XAttribute(XNamespace.Xmlns + "r", relationshipns),
|
||||
new XAttribute(XNamespace.Xmlns + "o", officens),
|
||||
new XAttribute(XNamespace.Xmlns + "v", vmlns),
|
||||
new XAttribute(XNamespace.Xmlns + "w10", wordns)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Header reference nodes inside the document
|
||||
/// </summary>
|
||||
/// <param name="type">The header part type</param>
|
||||
/// <returns>XElement containing the part reference in the document</returns>
|
||||
public static XElement GetHeaderReference(WordprocessingDocument document, HeaderType type, int sectionIndex)
|
||||
{
|
||||
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
|
||||
XName headerReferenceTag = ns + "headerReference";
|
||||
XName typeTag = ns + "type";
|
||||
string typeName = "";
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case HeaderType.First: typeName = "first";
|
||||
break;
|
||||
case HeaderType.Even: typeName = "even";
|
||||
break;
|
||||
case HeaderType.Default: typeName = "default";
|
||||
break;
|
||||
}
|
||||
|
||||
XElement sectionPropertyElement = SectionPropertiesElements(document).Skip(sectionIndex).FirstOrDefault();
|
||||
if (sectionPropertyElement != null)
|
||||
{
|
||||
return sectionPropertyElement.Descendants().Where(tag => (tag.Name == headerReferenceTag) && (tag.Attribute(typeTag).Value == typeName)).FirstOrDefault();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the specified header from the document
|
||||
/// </summary>
|
||||
/// <param name="type">The header part type</param>
|
||||
/// <returns>A XDocument containing the header</returns>
|
||||
public static XDocument GetHeader(WmlDocument doc, HeaderType type, int sectionIndex)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
OpenXmlPart header = GetHeaderPart(document, type, sectionIndex);
|
||||
if (header != null)
|
||||
return header.GetXDocument();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static XDocument GetHeader(WordprocessingDocument document, HeaderType type, int sectionIndex)
|
||||
{
|
||||
OpenXmlPart header = GetHeaderPart(document, type, sectionIndex);
|
||||
if (header != null)
|
||||
return header.GetXDocument();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The specified header part from the document
|
||||
/// </summary>
|
||||
/// <param name="type">The header part type</param>
|
||||
/// <returns>A OpenXmlPart containing the header part</returns>
|
||||
public static OpenXmlPart GetHeaderPart(WordprocessingDocument document, HeaderType type, int sectionIndex)
|
||||
{
|
||||
// look in the section properties of the main document part, the respective Header
|
||||
// needed to extract
|
||||
XElement headerReferenceElement = GetHeaderReference(document, type, sectionIndex);
|
||||
if (headerReferenceElement != null)
|
||||
{
|
||||
// get the relation id of the Header part to extract from the document
|
||||
string relationId = headerReferenceElement.Attribute(relationshipns + "id").Value;
|
||||
return document.MainDocumentPart.GetPartById(relationId);
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified header in the document
|
||||
/// </summary>
|
||||
/// <param name="type">The header part type</param>
|
||||
public void RemoveHeader(WmlDocument doc, HeaderType type, int sectionIndex)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
OpenXmlPart headerPart = GetHeaderPart(document, type, sectionIndex);
|
||||
headerPart.RemovePart();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a new header in a document
|
||||
/// </summary>
|
||||
/// <param name="header">XDocument containing the header to add in the document</param>
|
||||
/// <param name="type">The header part type</param>
|
||||
public static OpenXmlPowerToolsDocument SetHeader(WmlDocument doc, XDocument header, HeaderType type, int sectionIndex)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
// Removes the reference in the document.xml and the header part if those already
|
||||
// exist
|
||||
XElement headerReferenceElement = GetHeaderReference(document, type, sectionIndex);
|
||||
if (headerReferenceElement != null)
|
||||
{
|
||||
GetHeaderPart(document, type, sectionIndex).RemovePart();
|
||||
headerReferenceElement.Remove();
|
||||
}
|
||||
|
||||
// Add the new header
|
||||
HeaderPart headerPart = document.MainDocumentPart.AddNewPart<HeaderPart>();
|
||||
headerPart.PutXDocument(header);
|
||||
|
||||
// Creates the relationship of the header inside the section properties in the document
|
||||
string relID = document.MainDocumentPart.GetIdOfPart(headerPart);
|
||||
AddHeaderReference(document, type, relID, sectionIndex);
|
||||
|
||||
// add in the settings part the EvendAndOddHeaders. this element
|
||||
// allow to see the odd and even headers and headers in the document.
|
||||
SettingAccessor.AddEvenAndOddHeadersElement(document);
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a default sectPr element into the main document
|
||||
/// </summary>
|
||||
private static void AddDefaultSectionProperties(WordprocessingDocument document)
|
||||
{
|
||||
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
|
||||
mainDocument.Element(ns + "body").Add(
|
||||
new XElement(ns + "sectPr")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
841
omegapro/PowerTools/Classes/HtmlConverter.cs
Normal file
841
omegapro/PowerTools/Classes/HtmlConverter.cs
Normal file
@@ -0,0 +1,841 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2010.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license
|
||||
can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
public partial class WmlDocument : OpenXmlPowerToolsDocument
|
||||
{
|
||||
public XElement ConvertToHtml(HtmlConverterSettings htmlConverterSettings, Func<ImageInfo, XElement> imageHandler)
|
||||
{
|
||||
return HtmlConverter.ConvertToHtml(this, htmlConverterSettings, imageHandler);
|
||||
}
|
||||
public XElement ConvertToHtml(HtmlConverterSettings htmlConverterSettings)
|
||||
{
|
||||
return HtmlConverter.ConvertToHtml(this, htmlConverterSettings);
|
||||
}
|
||||
}
|
||||
|
||||
public class HtmlConverterSettings
|
||||
{
|
||||
public string PageTitle;
|
||||
public string CssClassPrefix;
|
||||
public string Css;
|
||||
public bool ConvertFormatting;
|
||||
}
|
||||
|
||||
public static class Xhtml
|
||||
{
|
||||
public static XNamespace xhtml = "http://www.w3.org/1999/xhtml";
|
||||
public static XName html = xhtml + "html";
|
||||
public static XName head = xhtml + "head";
|
||||
public static XName title = xhtml + "title";
|
||||
public static XName body = xhtml + "body";
|
||||
public static XName p = xhtml + "p";
|
||||
public static XName h1 = xhtml + "h1";
|
||||
public static XName h2 = xhtml + "h2";
|
||||
public static XName A = xhtml + "A";
|
||||
public static XName b = xhtml + "b";
|
||||
public static XName table = xhtml + "table";
|
||||
public static XName tr = xhtml + "tr";
|
||||
public static XName td = xhtml + "td";
|
||||
public static XName meta = xhtml + "meta";
|
||||
public static XName style = xhtml + "style";
|
||||
public static XName br = xhtml + "br";
|
||||
public static XName img = xhtml + "img";
|
||||
public static XName span = xhtml + "span";
|
||||
}
|
||||
|
||||
public static class HtmlNoNamespace
|
||||
{
|
||||
public static XName href = "href";
|
||||
public static XName border = "border";
|
||||
public static XName http_equiv = "http-equiv";
|
||||
public static XName content = "content";
|
||||
public static XName name = "name";
|
||||
public static XName width = "width";
|
||||
public static XName height = "height";
|
||||
public static XName src = "src";
|
||||
public static XName style = "style";
|
||||
public static XName alt = "alt";
|
||||
public static XName id = "id";
|
||||
public static XName descr = "descr";
|
||||
public static XName _class = "class";
|
||||
}
|
||||
|
||||
public class ImageInfo
|
||||
{
|
||||
public Bitmap Bitmap;
|
||||
public XAttribute ImgStyleAttribute;
|
||||
public string ContentType;
|
||||
public XElement DrawingElement;
|
||||
public string AltText;
|
||||
|
||||
public static int EmusPerInch = 914400;
|
||||
public static int EmusPerCm = 360000;
|
||||
}
|
||||
|
||||
public static class HtmlConverter
|
||||
{
|
||||
private static Dictionary<char, string> EntityMap = null;
|
||||
|
||||
public class InvalidSettingsException : Exception
|
||||
{
|
||||
public InvalidSettingsException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
private static XElement ProcessImage(WordprocessingDocument wordDoc,
|
||||
XElement element, Func<ImageInfo, XElement> imageHandler)
|
||||
{
|
||||
if (element.Name == W.drawing)
|
||||
{
|
||||
XElement containerElement = element.Elements()
|
||||
.Where(e => e.Name == WP.inline || e.Name == WP.anchor)
|
||||
.FirstOrDefault();
|
||||
if (containerElement != null)
|
||||
{
|
||||
int? extentCx = (int?)containerElement.Elements(WP.extent)
|
||||
.Attributes(NoNamespace.cx).FirstOrDefault();
|
||||
int? extentCy = (int?)containerElement.Elements(WP.extent)
|
||||
.Attributes(NoNamespace.cy).FirstOrDefault();
|
||||
string altText = (string)containerElement.Elements(WP.docPr)
|
||||
.Attributes(NoNamespace.descr).FirstOrDefault();
|
||||
if (altText == null)
|
||||
altText = (string)containerElement.Elements(WP.docPr)
|
||||
.Attributes(NoNamespace.name).FirstOrDefault();
|
||||
if (altText == null)
|
||||
altText = "";
|
||||
|
||||
XElement blipFill = containerElement.Elements(A.graphic)
|
||||
.Elements(A.graphicData)
|
||||
.Elements(Pic._pic).Elements(Pic.blipFill).FirstOrDefault();
|
||||
if (blipFill != null)
|
||||
{
|
||||
string imageRid = (string)blipFill.Elements(A.blip).Attributes(R.embed)
|
||||
.FirstOrDefault();
|
||||
ImagePart imagePart = (ImagePart)wordDoc.MainDocumentPart
|
||||
.GetPartById(imageRid);
|
||||
string contentType = imagePart.ContentType;
|
||||
if (contentType == "image/png" ||
|
||||
contentType == "image/gif" ||
|
||||
contentType == "image/tiff" ||
|
||||
contentType == "image/jpeg")
|
||||
{
|
||||
using (Stream partStream = imagePart.GetStream())
|
||||
using (Bitmap bitmap = new Bitmap(partStream))
|
||||
{
|
||||
if (extentCx != null && extentCy != null)
|
||||
{
|
||||
ImageInfo imageInfo = new ImageInfo()
|
||||
{
|
||||
Bitmap = bitmap,
|
||||
ImgStyleAttribute = new XAttribute(HtmlNoNamespace.style,
|
||||
string.Format("width: {0}in; height: {1}in",
|
||||
(float)extentCx / (float)ImageInfo.EmusPerInch,
|
||||
(float)extentCy / (float)ImageInfo.EmusPerInch)),
|
||||
ContentType = contentType,
|
||||
DrawingElement = element,
|
||||
AltText = altText,
|
||||
};
|
||||
return imageHandler(imageInfo);
|
||||
}
|
||||
ImageInfo imageInfo2 = new ImageInfo()
|
||||
{
|
||||
Bitmap = bitmap,
|
||||
ContentType = contentType,
|
||||
DrawingElement = element,
|
||||
AltText = altText,
|
||||
};
|
||||
return imageHandler(imageInfo2);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (element.Name == W.pict)
|
||||
{
|
||||
string imageRid = (string)element.Elements(VML.shape)
|
||||
.Elements(VML.imagedata).Attributes(R.id).FirstOrDefault();
|
||||
string style = (string)element.Elements(VML.shape)
|
||||
.Attributes(HtmlNoNamespace.style).FirstOrDefault();
|
||||
if (imageRid != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
ImagePart imagePart = (ImagePart)wordDoc.MainDocumentPart
|
||||
.GetPartById(imageRid);
|
||||
string contentType = imagePart.ContentType;
|
||||
if (contentType == "image/png" ||
|
||||
contentType == "image/gif" ||
|
||||
contentType == "image/tiff" ||
|
||||
contentType == "image/jpeg")
|
||||
{
|
||||
//string style = element.
|
||||
using (Stream partStream = imagePart.GetStream())
|
||||
using (Bitmap bitmap = new Bitmap(partStream))
|
||||
{
|
||||
ImageInfo imageInfo = new ImageInfo()
|
||||
{
|
||||
Bitmap = bitmap,
|
||||
ContentType = contentType,
|
||||
DrawingElement = element,
|
||||
};
|
||||
if (style != null)
|
||||
{
|
||||
float? widthInPoints = null;
|
||||
float? heightInPoints = null;
|
||||
string[] tokens = style.Split(';');
|
||||
var widthString = tokens
|
||||
.Select(t => new
|
||||
{
|
||||
Name = t.Split(':').First(),
|
||||
Value = t.Split(':').Skip(1)
|
||||
.Take(1).FirstOrDefault(),
|
||||
})
|
||||
.Where(p => p.Name == "width")
|
||||
.Select(p => p.Value)
|
||||
.FirstOrDefault();
|
||||
if (widthString != null &&
|
||||
widthString.Substring(widthString.Length - 2) == "pt")
|
||||
{
|
||||
float w;
|
||||
if (float.TryParse(widthString.Substring(0,
|
||||
widthString.Length - 2), out w))
|
||||
widthInPoints = w;
|
||||
}
|
||||
var heightString = tokens
|
||||
.Select(t => new
|
||||
{
|
||||
Name = t.Split(':').First(),
|
||||
Value = t.Split(':').Skip(1).Take(1).FirstOrDefault(),
|
||||
})
|
||||
.Where(p => p.Name == "height")
|
||||
.Select(p => p.Value)
|
||||
.FirstOrDefault();
|
||||
if (heightString != null &&
|
||||
heightString.Substring(heightString.Length - 2) == "pt")
|
||||
{
|
||||
float h;
|
||||
if (float.TryParse(heightString.Substring(0,
|
||||
heightString.Length - 2), out h))
|
||||
heightInPoints = h;
|
||||
}
|
||||
if (widthInPoints != null && heightInPoints != null)
|
||||
imageInfo.ImgStyleAttribute = new XAttribute(
|
||||
HtmlNoNamespace.style, string.Format(
|
||||
"width: {0}pt; height: {1}pt",
|
||||
widthInPoints, heightInPoints));
|
||||
}
|
||||
return imageHandler(imageInfo);
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static object ConvertEntities(string text)
|
||||
{
|
||||
if (text == null)
|
||||
return null;
|
||||
object o = text.ToCharArray()
|
||||
.GroupAdjacent((char c) =>
|
||||
{
|
||||
if (c == 0xf0b7 ||
|
||||
c == 0xf0a7 ||
|
||||
c == 0xf076 ||
|
||||
c == 0xf0d8 ||
|
||||
c == 0xf0a8 ||
|
||||
c == 0xf0fc ||
|
||||
c == 0xf0e0 ||
|
||||
c == 0xf0b2)
|
||||
return "bull";
|
||||
if (c >= 0xf000)
|
||||
return "loz";
|
||||
if (c >= 128)
|
||||
{
|
||||
string entity;
|
||||
if (EntityMap.TryGetValue(c, out entity))
|
||||
return entity;
|
||||
}
|
||||
return "-";
|
||||
})
|
||||
.Select(g =>
|
||||
{
|
||||
if (g.Key != "-")
|
||||
return (object)(g.Select(c => new XEntity(g.Key)));
|
||||
return new XText(g.Aggregate(new StringBuilder(),
|
||||
(s, i) => s.Append(i),
|
||||
s => s.ToString()));
|
||||
});
|
||||
return o;
|
||||
}
|
||||
|
||||
private static object ConvertToHtmlTransform(WordprocessingDocument wordDoc,
|
||||
HtmlConverterSettings settings, XNode node,
|
||||
Func<ImageInfo, XElement> imageHandler)
|
||||
{
|
||||
XElement element = node as XElement;
|
||||
if (element != null)
|
||||
{
|
||||
if (element.Name == W.document)
|
||||
return new XElement(Xhtml.html,
|
||||
new XElement(Xhtml.head,
|
||||
new XElement(Xhtml.meta,
|
||||
new XAttribute(HtmlNoNamespace.http_equiv, "Content-Type"),
|
||||
new XAttribute(HtmlNoNamespace.content,
|
||||
"text/html; charset=windows-1252")),
|
||||
new XElement(Xhtml.meta,
|
||||
new XAttribute(HtmlNoNamespace.name, "Generator"),
|
||||
new XAttribute(HtmlNoNamespace.content,
|
||||
"PowerTools for Open XML")),
|
||||
settings.PageTitle != null ? new XElement(Xhtml.title,
|
||||
settings.PageTitle) : null,
|
||||
settings.Css != null ? new XElement(Xhtml.style,
|
||||
new XComment(Environment.NewLine +
|
||||
settings.Css + Environment.NewLine)) : null
|
||||
),
|
||||
element.Elements().Select(e => ConvertToHtmlTransform(
|
||||
wordDoc, settings, e, imageHandler))
|
||||
);
|
||||
|
||||
// Transform the w:body element to the XHTML h:body element.
|
||||
if (element.Name == W.body)
|
||||
return new XElement(Xhtml.body,
|
||||
element.Elements().Select(e => ConvertToHtmlTransform(
|
||||
wordDoc, settings, e, imageHandler)));
|
||||
|
||||
// Transform every paragraph with a style that has paragraph properties
|
||||
// that has an outline level into the same level of heading. This takes
|
||||
// care of transforming headings of every level.
|
||||
if (element.Name == W.p)
|
||||
{
|
||||
string styleId = (string)element.Elements(W.pPr).Elements(W.pStyle)
|
||||
.Attributes(W.val).FirstOrDefault();
|
||||
XElement style = wordDoc.MainDocumentPart.StyleDefinitionsPart
|
||||
.GetXDocument().Root.Elements(W.style)
|
||||
.Where(s => (string)s.Attribute(W.styleId) == styleId)
|
||||
.FirstOrDefault();
|
||||
if (style != null)
|
||||
{
|
||||
int? outlineLevel = (int?)style.Elements(W.pPr)
|
||||
.Elements(W.outlineLvl).Attributes(W.val).FirstOrDefault();
|
||||
if (outlineLevel != null)
|
||||
{
|
||||
return new XElement(Xhtml.xhtml + string.Format("h{0}",
|
||||
outlineLevel + 1),
|
||||
settings.CssClassPrefix != null ?
|
||||
new XAttribute(HtmlNoNamespace._class,
|
||||
settings.CssClassPrefix + styleId) : null,
|
||||
ConvertEntities(ListItemRetriever.RetrieveListItem(wordDoc,
|
||||
element, null)),
|
||||
element.Elements().Select(e => ConvertToHtmlTransform(wordDoc,
|
||||
settings, e, imageHandler)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transform w:p to h:p.
|
||||
if (element.Name == W.p)
|
||||
{
|
||||
string styleId = (string)element.Elements(W.pPr).Elements(W.pStyle)
|
||||
.Attributes(W.val).FirstOrDefault();
|
||||
if (styleId == null)
|
||||
{
|
||||
styleId = (string)wordDoc.MainDocumentPart.StyleDefinitionsPart
|
||||
.GetXDocument().Root.Elements(W.style)
|
||||
.Where(e => (string)e.Attribute(W.type) == "paragraph" &&
|
||||
(string)e.Attribute(W._default) == "1")
|
||||
.FirstOrDefault().Attributes(W.styleId).FirstOrDefault();
|
||||
}
|
||||
XElement z = new XElement(Xhtml.p,
|
||||
styleId != null ? (
|
||||
settings.CssClassPrefix != null ?
|
||||
new XAttribute(HtmlNoNamespace._class,
|
||||
settings.CssClassPrefix + styleId) : null
|
||||
) : null,
|
||||
ConvertEntities(ListItemRetriever.RetrieveListItem(wordDoc,
|
||||
element, null)),
|
||||
element.Elements().Select(e => ConvertToHtmlTransform(wordDoc,
|
||||
settings, e, imageHandler)));
|
||||
return z;
|
||||
}
|
||||
|
||||
// Transform every hyperlink in the document to the XHTML h:A element.
|
||||
if (element.Name == W.hyperlink && element.Attribute(R.id) != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new XElement(Xhtml.A,
|
||||
new XAttribute(HtmlNoNamespace.href,
|
||||
wordDoc.MainDocumentPart
|
||||
.HyperlinkRelationships
|
||||
.Where(x => x.Id == (string)element.Attribute(R.id))
|
||||
.First()
|
||||
.Uri
|
||||
),
|
||||
ConvertEntities(element.Elements(W.r)
|
||||
.Elements(W.t)
|
||||
.Select(s => (string)s).StringConcatenate())
|
||||
);
|
||||
}
|
||||
catch (UriFormatException)
|
||||
{
|
||||
return element.Elements().Select(e => ConvertToHtmlTransform(wordDoc,
|
||||
settings, e, imageHandler));
|
||||
}
|
||||
}
|
||||
|
||||
// Transform contents of runs that are part of a hyperlink.
|
||||
if (element.Name == W.r &&
|
||||
element.Annotation<FieldInfo>() != null &&
|
||||
element.Annotation<FieldInfo>().Arguments.Length > 0)
|
||||
{
|
||||
FieldInfo fieldInfo = element.Annotation<FieldInfo>();
|
||||
return new XElement(Xhtml.A,
|
||||
new XAttribute(HtmlNoNamespace.href, fieldInfo.Arguments[0]),
|
||||
ConvertEntities(element.Elements(W.t)
|
||||
.Select(s => (string)s).StringConcatenate())
|
||||
);
|
||||
}
|
||||
|
||||
// Transform contents of runs.
|
||||
if (element.Name == W.r)
|
||||
return element.Elements().Select(e => ConvertToHtmlTransform(wordDoc,
|
||||
settings, e, imageHandler));
|
||||
|
||||
// Transform every w:t element to a text node.
|
||||
if (element.Name == W.t)
|
||||
return ConvertEntities(element.Value);
|
||||
|
||||
// Transform w:br to h:br.
|
||||
if (element.Name == W.br || element.Name == W.cr)
|
||||
return new XElement(Xhtml.br);
|
||||
|
||||
// Transform w:noBreakHyphen to '-'
|
||||
if (element.Name == W.noBreakHyphen)
|
||||
return new XText("-");
|
||||
|
||||
// Transform w:tbl to h:tbl.
|
||||
if (element.Name == W.tbl)
|
||||
return new XElement(Xhtml.table,
|
||||
new XAttribute(HtmlNoNamespace.border, 1),
|
||||
element.Elements().Select(e => ConvertToHtmlTransform(wordDoc,
|
||||
settings, e, imageHandler)));
|
||||
|
||||
// Transform w:tr to h:tr.
|
||||
if (element.Name == W.tr)
|
||||
return new XElement(Xhtml.tr,
|
||||
element.Elements().Select(e => ConvertToHtmlTransform(wordDoc,
|
||||
settings, e, imageHandler)));
|
||||
|
||||
// Transform w:tc to h:td.
|
||||
if (element.Name == W.tc)
|
||||
return new XElement(Xhtml.td,
|
||||
element.Elements().Select(e => ConvertToHtmlTransform(wordDoc,
|
||||
settings, e, imageHandler)));
|
||||
|
||||
// Transform images.
|
||||
if (element.Name == W.drawing || element.Name == W.pict)
|
||||
{
|
||||
if (imageHandler == null)
|
||||
return null;
|
||||
return ProcessImage(wordDoc, element, imageHandler);
|
||||
}
|
||||
|
||||
// The following removes any nodes that haven't been transformed.
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private enum AnnotateState
|
||||
{
|
||||
NotInHyperlink,
|
||||
InFirstSection,
|
||||
InSecondSection,
|
||||
}
|
||||
|
||||
private static void AnnotateHyperlinkContent(XElement rootElement)
|
||||
{
|
||||
AnnotateState state = AnnotateState.NotInHyperlink;
|
||||
foreach (XElement blockLevelContentContainer in
|
||||
rootElement.Descendants().Where(e => W.BlockLevelContentContainers.Contains(e.Name)))
|
||||
{
|
||||
FieldInfo fieldInfo = null;
|
||||
foreach (XElement runLevelContent in blockLevelContentContainer
|
||||
.LogicalChildrenContent(W.p).LogicalChildrenContent(W.r))
|
||||
{
|
||||
if (runLevelContent.Elements(W.fldChar).Attributes(W.fldCharType)
|
||||
.Any(a => a.Value == "begin"))
|
||||
state = AnnotateState.InFirstSection;
|
||||
XElement instrText = runLevelContent.Elements(W.instrText).FirstOrDefault();
|
||||
if (instrText != null && state == AnnotateState.InFirstSection)
|
||||
{
|
||||
FieldInfo tempFieldInfo = FieldParser.ParseField(instrText.Value);
|
||||
if (tempFieldInfo.FieldType == "HYPERLINK")
|
||||
fieldInfo = tempFieldInfo;
|
||||
}
|
||||
var z = runLevelContent.Elements(W.fldChar).FirstOrDefault();
|
||||
if (runLevelContent.Elements(W.fldChar).Attributes(W.fldCharType)
|
||||
.Any(a => a.Value == "separate"))
|
||||
state = AnnotateState.InSecondSection;
|
||||
if (runLevelContent.Elements(W.fldChar).Attributes(W.fldCharType)
|
||||
.Any(a => a.Value == "end"))
|
||||
{
|
||||
fieldInfo = null;
|
||||
state = AnnotateState.NotInHyperlink;
|
||||
}
|
||||
if (state == AnnotateState.InSecondSection && fieldInfo != null &&
|
||||
(string)runLevelContent.Elements(W.rPr).Elements(W.rStyle)
|
||||
.Attributes(W.val).FirstOrDefault() == "Hyperlink")
|
||||
runLevelContent.AddAnnotation(fieldInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void InitEntityMap()
|
||||
{
|
||||
EntityMap = new Dictionary<char, string>()
|
||||
{
|
||||
{ (char)160, "nbsp" },
|
||||
{ (char)161, "iexcl" },
|
||||
{ (char)162, "cent" },
|
||||
{ (char)163, "pound" },
|
||||
{ (char)164, "curren" },
|
||||
{ (char)165, "yen" },
|
||||
{ (char)166, "brvbar" },
|
||||
{ (char)167, "sect" },
|
||||
{ (char)168, "uml" },
|
||||
{ (char)169, "copy" },
|
||||
{ (char)170, "ordf" },
|
||||
{ (char)171, "laquo" },
|
||||
{ (char)172, "not" },
|
||||
{ (char)173, "shy" },
|
||||
{ (char)174, "reg" },
|
||||
{ (char)175, "macr" },
|
||||
{ (char)176, "deg" },
|
||||
{ (char)177, "plusmn" },
|
||||
{ (char)178, "sup2" },
|
||||
{ (char)179, "sup3" },
|
||||
{ (char)180, "acute" },
|
||||
{ (char)181, "micro" },
|
||||
{ (char)182, "para" },
|
||||
{ (char)183, "middot" },
|
||||
{ (char)184, "cedil" },
|
||||
{ (char)185, "sup1" },
|
||||
{ (char)186, "ordm" },
|
||||
{ (char)187, "raquo" },
|
||||
{ (char)188, "frac14" },
|
||||
{ (char)189, "frac12" },
|
||||
{ (char)190, "frac34" },
|
||||
{ (char)191, "iquest" },
|
||||
{ (char)192, "Agrave" },
|
||||
{ (char)193, "Aacute" },
|
||||
{ (char)194, "Acirc" },
|
||||
{ (char)195, "Atilde" },
|
||||
{ (char)196, "Auml" },
|
||||
{ (char)197, "Aring" },
|
||||
{ (char)198, "AElig" },
|
||||
{ (char)199, "Ccedil" },
|
||||
{ (char)200, "Egrave" },
|
||||
{ (char)201, "Eacute" },
|
||||
{ (char)202, "Ecirc" },
|
||||
{ (char)203, "Euml" },
|
||||
{ (char)204, "Igrave" },
|
||||
{ (char)205, "Iacute" },
|
||||
{ (char)206, "Icirc" },
|
||||
{ (char)207, "Iuml" },
|
||||
{ (char)208, "ETH" },
|
||||
{ (char)209, "Ntilde" },
|
||||
{ (char)210, "Ograve" },
|
||||
{ (char)211, "Oacute" },
|
||||
{ (char)212, "Ocirc" },
|
||||
{ (char)213, "Otilde" },
|
||||
{ (char)214, "Ouml" },
|
||||
{ (char)215, "times" },
|
||||
{ (char)216, "Oslash" },
|
||||
{ (char)217, "Ugrave" },
|
||||
{ (char)218, "Uacute" },
|
||||
{ (char)219, "Ucirc" },
|
||||
{ (char)220, "Uuml" },
|
||||
{ (char)221, "Yacute" },
|
||||
{ (char)222, "THORN" },
|
||||
{ (char)223, "szlig" },
|
||||
{ (char)224, "agrave" },
|
||||
{ (char)225, "aacute" },
|
||||
{ (char)226, "acirc" },
|
||||
{ (char)227, "atilde" },
|
||||
{ (char)228, "auml" },
|
||||
{ (char)229, "aring" },
|
||||
{ (char)230, "aelig" },
|
||||
{ (char)231, "ccedil" },
|
||||
{ (char)232, "egrave" },
|
||||
{ (char)233, "eacute" },
|
||||
{ (char)234, "ecirc" },
|
||||
{ (char)235, "euml" },
|
||||
{ (char)236, "igrave" },
|
||||
{ (char)237, "iacute" },
|
||||
{ (char)238, "icirc" },
|
||||
{ (char)239, "iuml" },
|
||||
{ (char)240, "eth" },
|
||||
{ (char)241, "ntilde" },
|
||||
{ (char)242, "ograve" },
|
||||
{ (char)243, "oacute" },
|
||||
{ (char)244, "ocirc" },
|
||||
{ (char)245, "otilde" },
|
||||
{ (char)246, "ouml" },
|
||||
{ (char)247, "divide" },
|
||||
{ (char)248, "oslash" },
|
||||
{ (char)249, "ugrave" },
|
||||
{ (char)250, "uacute" },
|
||||
{ (char)251, "ucirc" },
|
||||
{ (char)252, "uuml" },
|
||||
{ (char)253, "yacute" },
|
||||
{ (char)254, "thorn" },
|
||||
{ (char)255, "yuml" },
|
||||
{ (char)338, "OElig" },
|
||||
{ (char)339, "oelig" },
|
||||
{ (char)352, "Scaron" },
|
||||
{ (char)353, "scaron" },
|
||||
{ (char)376, "Yuml" },
|
||||
{ (char)402, "fnof" },
|
||||
{ (char)710, "circ" },
|
||||
{ (char)732, "tilde" },
|
||||
{ (char)913, "Alpha" },
|
||||
{ (char)914, "Beta" },
|
||||
{ (char)915, "Gamma" },
|
||||
{ (char)916, "Delta" },
|
||||
{ (char)917, "Epsilon" },
|
||||
{ (char)918, "Zeta" },
|
||||
{ (char)919, "Eta" },
|
||||
{ (char)920, "Theta" },
|
||||
{ (char)921, "Iota" },
|
||||
{ (char)922, "Kappa" },
|
||||
{ (char)923, "Lambda" },
|
||||
{ (char)924, "Mu" },
|
||||
{ (char)925, "Nu" },
|
||||
{ (char)926, "Xi" },
|
||||
{ (char)927, "Omicron" },
|
||||
{ (char)928, "Pi" },
|
||||
{ (char)929, "Rho" },
|
||||
{ (char)931, "Sigma" },
|
||||
{ (char)932, "Tau" },
|
||||
{ (char)933, "Upsilon" },
|
||||
{ (char)934, "Phi" },
|
||||
{ (char)935, "Chi" },
|
||||
{ (char)936, "Psi" },
|
||||
{ (char)937, "Omega" },
|
||||
{ (char)945, "alpha" },
|
||||
{ (char)946, "beta" },
|
||||
{ (char)947, "gamma" },
|
||||
{ (char)948, "delta" },
|
||||
{ (char)949, "epsilon" },
|
||||
{ (char)950, "zeta" },
|
||||
{ (char)951, "eta" },
|
||||
{ (char)952, "theta" },
|
||||
{ (char)953, "iota" },
|
||||
{ (char)954, "kappa" },
|
||||
{ (char)955, "lambda" },
|
||||
{ (char)956, "mu" },
|
||||
{ (char)957, "nu" },
|
||||
{ (char)958, "xi" },
|
||||
{ (char)959, "omicron" },
|
||||
{ (char)960, "pi" },
|
||||
{ (char)961, "rho" },
|
||||
{ (char)962, "sigmaf" },
|
||||
{ (char)963, "sigma" },
|
||||
{ (char)964, "tau" },
|
||||
{ (char)965, "upsilon" },
|
||||
{ (char)966, "phi" },
|
||||
{ (char)967, "chi" },
|
||||
{ (char)968, "psi" },
|
||||
{ (char)969, "omega" },
|
||||
{ (char)977, "thetasym" },
|
||||
{ (char)978, "upsih" },
|
||||
{ (char)982, "piv" },
|
||||
{ (char)8194, "ensp" },
|
||||
{ (char)8195, "emsp" },
|
||||
{ (char)8201, "thinsp" },
|
||||
{ (char)8204, "zwnj" },
|
||||
{ (char)8205, "zwj" },
|
||||
{ (char)8206, "lrm" },
|
||||
{ (char)8207, "rlm" },
|
||||
{ (char)8211, "ndash" },
|
||||
{ (char)8212, "mdash" },
|
||||
{ (char)8216, "lsquo" },
|
||||
{ (char)8217, "rsquo" },
|
||||
{ (char)8218, "sbquo" },
|
||||
{ (char)8220, "ldquo" },
|
||||
{ (char)8221, "rdquo" },
|
||||
{ (char)8222, "bdquo" },
|
||||
{ (char)8224, "dagger" },
|
||||
{ (char)8225, "Dagger" },
|
||||
{ (char)8226, "bull" },
|
||||
{ (char)8230, "hellip" },
|
||||
{ (char)8240, "permil" },
|
||||
{ (char)8242, "prime" },
|
||||
{ (char)8243, "Prime" },
|
||||
{ (char)8249, "lsaquo" },
|
||||
{ (char)8250, "rsaquo" },
|
||||
{ (char)8254, "oline" },
|
||||
{ (char)8260, "frasl" },
|
||||
{ (char)8364, "euro" },
|
||||
{ (char)8465, "image" },
|
||||
{ (char)8472, "weierp" },
|
||||
{ (char)8476, "real" },
|
||||
{ (char)8482, "trade" },
|
||||
{ (char)8501, "alefsym" },
|
||||
{ (char)8592, "larr" },
|
||||
{ (char)8593, "uarr" },
|
||||
{ (char)8594, "rarr" },
|
||||
{ (char)8595, "darr" },
|
||||
{ (char)8596, "harr" },
|
||||
{ (char)8629, "crarr" },
|
||||
{ (char)8656, "lArr" },
|
||||
{ (char)8657, "uArr" },
|
||||
{ (char)8658, "rArr" },
|
||||
{ (char)8659, "dArr" },
|
||||
{ (char)8660, "hArr" },
|
||||
{ (char)8704, "forall" },
|
||||
{ (char)8706, "part" },
|
||||
{ (char)8707, "exist" },
|
||||
{ (char)8709, "empty" },
|
||||
{ (char)8711, "nabla" },
|
||||
{ (char)8712, "isin" },
|
||||
{ (char)8713, "notin" },
|
||||
{ (char)8715, "ni" },
|
||||
{ (char)8719, "prod" },
|
||||
{ (char)8721, "sum" },
|
||||
{ (char)8722, "minus" },
|
||||
{ (char)8727, "lowast" },
|
||||
{ (char)8730, "radic" },
|
||||
{ (char)8733, "prop" },
|
||||
{ (char)8734, "infin" },
|
||||
{ (char)8736, "ang" },
|
||||
{ (char)8743, "and" },
|
||||
{ (char)8744, "or" },
|
||||
{ (char)8745, "cap" },
|
||||
{ (char)8746, "cup" },
|
||||
{ (char)8747, "int" },
|
||||
{ (char)8756, "there4" },
|
||||
{ (char)8764, "sim" },
|
||||
{ (char)8773, "cong" },
|
||||
{ (char)8776, "asymp" },
|
||||
{ (char)8800, "ne" },
|
||||
{ (char)8801, "equiv" },
|
||||
{ (char)8804, "le" },
|
||||
{ (char)8805, "ge" },
|
||||
{ (char)8834, "sub" },
|
||||
{ (char)8835, "sup" },
|
||||
{ (char)8836, "nsub" },
|
||||
{ (char)8838, "sube" },
|
||||
{ (char)8839, "supe" },
|
||||
{ (char)8853, "oplus" },
|
||||
{ (char)8855, "otimes" },
|
||||
{ (char)8869, "perp" },
|
||||
{ (char)8901, "sdot" },
|
||||
{ (char)8968, "lceil" },
|
||||
{ (char)8969, "rceil" },
|
||||
{ (char)8970, "lfloor" },
|
||||
{ (char)8971, "rfloor" },
|
||||
{ (char)9001, "lang" },
|
||||
{ (char)9002, "rang" },
|
||||
{ (char)9674, "loz" },
|
||||
{ (char)9824, "spades" },
|
||||
{ (char)9827, "clubs" },
|
||||
{ (char)9829, "hearts" },
|
||||
{ (char)9830, "diams" },
|
||||
};
|
||||
}
|
||||
|
||||
public static XElement ConvertToHtml(WordprocessingDocument wordDoc,
|
||||
HtmlConverterSettings htmlConverterSettings)
|
||||
{
|
||||
return ConvertToHtml(wordDoc, htmlConverterSettings, null);
|
||||
}
|
||||
|
||||
public static XElement ConvertToHtml(WmlDocument doc, HtmlConverterSettings htmlConverterSettings)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
return ConvertToHtml(document, htmlConverterSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static XElement ConvertToHtml(WmlDocument doc, HtmlConverterSettings htmlConverterSettings, Func<ImageInfo, XElement> imageHandler)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
return ConvertToHtml(document, htmlConverterSettings, imageHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static XElement ConvertToHtml(WordprocessingDocument wordDoc,
|
||||
HtmlConverterSettings htmlConverterSettings, Func<ImageInfo, XElement> imageHandler)
|
||||
{
|
||||
InitEntityMap();
|
||||
if (htmlConverterSettings.ConvertFormatting)
|
||||
{
|
||||
throw new InvalidSettingsException("Conversion with formatting is not supported");
|
||||
}
|
||||
RevisionAccepter.AcceptRevisions(wordDoc);
|
||||
SimplifyMarkupSettings settings = new SimplifyMarkupSettings
|
||||
{
|
||||
RemoveComments = true,
|
||||
RemoveContentControls = true,
|
||||
RemoveEndAndFootNotes = true,
|
||||
RemoveFieldCodes = false,
|
||||
RemoveLastRenderedPageBreak = true,
|
||||
RemovePermissions = true,
|
||||
RemoveProof = true,
|
||||
RemoveRsidInfo = true,
|
||||
RemoveSmartTags = true,
|
||||
RemoveSoftHyphens = true,
|
||||
ReplaceTabsWithSpaces = true,
|
||||
};
|
||||
MarkupSimplifier.SimplifyMarkup(wordDoc, settings);
|
||||
XElement rootElement = wordDoc.MainDocumentPart.GetXDocument().Root;
|
||||
AnnotateHyperlinkContent(rootElement);
|
||||
XElement xhtml = (XElement)ConvertToHtmlTransform(wordDoc, htmlConverterSettings,
|
||||
rootElement, imageHandler);
|
||||
|
||||
// Note: the xhtml returned by ConvertToHtmlTransform contains objects of type
|
||||
// XEntity. PtOpenXmlUtil.cs define the XEntity class. See
|
||||
// http://blogs.msdn.com/ericwhite/archive/2010/01/21/writing-entity-references-using-linq-to-xml.aspx
|
||||
// for detailed explanation.
|
||||
//
|
||||
// If you further transform the XML tree returned by ConvertToHtmlTransform, you
|
||||
// must do it correctly, or entities will not be serialized properly.
|
||||
|
||||
return xhtml;
|
||||
}
|
||||
}
|
||||
}
|
104
omegapro/PowerTools/Classes/IndexAccessor.cs
Normal file
104
omegapro/PowerTools/Classes/IndexAccessor.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
/// <summary>
|
||||
/// process available index references
|
||||
/// </summary>
|
||||
public class IndexAccessor
|
||||
{
|
||||
private static XNamespace ns;
|
||||
|
||||
static IndexAccessor()
|
||||
{
|
||||
ns = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
||||
}
|
||||
|
||||
public static void Generate(WordprocessingDocument document)
|
||||
{
|
||||
XElement Index = new XElement("Index");
|
||||
|
||||
XElement IndexFirstPart =
|
||||
new XElement(ns + "p",
|
||||
new XElement(ns + "r",
|
||||
new XElement(ns + "fldChar",
|
||||
new XAttribute(ns + "fldCharType", "begin")),
|
||||
new XElement(ns + "instrText",
|
||||
new XAttribute(XNamespace.Xml + "space", "preserve"),
|
||||
@" INDEX \h ""A"" \c ""2"" \z ""1033"" "),
|
||||
new XElement(ns + "fldChar",
|
||||
new XAttribute(ns + "fldCharType", "separate"))));
|
||||
|
||||
Index.Add(IndexFirstPart);
|
||||
|
||||
// Build the index with the IndexReferences
|
||||
foreach (XElement reference in IndexReferences(document))
|
||||
{
|
||||
//string fieldCode = GetFieldCode(reference);
|
||||
string mainEntry = reference.Value;//GetIndexMainEntry(reference.Value);
|
||||
|
||||
// Build the XElement containing the index reference
|
||||
XElement IndexElement =
|
||||
new XElement(ns + "p",
|
||||
new XElement(ns + "pPr",
|
||||
new XElement(ns + "pStyle",
|
||||
new XAttribute(ns + "val", "Index1")),
|
||||
new XElement(ns + "tabs",
|
||||
new XElement(ns + "tab",
|
||||
new XAttribute(ns + "val", "right"),
|
||||
new XAttribute(ns + "leader", "dot"),
|
||||
new XAttribute(ns + "pos", "9350")))),
|
||||
new XElement(ns + "r",
|
||||
new XElement(ns + "t", mainEntry)));
|
||||
|
||||
Index.Add(IndexElement);
|
||||
}
|
||||
|
||||
// Close the open character field
|
||||
Index.Add(
|
||||
new XElement(ns + "p",
|
||||
new XElement(ns + "r",
|
||||
new XElement(ns + "fldChar",
|
||||
new XAttribute(ns + "fldCharType", "end")))));
|
||||
|
||||
XDocument mainDocumentPart = document.MainDocumentPart.GetXDocument();
|
||||
foreach (XElement IndexElement in Index.Elements())
|
||||
{
|
||||
mainDocumentPart.Descendants(ns + "body").First().Add(IndexElement);
|
||||
}
|
||||
document.MainDocumentPart.PutXDocument();
|
||||
}
|
||||
|
||||
private static IEnumerable<XElement> IndexReferences(WordprocessingDocument document)
|
||||
{
|
||||
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
|
||||
IEnumerable<XElement> results =
|
||||
mainDocument
|
||||
.Descendants(ns + "p")
|
||||
.Elements(ns + "r")
|
||||
.Where(
|
||||
r =>
|
||||
r.Elements(ns + "instrText").Count() > 0 &&
|
||||
r.ElementsBeforeSelf().Last().Element(ns + "instrText")!= null &&
|
||||
r.ElementsBeforeSelf().Last().Element(ns + "instrText").Value.EndsWith("\"") &&
|
||||
r.ElementsAfterSelf().First().Element(ns + "instrText") != null &&
|
||||
r.ElementsAfterSelf().First().Element(ns + "instrText").Value.StartsWith("\"")
|
||||
);
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
633
omegapro/PowerTools/Classes/ListItemRetriever.cs
Normal file
633
omegapro/PowerTools/Classes/ListItemRetriever.cs
Normal file
@@ -0,0 +1,633 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2010.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license
|
||||
can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
public partial class WmlDocument : OpenXmlPowerToolsDocument
|
||||
{
|
||||
public string RetrieveListItem(XElement paragraph, string bulletReplacementString)
|
||||
{
|
||||
return ListItemRetriever.RetrieveListItem(this, paragraph, bulletReplacementString);
|
||||
}
|
||||
}
|
||||
|
||||
public class ListItemRetriever
|
||||
{
|
||||
private class ListItemInfo
|
||||
{
|
||||
public bool IsListItem;
|
||||
public XElement Lvl;
|
||||
public int? Start;
|
||||
public int? AbstractNumId;
|
||||
public ListItemInfo(bool isListItem)
|
||||
{
|
||||
IsListItem = isListItem;
|
||||
}
|
||||
}
|
||||
|
||||
private class LevelNumbers
|
||||
{
|
||||
public int[] LevelNumbersArray;
|
||||
}
|
||||
|
||||
private static ListItemInfo GetListItemInfoByNumIdAndIlvl(XDocument numbering,
|
||||
XDocument styles, int numId, int ilvl)
|
||||
{
|
||||
if (numId == 0)
|
||||
return new ListItemInfo(false);
|
||||
ListItemInfo listItemInfo = new ListItemInfo(true);
|
||||
XElement num = numbering.Root.Elements(W.num)
|
||||
.Where(e => (int)e.Attribute(W.numId) == numId).FirstOrDefault();
|
||||
if (num == null)
|
||||
return new ListItemInfo(false);
|
||||
listItemInfo.AbstractNumId = (int?)num.Elements(W.abstractNumId)
|
||||
.Attributes(W.val).FirstOrDefault();
|
||||
XElement lvlOverride = num.Elements(W.lvlOverride)
|
||||
.Where(e => (int)e.Attribute(W.ilvl) == ilvl).FirstOrDefault();
|
||||
// If there is a w:lvlOverride element, and if the w:lvlOverride contains a
|
||||
// w:lvl element, then return it. Otherwise, go look in the abstract numbering
|
||||
// definition.
|
||||
if (lvlOverride != null)
|
||||
{
|
||||
// Get the startOverride, if there is one.
|
||||
listItemInfo.Start = (int?)num.Elements(W.lvlOverride)
|
||||
.Where(o => (int)o.Attribute(W.ilvl) == ilvl).Elements(W.startOverride)
|
||||
.Attributes(W.val).FirstOrDefault();
|
||||
listItemInfo.Lvl = lvlOverride.Element(W.lvl);
|
||||
if (listItemInfo.Lvl != null)
|
||||
{
|
||||
if (listItemInfo.Start == null)
|
||||
listItemInfo.Start = (int?)listItemInfo.Lvl.Elements(W.start)
|
||||
.Attributes(W.val).FirstOrDefault();
|
||||
return listItemInfo;
|
||||
}
|
||||
}
|
||||
int? a = listItemInfo.AbstractNumId;
|
||||
XElement abstractNum = numbering.Root.Elements(W.abstractNum)
|
||||
.Where(e => (int)e.Attribute(W.abstractNumId) == a).FirstOrDefault();
|
||||
string numStyleLink = (string)abstractNum.Elements(W.numStyleLink)
|
||||
.Attributes(W.val).FirstOrDefault();
|
||||
if (numStyleLink != null)
|
||||
{
|
||||
XElement style = styles.Root.Elements(W.style)
|
||||
.Where(e => (string)e.Attribute(W.styleId) == numStyleLink)
|
||||
.FirstOrDefault();
|
||||
XElement numPr = style.Elements(W.pPr).Elements(W.numPr).FirstOrDefault();
|
||||
int lNumId = (int)numPr.Elements(W.numId).Attributes(W.val)
|
||||
.FirstOrDefault();
|
||||
return GetListItemInfoByNumIdAndIlvl(numbering, styles, lNumId, ilvl);
|
||||
}
|
||||
for (int l = ilvl; l >= 0; --l)
|
||||
{
|
||||
listItemInfo.Lvl = abstractNum.Elements(W.lvl)
|
||||
.Where(e => (int)e.Attribute(W.ilvl) == l).FirstOrDefault();
|
||||
if (listItemInfo.Lvl == null)
|
||||
continue;
|
||||
if (listItemInfo.Start == null)
|
||||
listItemInfo.Start = (int?)listItemInfo.Lvl.Elements(W.start)
|
||||
.Attributes(W.val).FirstOrDefault();
|
||||
return listItemInfo;
|
||||
}
|
||||
return new ListItemInfo(false);
|
||||
}
|
||||
|
||||
private static ListItemInfo GetListItemInfoByNumIdAndStyleId(XDocument numbering,
|
||||
XDocument styles, int numId, string paragraphStyle)
|
||||
{
|
||||
// If you have to find the w:lvl by style id, then we can't find it in the
|
||||
// w:lvlOverride, as that requires that you have determined the level already.
|
||||
ListItemInfo listItemInfo = new ListItemInfo(true);
|
||||
XElement num = numbering.Root.Elements(W.num)
|
||||
.Where(e => (int)e.Attribute(W.numId) == numId).FirstOrDefault();
|
||||
listItemInfo.AbstractNumId = (int)num.Elements(W.abstractNumId)
|
||||
.Attributes(W.val).FirstOrDefault();
|
||||
int? a = listItemInfo.AbstractNumId;
|
||||
XElement abstractNum = numbering.Root.Elements(W.abstractNum)
|
||||
.Where(e => (int)e.Attribute(W.abstractNumId) == a).FirstOrDefault();
|
||||
string numStyleLink = (string)abstractNum.Element(W.numStyleLink)
|
||||
.Attributes(W.val).FirstOrDefault();
|
||||
if (numStyleLink != null)
|
||||
{
|
||||
XElement style = styles.Root.Elements(W.style)
|
||||
.Where(e => (string)e.Attribute(W.styleId) == numStyleLink)
|
||||
.FirstOrDefault();
|
||||
XElement numPr = style.Elements(W.pPr).Elements(W.numPr).FirstOrDefault();
|
||||
int lNumId = (int)numPr.Elements(W.numId).Attributes(W.val).FirstOrDefault();
|
||||
return GetListItemInfoByNumIdAndStyleId(numbering, styles, lNumId,
|
||||
paragraphStyle);
|
||||
}
|
||||
listItemInfo.Lvl = abstractNum.Elements(W.lvl)
|
||||
.Where(e => (string)e.Element(W.pStyle) == paragraphStyle).FirstOrDefault();
|
||||
listItemInfo.Start = (int?)listItemInfo.Lvl.Elements(W.start).Attributes(W.val)
|
||||
.FirstOrDefault();
|
||||
return listItemInfo;
|
||||
}
|
||||
|
||||
private static ListItemInfo GetListItemInfo(XDocument numbering, XDocument styles,
|
||||
XElement paragraph)
|
||||
{
|
||||
// The following is an optimization - only determine ListItemInfo once for a
|
||||
// paragraph.
|
||||
ListItemInfo listItemInfo = paragraph.Annotation<ListItemInfo>();
|
||||
if (listItemInfo != null)
|
||||
return listItemInfo;
|
||||
XElement paragraphNumberingProperties = paragraph.Elements(W.pPr)
|
||||
.Elements(W.numPr).FirstOrDefault();
|
||||
string paragraphStyle = (string)paragraph.Elements(W.pPr).Elements(W.pStyle)
|
||||
.Attributes(W.val).FirstOrDefault();
|
||||
if (paragraphNumberingProperties != null &&
|
||||
paragraphNumberingProperties.Element(W.numId) != null)
|
||||
{
|
||||
// Paragraph numbering properties must contain a numId.
|
||||
int numId = (int)paragraphNumberingProperties.Elements(W.numId)
|
||||
.Attributes(W.val).FirstOrDefault();
|
||||
int? ilvl = (int?)paragraphNumberingProperties.Elements(W.ilvl)
|
||||
.Attributes(W.val).FirstOrDefault();
|
||||
if (ilvl != null)
|
||||
{
|
||||
listItemInfo = GetListItemInfoByNumIdAndIlvl(numbering, styles, numId,
|
||||
(int)ilvl);
|
||||
paragraph.AddAnnotation(listItemInfo);
|
||||
return listItemInfo;
|
||||
}
|
||||
if (paragraphStyle != null)
|
||||
{
|
||||
listItemInfo = GetListItemInfoByNumIdAndStyleId(numbering, styles,
|
||||
numId, paragraphStyle);
|
||||
paragraph.AddAnnotation(listItemInfo);
|
||||
return listItemInfo;
|
||||
}
|
||||
listItemInfo = new ListItemInfo(false);
|
||||
paragraph.AddAnnotation(listItemInfo);
|
||||
return listItemInfo;
|
||||
}
|
||||
if (paragraphStyle != null)
|
||||
{
|
||||
XElement style = styles.Root.Elements(W.style).Where(s =>
|
||||
(string)s.Attribute(W.type) == "paragraph" &&
|
||||
(string)s.Attribute(W.styleId) == paragraphStyle).FirstOrDefault();
|
||||
if (style != null)
|
||||
{
|
||||
XElement styleNumberingProperties = style.Elements(W.pPr)
|
||||
.Elements(W.numPr).FirstOrDefault();
|
||||
if (styleNumberingProperties != null &&
|
||||
styleNumberingProperties.Element(W.numId) != null)
|
||||
{
|
||||
int numId = (int)styleNumberingProperties.Elements(W.numId)
|
||||
.Attributes(W.val).FirstOrDefault();
|
||||
int? ilvl = (int?)styleNumberingProperties.Elements(W.ilvl)
|
||||
.Attributes(W.val).FirstOrDefault();
|
||||
if (ilvl == null)
|
||||
ilvl = 0;
|
||||
listItemInfo = GetListItemInfoByNumIdAndIlvl(numbering, styles,
|
||||
numId, (int)ilvl);
|
||||
paragraph.AddAnnotation(listItemInfo);
|
||||
return listItemInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
listItemInfo = new ListItemInfo(false);
|
||||
paragraph.AddAnnotation(listItemInfo);
|
||||
return listItemInfo;
|
||||
}
|
||||
|
||||
private static IEnumerable<LevelNumbers> ParagraphsToConsiderWhenCounting(
|
||||
XDocument numbering, XDocument styles, XElement paragraph, int levelNumber)
|
||||
{
|
||||
ListItemInfo listItemInfo = GetListItemInfo(numbering, styles, paragraph);
|
||||
int? lvlRestart = (int?)listItemInfo.Lvl.Elements(W.lvlRestart)
|
||||
.Attributes(W.val).FirstOrDefault();
|
||||
int paragraphLevel = (int)listItemInfo.Lvl.Attribute(W.ilvl);
|
||||
IEnumerable<XElement> paragraphsBeforeSelf = paragraph
|
||||
.ElementsBeforeSelfReverseDocumentOrder().Where(e => e.Name == W.p);
|
||||
foreach (var p in paragraphsBeforeSelf)
|
||||
{
|
||||
ListItemInfo pListItemInfo = GetListItemInfo(numbering, styles, p);
|
||||
if (!pListItemInfo.IsListItem ||
|
||||
pListItemInfo.AbstractNumId != listItemInfo.AbstractNumId)
|
||||
continue;
|
||||
LevelNumbers pLevelNumbers = p.Annotation<LevelNumbers>();
|
||||
int pLevel = (int)pListItemInfo.Lvl.Attribute(W.ilvl);
|
||||
if (pLevel > levelNumber)
|
||||
yield return pLevelNumbers;
|
||||
if (lvlRestart == null)
|
||||
{
|
||||
if (pLevel < levelNumber)
|
||||
yield break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pLevel < levelNumber && pLevel > lvlRestart - 1)
|
||||
continue;
|
||||
else if (pLevel < levelNumber)
|
||||
yield break;
|
||||
}
|
||||
yield return pLevelNumbers;
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetLevelNumberForLevel(XDocument numbering, XDocument styles,
|
||||
XElement paragraph, int level)
|
||||
{
|
||||
ListItemInfo listItemInfo = GetListItemInfo(numbering, styles, paragraph);
|
||||
int paragraphLevel = (int)listItemInfo.Lvl.Attribute(W.ilvl);
|
||||
var paragraphsToConsider = ParagraphsToConsiderWhenCounting(numbering, styles,
|
||||
paragraph, level)
|
||||
.Select(o => o.LevelNumbersArray.Take(paragraphLevel + 1)
|
||||
.Select(z => z.ToString() + ".").StringConcatenate())
|
||||
.GroupBy(o => o);
|
||||
int levelNumberForLevel = paragraphsToConsider.Count();
|
||||
return levelNumberForLevel;
|
||||
}
|
||||
|
||||
private static int[] GetLevelNumbers(XDocument numbering, XDocument styles,
|
||||
XElement paragraph)
|
||||
{
|
||||
IEnumerable<XElement> paragraphsBeforeSelf = paragraph
|
||||
.ElementsBeforeSelfReverseDocumentOrder().Where(e => e.Name == W.p);
|
||||
int level;
|
||||
ListItemInfo listItemInfo = GetListItemInfo(numbering, styles, paragraph);
|
||||
level = (int)listItemInfo.Lvl.Attribute(W.ilvl);
|
||||
List<int> levelNumbers = new List<int>();
|
||||
for (int indentationLevel = 0; indentationLevel <= level; ++indentationLevel)
|
||||
{
|
||||
XElement currentIndentLvl = GetRelatedLevel(listItemInfo.Lvl,
|
||||
indentationLevel);
|
||||
int? start = (int?)currentIndentLvl.Elements(W.start).Attributes(W.val)
|
||||
.FirstOrDefault();
|
||||
if (start == null)
|
||||
start = 1;
|
||||
XElement paragraphWithSameAbstractNumId = paragraphsBeforeSelf
|
||||
.FirstOrDefault(p =>
|
||||
{
|
||||
ListItemInfo pListItemInfo = GetListItemInfo(numbering, styles, p);
|
||||
return pListItemInfo.IsListItem &&
|
||||
pListItemInfo.AbstractNumId == listItemInfo.AbstractNumId;
|
||||
});
|
||||
if (paragraphWithSameAbstractNumId != null)
|
||||
{
|
||||
LevelNumbers pLevelNumbers = paragraphWithSameAbstractNumId
|
||||
.Annotation<LevelNumbers>();
|
||||
if (pLevelNumbers.LevelNumbersArray.Length > indentationLevel)
|
||||
{
|
||||
if (indentationLevel == level)
|
||||
levelNumbers.Add(
|
||||
pLevelNumbers.LevelNumbersArray[indentationLevel] + 1);
|
||||
else
|
||||
levelNumbers.Add(pLevelNumbers
|
||||
.LevelNumbersArray[indentationLevel]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (level == indentationLevel)
|
||||
{
|
||||
int c1 = GetLevelNumberForLevel(numbering, styles, paragraph,
|
||||
indentationLevel);
|
||||
int? start2 = listItemInfo.Start;
|
||||
if (start2 == null)
|
||||
start2 = 1;
|
||||
levelNumbers.Add(c1 + (int)start2);
|
||||
continue;
|
||||
}
|
||||
levelNumbers.Add((int)start);
|
||||
}
|
||||
return levelNumbers.ToArray();
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetFormatTokens(string lvlText)
|
||||
{
|
||||
int i = 0;
|
||||
while (true)
|
||||
{
|
||||
if (i >= lvlText.Length)
|
||||
yield break;
|
||||
if (lvlText[i] == '%' && i <= lvlText.Length - 2)
|
||||
{
|
||||
yield return lvlText.Substring(i, 2);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
int percentIndex = lvlText.IndexOf('%', i);
|
||||
if (percentIndex == -1 || percentIndex > lvlText.Length - 2)
|
||||
{
|
||||
yield return lvlText.Substring(i);
|
||||
yield break;
|
||||
}
|
||||
yield return lvlText.Substring(i, percentIndex - i);
|
||||
yield return lvlText.Substring(percentIndex, 2);
|
||||
i = percentIndex + 2;
|
||||
}
|
||||
}
|
||||
|
||||
private static string[] RomanOnes =
|
||||
{
|
||||
"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"
|
||||
};
|
||||
|
||||
private static string[] RomanTens =
|
||||
{
|
||||
"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"
|
||||
};
|
||||
|
||||
private static string[] RomanHundreds =
|
||||
{
|
||||
"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", "M"
|
||||
};
|
||||
|
||||
private static string[] RomanThousands =
|
||||
{
|
||||
"", "M", "MM", "MMM", "MMMM", "MMMMM", "MMMMMM", "MMMMMMM", "MMMMMMMM",
|
||||
"MMMMMMMMM", "MMMMMMMMMM"
|
||||
};
|
||||
|
||||
private static string[] OneThroughNineteen = {
|
||||
"one", "two", "three", "four", "five", "six", "seven", "eight",
|
||||
"nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
|
||||
"fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
|
||||
};
|
||||
|
||||
private static string[] Tens = {
|
||||
"ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy",
|
||||
"eighty", "ninety"
|
||||
};
|
||||
|
||||
private static string[] OrdinalOneThroughNineteen = {
|
||||
"first", "second", "third", "fourth", "fifth", "sixth",
|
||||
"seventh", "eighth", "ninth", "tenth", "eleventh", "twelfth",
|
||||
"thirteenth", "fourteenth", "fifteenth", "sixteenth",
|
||||
"seventeenth", "eighteenth", "nineteenth"
|
||||
};
|
||||
|
||||
private static string[] OrdinalTenths = {
|
||||
"tenth", "twentieth", "thirtieth", "fortieth", "fiftieth",
|
||||
"sixtieth", "seventieth", "eightieth", "ninetieth"
|
||||
};
|
||||
|
||||
private static string GetLevelText(int levelNumber, string numFmt)
|
||||
{
|
||||
if (numFmt == "decimal")
|
||||
{
|
||||
return levelNumber.ToString();
|
||||
}
|
||||
if (numFmt == "decimalZero")
|
||||
{
|
||||
if (levelNumber <= 9)
|
||||
return "0" + levelNumber.ToString();
|
||||
else
|
||||
return levelNumber.ToString();
|
||||
}
|
||||
if (numFmt == "upperRoman")
|
||||
{
|
||||
int ones = levelNumber % 10;
|
||||
int tens = (levelNumber % 100) / 10;
|
||||
int hundreds = (levelNumber % 1000) / 100;
|
||||
int thousands = levelNumber / 1000;
|
||||
return RomanThousands[thousands] + RomanHundreds[hundreds] +
|
||||
RomanTens[tens] + RomanOnes[ones];
|
||||
}
|
||||
if (numFmt == "lowerRoman")
|
||||
{
|
||||
int ones = levelNumber % 10;
|
||||
int tens = (levelNumber % 100) / 10;
|
||||
int hundreds = (levelNumber % 1000) / 100;
|
||||
int thousands = levelNumber / 1000;
|
||||
return (RomanThousands[thousands] + RomanHundreds[hundreds] +
|
||||
RomanTens[tens] + RomanOnes[ones]).ToLower();
|
||||
}
|
||||
if (numFmt == "upperLetter")
|
||||
{
|
||||
string a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
int c = (levelNumber - 1) / 26;
|
||||
int n = (levelNumber - 1) % 26;
|
||||
char x = a[n];
|
||||
return "".PadRight(c + 1, x);
|
||||
}
|
||||
if (numFmt == "lowerLetter")
|
||||
{
|
||||
string a = "abcdefghijklmnopqrstuvwxyz";
|
||||
int c = (levelNumber - 1) / 26;
|
||||
int n = (levelNumber - 1) % 26;
|
||||
char x = a[n];
|
||||
return "".PadRight(c + 1, x);
|
||||
}
|
||||
if (numFmt == "ordinal")
|
||||
{
|
||||
string suffix;
|
||||
if (levelNumber % 100 == 11 || levelNumber % 100 == 12 ||
|
||||
levelNumber % 100 == 13)
|
||||
suffix = "th";
|
||||
else if (levelNumber % 10 == 1)
|
||||
suffix = "st";
|
||||
else if (levelNumber % 10 == 2)
|
||||
suffix = "nd";
|
||||
else if (levelNumber % 10 == 3)
|
||||
suffix = "rd";
|
||||
else
|
||||
suffix = "th";
|
||||
return levelNumber.ToString() + suffix;
|
||||
}
|
||||
if (numFmt == "cardinalText")
|
||||
{
|
||||
string result = "";
|
||||
int t1 = levelNumber / 1000;
|
||||
int t2 = levelNumber % 1000;
|
||||
if (t1 >= 1)
|
||||
result += OneThroughNineteen[t1 - 1] + " thousand";
|
||||
if (t1 >= 1 && t2 == 0)
|
||||
return result.Substring(0, 1).ToUpper() +
|
||||
result.Substring(1);
|
||||
if (t1 >= 1)
|
||||
result += " ";
|
||||
int h1 = (levelNumber % 1000) / 100;
|
||||
int h2 = levelNumber % 100;
|
||||
if (h1 >= 1)
|
||||
result += OneThroughNineteen[h1 - 1] + " hundred";
|
||||
if (h1 >= 1 && h2 == 0)
|
||||
return result.Substring(0, 1).ToUpper() +
|
||||
result.Substring(1);
|
||||
if (h1 >= 1)
|
||||
result += " ";
|
||||
int z = levelNumber % 100;
|
||||
if (z <= 19)
|
||||
result += OneThroughNineteen[z - 1];
|
||||
else
|
||||
{
|
||||
int x = z / 10;
|
||||
int r = z % 10;
|
||||
result += Tens[x - 1];
|
||||
if (r >= 1)
|
||||
result += "-" + OneThroughNineteen[r - 1];
|
||||
}
|
||||
return result.Substring(0, 1).ToUpper() +
|
||||
result.Substring(1);
|
||||
}
|
||||
if (numFmt == "ordinalText")
|
||||
{
|
||||
string result = "";
|
||||
int t1 = levelNumber / 1000;
|
||||
int t2 = levelNumber % 1000;
|
||||
if (t1 >= 1 && t2 != 0)
|
||||
result += OneThroughNineteen[t1 - 1] + " thousand";
|
||||
if (t1 >= 1 && t2 == 0)
|
||||
{
|
||||
result += OneThroughNineteen[t1 - 1] + " thousandth";
|
||||
return result.Substring(0, 1).ToUpper() +
|
||||
result.Substring(1);
|
||||
}
|
||||
if (t1 >= 1)
|
||||
result += " ";
|
||||
int h1 = (levelNumber % 1000) / 100;
|
||||
int h2 = levelNumber % 100;
|
||||
if (h1 >= 1 && h2 != 0)
|
||||
result += OneThroughNineteen[h1 - 1] + " hundred";
|
||||
if (h1 >= 1 && h2 == 0)
|
||||
{
|
||||
result += OneThroughNineteen[h1 - 1] + " hundredth";
|
||||
return result.Substring(0, 1).ToUpper() +
|
||||
result.Substring(1);
|
||||
}
|
||||
if (h1 >= 1)
|
||||
result += " ";
|
||||
int z = levelNumber % 100;
|
||||
if (z <= 19)
|
||||
result += OrdinalOneThroughNineteen[z - 1];
|
||||
else
|
||||
{
|
||||
int x = z / 10;
|
||||
int r = z % 10;
|
||||
if (r == 0)
|
||||
result += OrdinalTenths[x - 1];
|
||||
else
|
||||
result += Tens[x - 1];
|
||||
if (r >= 1)
|
||||
result += "-" + OrdinalOneThroughNineteen[r - 1];
|
||||
}
|
||||
return result.Substring(0, 1).ToUpper() +
|
||||
result.Substring(1);
|
||||
}
|
||||
if (numFmt == "bullet")
|
||||
return "";
|
||||
// This method needs to be enhanced to support all languages and
|
||||
// all number formats.
|
||||
return levelNumber.ToString();
|
||||
}
|
||||
|
||||
private static XElement GetRelatedLevel(XElement lvl, int level)
|
||||
{
|
||||
XElement parent = lvl.Parent;
|
||||
XElement newLvl;
|
||||
if (parent.Name == W.lvlOverride)
|
||||
{
|
||||
newLvl = parent.Parent.Elements(W.lvlOverride).Elements(W.lvl)
|
||||
.Where(e => (int)e.Attribute(W.ilvl) == level).FirstOrDefault();
|
||||
if (newLvl != null)
|
||||
return newLvl;
|
||||
int abstractNumId = (int)parent.Parent.Elements(W.abstractNumId)
|
||||
.Attributes(W.val).FirstOrDefault();
|
||||
XElement abstractNum = lvl.Ancestors().Last().Elements(W.abstractNum)
|
||||
.Where(e => (int)e.Attribute(W.abstractNumId) == abstractNumId)
|
||||
.FirstOrDefault();
|
||||
newLvl = abstractNum.Elements(W.lvl)
|
||||
.Where(e => (int)e.Attribute(W.ilvl) == level).FirstOrDefault();
|
||||
return newLvl;
|
||||
}
|
||||
newLvl = parent.Elements(W.lvl).Where(e => (int)e.Attribute(W.ilvl) == level)
|
||||
.FirstOrDefault();
|
||||
return newLvl;
|
||||
}
|
||||
|
||||
private static string FormatListItem(XElement lvl, int[] levelNumbers,
|
||||
string lvlText, string bulletReplacementString)
|
||||
{
|
||||
if (bulletReplacementString != null)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(lvlText);
|
||||
sb.Replace("\xF076", bulletReplacementString);
|
||||
sb.Replace("\xF0D8", bulletReplacementString);
|
||||
sb.Replace("\xF0A7", bulletReplacementString);
|
||||
sb.Replace("\xF0B7", bulletReplacementString);
|
||||
sb.Replace("\xF0A8", bulletReplacementString);
|
||||
sb.Replace("\xF0FC", bulletReplacementString);
|
||||
lvlText = sb.ToString();
|
||||
}
|
||||
string[] formatTokens = GetFormatTokens(lvlText).ToArray();
|
||||
bool isLgl = lvl.Elements(W.isLgl).Any();
|
||||
string listItem = formatTokens.Select((t, l) =>
|
||||
{
|
||||
if (t.Substring(0, 1) != "%")
|
||||
return t;
|
||||
int indentationLevel = Int32.Parse(t.Substring(1)) - 1;
|
||||
int levelNumber = levelNumbers[indentationLevel];
|
||||
string levelText;
|
||||
XElement rlvl = GetRelatedLevel(lvl, indentationLevel);
|
||||
string numFmtForLevel = (string)rlvl.Elements(W.numFmt).Attributes(W.val)
|
||||
.FirstOrDefault();
|
||||
if (isLgl && numFmtForLevel != "decimalZero")
|
||||
numFmtForLevel = "decimal";
|
||||
levelText = GetLevelText(levelNumber, numFmtForLevel);
|
||||
return levelText;
|
||||
}).StringConcatenate();
|
||||
return listItem + " ";
|
||||
}
|
||||
|
||||
public static string RetrieveListItem(WmlDocument document,
|
||||
XElement paragraph, string bulletReplacementString)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(document))
|
||||
{
|
||||
using (WordprocessingDocument wdoc = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
return RetrieveListItem(wdoc, paragraph, bulletReplacementString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string RetrieveListItem(WordprocessingDocument wordDoc,
|
||||
XElement paragraph, string bulletReplacementString)
|
||||
{
|
||||
string pt = paragraph.Elements(W.r).Elements(W.t).Select(e => e.Value)
|
||||
.StringConcatenate();
|
||||
NumberingDefinitionsPart numberingDefinitionsPart =
|
||||
wordDoc.MainDocumentPart.NumberingDefinitionsPart;
|
||||
if (numberingDefinitionsPart == null)
|
||||
return null;
|
||||
StyleDefinitionsPart styleDefinitionsPart = wordDoc.MainDocumentPart
|
||||
.StyleDefinitionsPart;
|
||||
if (styleDefinitionsPart == null)
|
||||
return null;
|
||||
XDocument numbering = numberingDefinitionsPart.GetXDocument();
|
||||
XDocument styles = styleDefinitionsPart.GetXDocument();
|
||||
ListItemInfo listItemInfo = GetListItemInfo(numbering, styles, paragraph);
|
||||
if (listItemInfo.IsListItem)
|
||||
{
|
||||
string lvlText = (string)listItemInfo.Lvl.Elements(W.lvlText)
|
||||
.Attributes(W.val).FirstOrDefault();
|
||||
int[] levelNumbers = GetLevelNumbers(numbering, styles, paragraph);
|
||||
paragraph.AddAnnotation(new LevelNumbers()
|
||||
{
|
||||
LevelNumbersArray = levelNumbers
|
||||
});
|
||||
string listItem = FormatListItem(listItemInfo.Lvl, levelNumbers, lvlText,
|
||||
bulletReplacementString);
|
||||
return listItem;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
688
omegapro/PowerTools/Classes/MarkupSimplifier.cs
Normal file
688
omegapro/PowerTools/Classes/MarkupSimplifier.cs
Normal file
@@ -0,0 +1,688 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license
|
||||
can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Schema;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
public partial class WmlDocument : OpenXmlPowerToolsDocument
|
||||
{
|
||||
public WmlDocument SimplifyMarkup(SimplifyMarkupSettings settings)
|
||||
{
|
||||
return MarkupSimplifier.SimplifyMarkup(this, settings);
|
||||
}
|
||||
}
|
||||
|
||||
public class SimplifyMarkupSettings
|
||||
{
|
||||
public bool AcceptRevisions;
|
||||
public bool RemoveContentControls;
|
||||
public bool RemoveSmartTags;
|
||||
public bool RemoveRsidInfo;
|
||||
public bool RemoveComments;
|
||||
public bool RemoveEndAndFootNotes;
|
||||
public bool ReplaceTabsWithSpaces;
|
||||
public bool RemoveFieldCodes;
|
||||
public bool RemovePermissions;
|
||||
public bool RemoveProof;
|
||||
public bool RemoveSoftHyphens;
|
||||
public bool RemoveLastRenderedPageBreak;
|
||||
public bool RemoveBookmarks;
|
||||
public bool RemoveWebHidden;
|
||||
public bool RemoveGoBackBookmark;
|
||||
public bool NormalizeXml;
|
||||
}
|
||||
|
||||
public static class MarkupSimplifier
|
||||
{
|
||||
public static WmlDocument SimplifyMarkup(WmlDocument doc, SimplifyMarkupSettings settings)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
SimplifyMarkup(document, settings);
|
||||
}
|
||||
return streamDoc.GetModifiedWmlDocument();
|
||||
}
|
||||
}
|
||||
|
||||
public static void SimplifyMarkup(WordprocessingDocument doc,
|
||||
SimplifyMarkupSettings settings)
|
||||
{
|
||||
if (settings.AcceptRevisions)
|
||||
RevisionAccepter.AcceptRevisions(doc);
|
||||
foreach (var part in doc.ContentParts())
|
||||
SimplifyMarkupForPart(part, settings);
|
||||
if (doc.MainDocumentPart.StyleDefinitionsPart != null)
|
||||
SimplifyMarkupForPart(doc.MainDocumentPart.StyleDefinitionsPart, settings);
|
||||
if (doc.MainDocumentPart.StylesWithEffectsPart != null)
|
||||
SimplifyMarkupForPart(doc.MainDocumentPart.StylesWithEffectsPart, settings);
|
||||
}
|
||||
|
||||
public static XElement MergeAdjacentSuperfluousRuns(XElement element)
|
||||
{
|
||||
return (XElement)MergeAdjacentRunsTransform(element);
|
||||
}
|
||||
|
||||
public static XElement TransformElementToSingleCharacterRuns(XElement element)
|
||||
{
|
||||
return (XElement)SingleCharacterRunTransform(element);
|
||||
}
|
||||
|
||||
public static void TransformPartToSingleCharacterRuns(OpenXmlPart part)
|
||||
{
|
||||
// After transforming to single character runs, Rsid info will be invalid, so
|
||||
// remove from the part.
|
||||
XDocument xDoc = part.GetXDocument();
|
||||
XElement newRoot = (XElement)RemoveRsidTransform(xDoc.Root);
|
||||
newRoot = (XElement)SingleCharacterRunTransform(newRoot);
|
||||
xDoc.Elements().First().ReplaceWith(newRoot);
|
||||
part.PutXDocument();
|
||||
}
|
||||
|
||||
public static void TransformToSingleCharacterRuns(WordprocessingDocument doc)
|
||||
{
|
||||
if (RevisionAccepter.HasTrackedRevisions(doc))
|
||||
throw new OpenXmlPowerToolsException(
|
||||
"Transforming a document to single character runs is not supported for " +
|
||||
"a document with tracked revisions.");
|
||||
foreach (var part in doc.ContentParts())
|
||||
TransformPartToSingleCharacterRuns(part);
|
||||
}
|
||||
|
||||
private static object RemoveCustomXmlAndContentControlsTransform(
|
||||
XNode node, SimplifyMarkupSettings simplifyMarkupSettings)
|
||||
{
|
||||
XElement element = node as XElement;
|
||||
if (element != null)
|
||||
{
|
||||
if (simplifyMarkupSettings.RemoveSmartTags &&
|
||||
element.Name == W.smartTag)
|
||||
return element
|
||||
.Elements()
|
||||
.Select(e =>
|
||||
RemoveCustomXmlAndContentControlsTransform(e,
|
||||
simplifyMarkupSettings));
|
||||
|
||||
if (simplifyMarkupSettings.RemoveContentControls &&
|
||||
element.Name == W.sdt)
|
||||
return element
|
||||
.Element(W.sdtContent)
|
||||
.Elements()
|
||||
.Select(e =>
|
||||
RemoveCustomXmlAndContentControlsTransform(e,
|
||||
simplifyMarkupSettings));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private static object RemoveRsidTransform(XNode node)
|
||||
{
|
||||
XElement element = node as XElement;
|
||||
if (element != null)
|
||||
{
|
||||
if (element.Name == W.rsid)
|
||||
return null;
|
||||
return new XElement(element.Name,
|
||||
element.Attributes().Where(a => a.Name != W.rsid &&
|
||||
a.Name != W.rsidDel &&
|
||||
a.Name != W.rsidP &&
|
||||
a.Name != W.rsidR &&
|
||||
a.Name != W.rsidRDefault &&
|
||||
a.Name != W.rsidRPr &&
|
||||
a.Name != W.rsidSect &&
|
||||
a.Name != W.rsidTr),
|
||||
element.Nodes().Select(n => RemoveRsidTransform(n)));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private static XAttribute GetXmlSpaceAttribute(
|
||||
string textElementValue)
|
||||
{
|
||||
if (textElementValue.Length > 0 &&
|
||||
(textElementValue[0] == ' ' ||
|
||||
textElementValue[textElementValue.Length - 1] == ' '))
|
||||
return new XAttribute(XNamespace.Xml + "space",
|
||||
"preserve");
|
||||
return null;
|
||||
}
|
||||
|
||||
private static object MergeAdjacentRunsTransform(XNode node)
|
||||
{
|
||||
XElement element = node as XElement;
|
||||
if (element != null)
|
||||
{
|
||||
if (element.Name == W.p)
|
||||
{
|
||||
XElement paragraph = element;
|
||||
var runGroups = paragraph
|
||||
.Elements()
|
||||
.GroupAdjacent(r =>
|
||||
{
|
||||
if (r.Name != W.r)
|
||||
return "NotRuns";
|
||||
XElement rPr = r.Element(W.rPr);
|
||||
if (rPr == null)
|
||||
return "NoRunProperties";
|
||||
return rPr.ToString(
|
||||
SaveOptions.DisableFormatting);
|
||||
});
|
||||
XElement newParagraph = new XElement(W.p,
|
||||
paragraph.Attributes(),
|
||||
runGroups.Select(g =>
|
||||
{
|
||||
if (g.Key == "NotRuns")
|
||||
return (object)g;
|
||||
if (g.Key == "NoRunProperties")
|
||||
{
|
||||
XElement newRun = new XElement(W.r,
|
||||
g.First().Attributes(),
|
||||
g.Elements()
|
||||
.GroupAdjacent(c => c.Name)
|
||||
.Select(gc =>
|
||||
{
|
||||
if (gc.Key != W.t)
|
||||
return (object)gc;
|
||||
string textElementValue =
|
||||
gc.Select(t => (string)t)
|
||||
.StringConcatenate();
|
||||
return new XElement(W.t,
|
||||
GetXmlSpaceAttribute(
|
||||
textElementValue),
|
||||
textElementValue);
|
||||
}));
|
||||
return newRun;
|
||||
}
|
||||
XElement runPropertyElement = XElement.Parse(
|
||||
g.Key);
|
||||
runPropertyElement.Attributes()
|
||||
.Where(a => a.IsNamespaceDeclaration)
|
||||
.Remove();
|
||||
XElement newRunWithProperties = new XElement(
|
||||
W.r,
|
||||
g.First().Attributes(),
|
||||
runPropertyElement,
|
||||
g.Elements()
|
||||
.Where(e => e.Name != W.rPr)
|
||||
.GroupAdjacent(c => c.Name)
|
||||
.Select(gc =>
|
||||
{
|
||||
if (gc.Key != W.t)
|
||||
return (object)gc;
|
||||
string textElementValue = gc
|
||||
.Select(t => (string)t)
|
||||
.StringConcatenate();
|
||||
return new XElement(W.t,
|
||||
GetXmlSpaceAttribute(
|
||||
textElementValue),
|
||||
textElementValue);
|
||||
}));
|
||||
return newRunWithProperties;
|
||||
}
|
||||
));
|
||||
return newParagraph;
|
||||
}
|
||||
return new XElement(element.Name,
|
||||
element.Attributes(),
|
||||
element.Nodes().Select(n =>
|
||||
MergeAdjacentRunsTransform(n)));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private static object RemoveEmptyRunsAndRunPropertiesTransform(
|
||||
XNode node)
|
||||
{
|
||||
XElement element = node as XElement;
|
||||
if (element != null)
|
||||
{
|
||||
if ((element.Name == W.r || element.Name == W.rPr || element.Name == W.pPr) &&
|
||||
!element.Elements().Any())
|
||||
return null;
|
||||
return new XElement(element.Name,
|
||||
element.Attributes(),
|
||||
element.Nodes()
|
||||
.Select(n =>
|
||||
RemoveEmptyRunsAndRunPropertiesTransform(n)));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private static object MergeAdjacentInstrText(
|
||||
XNode node)
|
||||
{
|
||||
XElement element = node as XElement;
|
||||
if (element != null)
|
||||
{
|
||||
if (element.Name == W.r && element.Elements(W.instrText).Any())
|
||||
{
|
||||
var grouped = element.Elements().GroupAdjacent(e => e.Name == W.instrText);
|
||||
return new XElement(W.r,
|
||||
grouped.Select(g =>
|
||||
{
|
||||
if (g.Key == false)
|
||||
return (object)g;
|
||||
string newInstrText = g.Select(i => (string)i).StringConcatenate();
|
||||
return new XElement(W.instrText,
|
||||
newInstrText[0] == ' ' || newInstrText[newInstrText.Length - 1] == ' ' ?
|
||||
new XAttribute(XNamespace.Xml + "space", "preserve") : null,
|
||||
newInstrText);
|
||||
}));
|
||||
}
|
||||
return new XElement(element.Name,
|
||||
element.Attributes(),
|
||||
element.Nodes()
|
||||
.Select(n =>
|
||||
MergeAdjacentInstrText(n)));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
// lastRenderedPageBreak, permEnd, permStart, proofErr, noProof
|
||||
// softHyphen:
|
||||
// Remove when simplifying.
|
||||
|
||||
// fldSimple, fldData, fldChar, instrText:
|
||||
// For hyperlinks, generate same in XHtml. Other than hyperlinks, do the following:
|
||||
// - collapse fldSimple
|
||||
// - remove fldSimple, fldData, fldChar, instrText.
|
||||
|
||||
private static object SimplifyMarkupTransform(
|
||||
XNode node,
|
||||
SimplifyMarkupSettings settings,
|
||||
SimplifyMarkupParameters parameters)
|
||||
{
|
||||
XElement element = node as XElement;
|
||||
if (element != null)
|
||||
{
|
||||
if (settings.RemovePermissions &&
|
||||
(element.Name == W.permEnd ||
|
||||
element.Name == W.permStart))
|
||||
return null;
|
||||
|
||||
if (settings.RemoveProof &&
|
||||
(element.Name == W.proofErr ||
|
||||
element.Name == W.noProof))
|
||||
return null;
|
||||
|
||||
if (settings.RemoveSoftHyphens &&
|
||||
element.Name == W.softHyphen)
|
||||
return null;
|
||||
|
||||
if (settings.RemoveLastRenderedPageBreak &&
|
||||
element.Name == W.lastRenderedPageBreak)
|
||||
return null;
|
||||
|
||||
if (settings.RemoveBookmarks &&
|
||||
(element.Name == W.bookmarkStart ||
|
||||
element.Name == W.bookmarkEnd))
|
||||
return null;
|
||||
|
||||
if (settings.RemoveGoBackBookmark &&
|
||||
((element.Name == W.bookmarkStart && (int)element.Attribute(W.id) == parameters.GoBackId) ||
|
||||
(element.Name == W.bookmarkEnd && (int)element.Attribute(W.id) == parameters.GoBackId)))
|
||||
return null;
|
||||
|
||||
if (settings.RemoveWebHidden &&
|
||||
element.Name == W.webHidden)
|
||||
return null;
|
||||
|
||||
if (settings.ReplaceTabsWithSpaces && element.Name == W.tab &&
|
||||
element.Parent.Name == W.r)
|
||||
return new XElement(W.t,
|
||||
new XAttribute(XNamespace.Xml + "space", "preserve"),
|
||||
" ");
|
||||
|
||||
if (settings.RemoveComments &&
|
||||
(element.Name == W.commentRangeStart ||
|
||||
element.Name == W.commentRangeEnd ||
|
||||
element.Name == W.commentReference ||
|
||||
element.Name == W.annotationRef))
|
||||
return null;
|
||||
|
||||
if (settings.RemoveComments &&
|
||||
element.Name == W.rStyle &&
|
||||
element.Attribute(W.val).Value == "CommentReference")
|
||||
return null;
|
||||
|
||||
if (settings.RemoveEndAndFootNotes &&
|
||||
(element.Name == W.endnoteReference ||
|
||||
element.Name == W.footnoteReference))
|
||||
return null;
|
||||
|
||||
if (settings.RemoveFieldCodes)
|
||||
{
|
||||
if (element.Name == W.fldSimple)
|
||||
return element.Elements().Select(e =>
|
||||
SimplifyMarkupTransform(e, settings, parameters));
|
||||
if (element.Name == W.fldData ||
|
||||
element.Name == W.fldChar ||
|
||||
element.Name == W.instrText)
|
||||
return null;
|
||||
}
|
||||
|
||||
return new XElement(element.Name,
|
||||
element.Attributes(),
|
||||
element.Nodes().Select(n =>
|
||||
SimplifyMarkupTransform(n, settings, parameters)));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private static class Xsi
|
||||
{
|
||||
public static XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";
|
||||
|
||||
public static XName schemaLocation = xsi + "schemaLocation";
|
||||
public static XName noNamespaceSchemaLocation = xsi + "noNamespaceSchemaLocation";
|
||||
}
|
||||
|
||||
private static XDocument Normalize(XDocument source, XmlSchemaSet schema)
|
||||
{
|
||||
bool havePSVI = false;
|
||||
// validate, throw errors, add PSVI information
|
||||
if (schema != null)
|
||||
{
|
||||
source.Validate(schema, null, true);
|
||||
havePSVI = true;
|
||||
}
|
||||
return new XDocument(
|
||||
source.Declaration,
|
||||
source.Nodes().Select(n =>
|
||||
{
|
||||
// Remove comments, processing instructions, and text nodes that are
|
||||
// children of XDocument. Only white space text nodes are allowed as
|
||||
// children of a document, so we can remove all text nodes.
|
||||
if (n is XComment || n is XProcessingInstruction || n is XText)
|
||||
return null;
|
||||
XElement e = n as XElement;
|
||||
if (e != null)
|
||||
return NormalizeElement(e, havePSVI);
|
||||
return n;
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static bool DeepEqualsWithNormalization(XDocument doc1, XDocument doc2,
|
||||
XmlSchemaSet schemaSet)
|
||||
{
|
||||
XDocument d1 = Normalize(doc1, schemaSet);
|
||||
XDocument d2 = Normalize(doc2, schemaSet);
|
||||
return XNode.DeepEquals(d1, d2);
|
||||
}
|
||||
|
||||
private static IEnumerable<XAttribute> NormalizeAttributes(XElement element,
|
||||
bool havePSVI)
|
||||
{
|
||||
return element.Attributes()
|
||||
.Where(a => !a.IsNamespaceDeclaration &&
|
||||
a.Name != Xsi.schemaLocation &&
|
||||
a.Name != Xsi.noNamespaceSchemaLocation)
|
||||
.OrderBy(a => a.Name.NamespaceName)
|
||||
.ThenBy(a => a.Name.LocalName)
|
||||
.Select(
|
||||
a =>
|
||||
{
|
||||
if (havePSVI)
|
||||
{
|
||||
var dt = a.GetSchemaInfo().SchemaType.TypeCode;
|
||||
switch (dt)
|
||||
{
|
||||
case XmlTypeCode.Boolean:
|
||||
return new XAttribute(a.Name, (bool)a);
|
||||
case XmlTypeCode.DateTime:
|
||||
return new XAttribute(a.Name, (DateTime)a);
|
||||
case XmlTypeCode.Decimal:
|
||||
return new XAttribute(a.Name, (decimal)a);
|
||||
case XmlTypeCode.Double:
|
||||
return new XAttribute(a.Name, (double)a);
|
||||
case XmlTypeCode.Float:
|
||||
return new XAttribute(a.Name, (float)a);
|
||||
case XmlTypeCode.HexBinary:
|
||||
case XmlTypeCode.Language:
|
||||
return new XAttribute(a.Name,
|
||||
((string)a).ToLower());
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static XNode NormalizeNode(XNode node, bool havePSVI)
|
||||
{
|
||||
// trim comments and processing instructions from normalized tree
|
||||
if (node is XComment || node is XProcessingInstruction)
|
||||
return null;
|
||||
XElement e = node as XElement;
|
||||
if (e != null)
|
||||
return NormalizeElement(e, havePSVI);
|
||||
// Only thing left is XCData and XText, so clone them
|
||||
return node;
|
||||
}
|
||||
|
||||
private static XElement NormalizeElement(XElement element, bool havePSVI)
|
||||
{
|
||||
if (havePSVI)
|
||||
{
|
||||
var dt = element.GetSchemaInfo();
|
||||
switch (dt.SchemaType.TypeCode)
|
||||
{
|
||||
case XmlTypeCode.Boolean:
|
||||
return new XElement(element.Name,
|
||||
NormalizeAttributes(element, havePSVI),
|
||||
(bool)element);
|
||||
case XmlTypeCode.DateTime:
|
||||
return new XElement(element.Name,
|
||||
NormalizeAttributes(element, havePSVI),
|
||||
(DateTime)element);
|
||||
case XmlTypeCode.Decimal:
|
||||
return new XElement(element.Name,
|
||||
NormalizeAttributes(element, havePSVI),
|
||||
(decimal)element);
|
||||
case XmlTypeCode.Double:
|
||||
return new XElement(element.Name,
|
||||
NormalizeAttributes(element, havePSVI),
|
||||
(double)element);
|
||||
case XmlTypeCode.Float:
|
||||
return new XElement(element.Name,
|
||||
NormalizeAttributes(element, havePSVI),
|
||||
(float)element);
|
||||
case XmlTypeCode.HexBinary:
|
||||
case XmlTypeCode.Language:
|
||||
return new XElement(element.Name,
|
||||
NormalizeAttributes(element, havePSVI),
|
||||
((string)element).ToLower());
|
||||
default:
|
||||
return new XElement(element.Name,
|
||||
NormalizeAttributes(element, havePSVI),
|
||||
element.Nodes().Select(n => NormalizeNode(n, havePSVI))
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new XElement(element.Name,
|
||||
NormalizeAttributes(element, havePSVI),
|
||||
element.Nodes().Select(n => NormalizeNode(n, havePSVI))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SimplifyMarkupForPart(
|
||||
OpenXmlPart part,
|
||||
SimplifyMarkupSettings settings)
|
||||
{
|
||||
SimplifyMarkupParameters parameters = new SimplifyMarkupParameters();
|
||||
if (part.ContentType == "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml")
|
||||
{
|
||||
WordprocessingDocument doc = (WordprocessingDocument)part.OpenXmlPackage;
|
||||
if (settings.RemoveGoBackBookmark == true)
|
||||
{
|
||||
var goBackBookmark = doc
|
||||
.MainDocumentPart
|
||||
.GetXDocument()
|
||||
.Root
|
||||
.Descendants(W.bookmarkStart)
|
||||
.FirstOrDefault(bm => (string)bm.Attribute(W.name) == "_GoBack");
|
||||
if (goBackBookmark != null)
|
||||
parameters.GoBackId = (int)goBackBookmark.Attribute(W.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
XDocument xdoc = part.GetXDocument();
|
||||
XElement newRoot = xdoc.Root;
|
||||
|
||||
// Need to do this first to enable simplifying hyperlinks.
|
||||
if (settings.RemoveContentControls ||
|
||||
settings.RemoveSmartTags)
|
||||
newRoot = (XElement)
|
||||
RemoveCustomXmlAndContentControlsTransform(
|
||||
newRoot, settings);
|
||||
|
||||
// This may touch many elements, so needs to be its own
|
||||
// transform.
|
||||
if (settings.RemoveRsidInfo)
|
||||
newRoot = (XElement)RemoveRsidTransform(newRoot);
|
||||
|
||||
XDocument prevNewRoot = new XDocument(newRoot);
|
||||
while (true)
|
||||
{
|
||||
if (settings.RemoveComments ||
|
||||
settings.RemoveEndAndFootNotes ||
|
||||
settings.ReplaceTabsWithSpaces ||
|
||||
settings.RemoveFieldCodes ||
|
||||
settings.RemovePermissions ||
|
||||
settings.RemoveProof ||
|
||||
settings.RemoveBookmarks ||
|
||||
settings.RemoveWebHidden ||
|
||||
settings.RemoveGoBackBookmark)
|
||||
newRoot = (XElement)SimplifyMarkupTransform(newRoot,
|
||||
settings, parameters);
|
||||
|
||||
// Remove runs and run properties that have become empty due to previous
|
||||
// transforms.
|
||||
newRoot = (XElement)
|
||||
RemoveEmptyRunsAndRunPropertiesTransform(newRoot);
|
||||
|
||||
// Merge adjacent runs that have identical run properties.
|
||||
newRoot = (XElement)MergeAdjacentRunsTransform(newRoot);
|
||||
|
||||
// Merge adjacent instrText elements.
|
||||
newRoot = (XElement)MergeAdjacentInstrText(newRoot);
|
||||
|
||||
if (XNode.DeepEquals(prevNewRoot.Root, newRoot))
|
||||
break;
|
||||
|
||||
prevNewRoot = new XDocument(newRoot);
|
||||
}
|
||||
|
||||
if (settings.NormalizeXml)
|
||||
{
|
||||
XAttribute[] ns_attrs =
|
||||
{
|
||||
new XAttribute(XNamespace.Xmlns + "wpc", WPC.wpc),
|
||||
new XAttribute(XNamespace.Xmlns + "mc", MC.mc),
|
||||
new XAttribute(XNamespace.Xmlns + "o", O.o),
|
||||
new XAttribute(XNamespace.Xmlns + "r", R.r),
|
||||
new XAttribute(XNamespace.Xmlns + "m", M.m),
|
||||
new XAttribute(XNamespace.Xmlns + "v", VML.vml),
|
||||
new XAttribute(XNamespace.Xmlns + "wp14", WP14.wp14),
|
||||
new XAttribute(XNamespace.Xmlns + "wp", WP.wp),
|
||||
new XAttribute(XNamespace.Xmlns + "w10", W10.w10),
|
||||
new XAttribute(XNamespace.Xmlns + "w", W.w),
|
||||
new XAttribute(XNamespace.Xmlns + "w14", W14.w14),
|
||||
new XAttribute(XNamespace.Xmlns + "wpg", WPG.wpg),
|
||||
new XAttribute(XNamespace.Xmlns + "wpi", WPI.wpi),
|
||||
new XAttribute(XNamespace.Xmlns + "wne", WNE.wne),
|
||||
new XAttribute(XNamespace.Xmlns + "wps", WPS.wps),
|
||||
new XAttribute(MC.Ignorable, "w14 wp14"),
|
||||
};
|
||||
|
||||
XDocument newXDoc = Normalize(new XDocument(newRoot), null);
|
||||
foreach (var nsatt in ns_attrs)
|
||||
{
|
||||
if (newXDoc.Root.Attribute(nsatt.Name) == null)
|
||||
newXDoc.Root.Add(nsatt);
|
||||
}
|
||||
part.PutXDocument(newXDoc);
|
||||
}
|
||||
else
|
||||
{
|
||||
part.PutXDocument(new XDocument(newRoot));
|
||||
}
|
||||
}
|
||||
|
||||
private static object SingleCharacterRunTransform(XNode node)
|
||||
{
|
||||
XElement element = node as XElement;
|
||||
if (element != null)
|
||||
{
|
||||
if (element.Name == W.r)
|
||||
{
|
||||
var replacementRuns = element.Elements()
|
||||
.Where(e => e.Name != W.rPr)
|
||||
.GroupAdjacent(sr => sr.Name == W.t)
|
||||
.Select(g =>
|
||||
{
|
||||
if (g.Key)
|
||||
{
|
||||
string s = g.Select(t => (string)t).StringConcatenate();
|
||||
var newTextRuns = s.Select(c =>
|
||||
new XElement(W.r,
|
||||
element.Elements(W.rPr),
|
||||
new XElement(W.t,
|
||||
c == ' ' ? new XAttribute(XNamespace.Xml + "space", "preserve") : null,
|
||||
c)));
|
||||
return newTextRuns;
|
||||
}
|
||||
var newNonTextRuns = g.Select(sr =>
|
||||
new XElement(W.r,
|
||||
element.Elements(W.rPr),
|
||||
new XElement(sr.Name,
|
||||
sr.Attributes(),
|
||||
sr.Nodes().Select(n => SingleCharacterRunTransform(n)))));
|
||||
return newNonTextRuns;
|
||||
});
|
||||
return replacementRuns;
|
||||
}
|
||||
return new XElement(element.Name,
|
||||
element.Attributes(),
|
||||
element.Nodes().Select(n => SingleCharacterRunTransform(n)));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
public class InternalException : Exception
|
||||
{
|
||||
public InternalException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
public class InvalidSettingsException : Exception
|
||||
{
|
||||
public InvalidSettingsException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
private class SimplifyMarkupParameters
|
||||
{
|
||||
public int? GoBackId { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
164
omegapro/PowerTools/Classes/PictureAccessor.cs
Normal file
164
omegapro/PowerTools/Classes/PictureAccessor.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.XPath;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
using System;
|
||||
using System.Drawing.Imaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to picture operations
|
||||
/// </summary>
|
||||
public class PictureAccessor
|
||||
{
|
||||
private static XNamespace mainns;
|
||||
private static XNamespace wordprocessingDrawingns;
|
||||
private static XNamespace drawingmlMainns;
|
||||
private static XNamespace picturens;
|
||||
private static XNamespace relationshipns;
|
||||
private const int pixelsPerEmu = 9525;
|
||||
|
||||
static PictureAccessor() {
|
||||
mainns = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
||||
wordprocessingDrawingns = "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing";
|
||||
drawingmlMainns = "http://schemas.openxmlformats.org/drawingml/2006/main";
|
||||
picturens = "http://schemas.openxmlformats.org/drawingml/2006/picture";
|
||||
relationshipns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
||||
}
|
||||
|
||||
public static OpenXmlPowerToolsDocument Insert(WmlDocument doc, string xpathInsertionPoint, Image pictureToInsert, string name)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
XDocument xmlMainDocument = document.MainDocumentPart.GetXDocument();
|
||||
ImagePart picturePart = null;
|
||||
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(new NameTable());
|
||||
namespaceManager.AddNamespace("w", mainns.NamespaceName);
|
||||
IEnumerable<XElement> insertionPoints = xmlMainDocument.XPathSelectElements(xpathInsertionPoint, namespaceManager);
|
||||
|
||||
//make the insertion for each insertion point specified in the xpath query
|
||||
foreach (XElement insertionPoint in insertionPoints)
|
||||
{
|
||||
if (picturePart == null)
|
||||
{
|
||||
// Create the picture part in the package
|
||||
picturePart = document.MainDocumentPart.AddImagePart(GetImagePartType(pictureToInsert.RawFormat));
|
||||
}
|
||||
|
||||
// the pictures in the main document part goes in a very long xml, wich specifies the way the picture
|
||||
// has to be placed using drawingXml.
|
||||
insertionPoint.AddAfterSelf(
|
||||
new XElement(mainns + "p",
|
||||
new XElement(mainns + "r",
|
||||
new XElement(mainns + "drawing",
|
||||
new XElement(wordprocessingDrawingns + "inline",
|
||||
new XElement(wordprocessingDrawingns + "extent",
|
||||
new XAttribute("cx", pictureToInsert.Width * pixelsPerEmu),
|
||||
new XAttribute("cy", pictureToInsert.Height * pixelsPerEmu)
|
||||
),
|
||||
new XElement(wordprocessingDrawingns + "docPr",
|
||||
new XAttribute("name", name),
|
||||
new XAttribute("id", "1")
|
||||
),
|
||||
new XElement(drawingmlMainns + "graphic",
|
||||
new XAttribute(XNamespace.Xmlns + "a", drawingmlMainns.NamespaceName),
|
||||
new XElement(drawingmlMainns + "graphicData",
|
||||
new XAttribute("uri", picturens.NamespaceName),
|
||||
new XElement(picturens + "pic",
|
||||
new XAttribute(XNamespace.Xmlns + "pic", picturens.NamespaceName),
|
||||
new XElement(picturens + "nvPicPr",
|
||||
new XElement(picturens + "cNvPr",
|
||||
new XAttribute("id", "0"),
|
||||
new XAttribute("name", name)
|
||||
),
|
||||
new XElement(picturens + "cNvPicPr")
|
||||
),
|
||||
new XElement(picturens + "blipFill",
|
||||
new XElement(drawingmlMainns + "blip",
|
||||
new XAttribute(relationshipns + "embed", document.MainDocumentPart.GetIdOfPart(picturePart))
|
||||
),
|
||||
new XElement(drawingmlMainns + "stretch",
|
||||
new XElement(drawingmlMainns + "fillRect")
|
||||
)
|
||||
),
|
||||
new XElement(picturens + "spPr",
|
||||
new XElement(drawingmlMainns + "xfrm",
|
||||
new XElement(drawingmlMainns + "off",
|
||||
new XAttribute("x", "0"),
|
||||
new XAttribute("y", "0")
|
||||
),
|
||||
new XElement(drawingmlMainns + "ext",
|
||||
new XAttribute("cx", pictureToInsert.Width * pixelsPerEmu),
|
||||
new XAttribute("cy", pictureToInsert.Height * pixelsPerEmu)
|
||||
)
|
||||
),
|
||||
new XElement(drawingmlMainns + "prstGeom",
|
||||
new XAttribute("prst", "rect")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
if (picturePart != null)
|
||||
{
|
||||
Stream partStream = picturePart.GetStream(FileMode.Create, FileAccess.ReadWrite);
|
||||
pictureToInsert.Save(partStream, pictureToInsert.RawFormat);
|
||||
}
|
||||
else
|
||||
throw new ArgumentException("The xpath query did not return a valid location.");
|
||||
document.MainDocumentPart.PutXDocument();
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the image type representation for a mimetype
|
||||
/// </summary>
|
||||
/// <param name="format">Content mimetype</param>
|
||||
/// <returns>Image type</returns>
|
||||
private static ImagePartType GetImagePartType(ImageFormat format)
|
||||
{
|
||||
if (format.Equals(ImageFormat.Jpeg))
|
||||
return ImagePartType.Jpeg;
|
||||
else if (format.Equals(ImageFormat.Emf))
|
||||
return ImagePartType.Emf;
|
||||
else if (format.Equals(ImageFormat.Gif))
|
||||
return ImagePartType.Gif;
|
||||
else if (format.Equals(ImageFormat.Icon))
|
||||
return ImagePartType.Icon;
|
||||
else if (format.Equals(ImageFormat.Png))
|
||||
return ImagePartType.Png;
|
||||
else if (format.Equals(ImageFormat.Tiff))
|
||||
return ImagePartType.Tiff;
|
||||
else if (format.Equals(ImageFormat.Wmf))
|
||||
return ImagePartType.Wmf;
|
||||
else
|
||||
return ImagePartType.Bmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
544
omegapro/PowerTools/Classes/PowerToolsExtensions.cs
Normal file
544
omegapro/PowerTools/Classes/PowerToolsExtensions.cs
Normal file
@@ -0,0 +1,544 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using System.IO;
|
||||
using System.IO.Packaging;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
using DocumentFormat.OpenXml.Validation;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains extension methods to modify Open XML Documents
|
||||
/// </summary>
|
||||
public static class PowerToolsExtensions
|
||||
{
|
||||
public static void RemovePart(this OpenXmlPart part)
|
||||
{
|
||||
var parentParts = part.GetParentParts().ToList();
|
||||
foreach (var parentPart in parentParts)
|
||||
parentPart.DeletePart(part);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes personal information from the document.
|
||||
/// </summary>
|
||||
/// <param name="document"></param>
|
||||
public static OpenXmlPowerToolsDocument RemovePersonalInformation(WmlDocument doc)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
XNamespace x = "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties";
|
||||
XDocument extendedFileProperties = document.ExtendedFilePropertiesPart.GetXDocument();
|
||||
extendedFileProperties.Elements(x + "Properties").Elements(x + "Company").Remove();
|
||||
XElement totalTime = extendedFileProperties.Elements(x + "Properties").Elements(x + "TotalTime").FirstOrDefault();
|
||||
if (totalTime != null)
|
||||
totalTime.Value = "0";
|
||||
document.ExtendedFilePropertiesPart.PutXDocument();
|
||||
|
||||
XNamespace dc = "http://purl.org/dc/elements/1.1/";
|
||||
XNamespace cp = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties";
|
||||
XDocument coreFileProperties = document.CoreFilePropertiesPart.GetXDocument();
|
||||
foreach (var textNode in coreFileProperties.Elements(cp + "coreProperties")
|
||||
.Elements(dc + "creator")
|
||||
.Nodes()
|
||||
.OfType<XText>())
|
||||
textNode.Value = "";
|
||||
foreach (var textNode in coreFileProperties.Elements(cp + "coreProperties")
|
||||
.Elements(cp + "lastModifiedBy")
|
||||
.Nodes()
|
||||
.OfType<XText>())
|
||||
textNode.Value = "";
|
||||
foreach (var textNode in coreFileProperties.Elements(cp + "coreProperties")
|
||||
.Elements(dc + "title")
|
||||
.Nodes()
|
||||
.OfType<XText>())
|
||||
textNode.Value = "";
|
||||
XElement revision = coreFileProperties.Elements(cp + "coreProperties").Elements(cp + "revision").FirstOrDefault();
|
||||
if (revision != null)
|
||||
revision.Value = "1";
|
||||
document.CoreFilePropertiesPart.PutXDocument();
|
||||
|
||||
// add w:removePersonalInformation, w:removeDateAndTime to DocumentSettingsPart
|
||||
XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
||||
XDocument documentSettings = document.MainDocumentPart.DocumentSettingsPart.GetXDocument();
|
||||
// add the new elements in the right position. Add them after the following three elements
|
||||
// (which may or may not exist in the xml document).
|
||||
XElement settings = documentSettings.Root;
|
||||
XElement lastOfTop3 = settings.Elements()
|
||||
.Where(e => e.Name == w + "writeProtection" ||
|
||||
e.Name == w + "view" ||
|
||||
e.Name == w + "zoom")
|
||||
.InDocumentOrder()
|
||||
.LastOrDefault();
|
||||
if (lastOfTop3 == null)
|
||||
{
|
||||
// none of those three exist, so add as first children of the root element
|
||||
settings.AddFirst(
|
||||
settings.Elements(w + "removePersonalInformation").Any() ?
|
||||
null :
|
||||
new XElement(w + "removePersonalInformation"),
|
||||
settings.Elements(w + "removeDateAndTime").Any() ?
|
||||
null :
|
||||
new XElement(w + "removeDateAndTime")
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// one of those three exist, so add after the last one
|
||||
lastOfTop3.AddAfterSelf(
|
||||
settings.Elements(w + "removePersonalInformation").Any() ?
|
||||
null :
|
||||
new XElement(w + "removePersonalInformation"),
|
||||
settings.Elements(w + "removeDateAndTime").Any() ?
|
||||
null :
|
||||
new XElement(w + "removeDateAndTime")
|
||||
);
|
||||
}
|
||||
document.MainDocumentPart.DocumentSettingsPart.PutXDocument();
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
|
||||
private static string StringConcatenate<T>(this IEnumerable<T> source, Func<T, string> func, string separator)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (T item in source)
|
||||
sb.Append(func(item)).Append(separator);
|
||||
if (sb.Length > separator.Length)
|
||||
sb.Length -= separator.Length;
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
static string ContainsAnyStyles(IEnumerable<string> stylesToSearch, IEnumerable<string> searchStrings)
|
||||
{
|
||||
if (searchStrings == null)
|
||||
return null;
|
||||
foreach (var style in stylesToSearch)
|
||||
foreach (var s in searchStrings)
|
||||
if (String.Compare(style, s, true) == 0)
|
||||
return s;
|
||||
return null;
|
||||
}
|
||||
|
||||
static string ContainsAnyContent(string stringToSearch, IEnumerable<string> searchStrings,
|
||||
IEnumerable<Regex> regularExpressions, bool isRegularExpression, bool caseInsensitive)
|
||||
{
|
||||
if (searchStrings == null)
|
||||
return null;
|
||||
if (isRegularExpression)
|
||||
{
|
||||
foreach (var r in regularExpressions)
|
||||
if (r.IsMatch(stringToSearch))
|
||||
return r.ToString();
|
||||
}
|
||||
else
|
||||
if (caseInsensitive)
|
||||
{
|
||||
foreach (var s in searchStrings)
|
||||
if (stringToSearch.ToLower().Contains(s.ToLower()))
|
||||
return s;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var s in searchStrings)
|
||||
if (stringToSearch.Contains(s))
|
||||
return s;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static IEnumerable<string> GetAllStyleIdsAndNames(WordprocessingDocument doc, string styleId)
|
||||
{
|
||||
string localStyleId = styleId;
|
||||
XNamespace w =
|
||||
"http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
||||
|
||||
yield return styleId;
|
||||
|
||||
string styleNameForFirstStyle = (string)doc
|
||||
.MainDocumentPart
|
||||
.StyleDefinitionsPart
|
||||
.GetXDocument()
|
||||
.Root
|
||||
.Elements(w + "style")
|
||||
.Where(e => (string)e.Attribute(w + "type") == "paragraph" &&
|
||||
(string)e.Attribute(w + "styleId") == styleId)
|
||||
.Elements(w + "name")
|
||||
.Attributes(w + "val")
|
||||
.FirstOrDefault();
|
||||
|
||||
if (styleNameForFirstStyle != null)
|
||||
yield return styleNameForFirstStyle;
|
||||
|
||||
while (true)
|
||||
{
|
||||
XElement style = doc
|
||||
.MainDocumentPart
|
||||
.StyleDefinitionsPart
|
||||
.GetXDocument()
|
||||
.Root
|
||||
.Elements(w + "style")
|
||||
.Where(e => (string)e.Attribute(w + "type") == "paragraph" &&
|
||||
(string)e.Attribute(w + "styleId") == localStyleId)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (style == null)
|
||||
yield break;
|
||||
|
||||
var basedOn = (string)style
|
||||
.Elements(w + "basedOn")
|
||||
.Attributes(w + "val")
|
||||
.FirstOrDefault();
|
||||
|
||||
if (basedOn == null)
|
||||
yield break;
|
||||
|
||||
yield return basedOn;
|
||||
|
||||
XElement basedOnStyle = doc
|
||||
.MainDocumentPart
|
||||
.StyleDefinitionsPart
|
||||
.GetXDocument()
|
||||
.Root
|
||||
.Elements(w + "style")
|
||||
.Where(e => (string)e.Attribute(w + "type") == "paragraph" &&
|
||||
(string)e.Attribute(w + "styleId") == basedOn)
|
||||
.FirstOrDefault();
|
||||
|
||||
string basedOnStyleName = (string)basedOnStyle
|
||||
.Elements(w + "name")
|
||||
.Attributes(w + "val")
|
||||
.FirstOrDefault();
|
||||
|
||||
|
||||
if (basedOnStyleName != null)
|
||||
yield return basedOnStyleName;
|
||||
|
||||
localStyleId = basedOn;
|
||||
}
|
||||
}
|
||||
private static IEnumerable<string> GetInheritedStyles(WordprocessingDocument doc, string styleName)
|
||||
{
|
||||
string localStyleName = styleName;
|
||||
XNamespace w =
|
||||
"http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
||||
|
||||
yield return styleName;
|
||||
while (true)
|
||||
{
|
||||
XElement style = doc
|
||||
.MainDocumentPart
|
||||
.StyleDefinitionsPart
|
||||
.GetXDocument()
|
||||
.Root
|
||||
.Elements(w + "style")
|
||||
.Where(e => (string)e.Attribute(w + "type") == "paragraph" &&
|
||||
(string)e.Element(w + "name").Attribute(w + "val") == localStyleName)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (style == null)
|
||||
yield break;
|
||||
|
||||
var basedOn = (string)style
|
||||
.Elements(w + "basedOn")
|
||||
.Attributes(w + "val")
|
||||
.FirstOrDefault();
|
||||
|
||||
if (basedOn == null)
|
||||
yield break;
|
||||
|
||||
yield return basedOn;
|
||||
localStyleName = basedOn;
|
||||
}
|
||||
}
|
||||
|
||||
static public Commands.MatchInfo[] SearchInDocument(WmlDocument doc,
|
||||
IEnumerable<string> styleSearchString, IEnumerable<string> contentSearchString,
|
||||
bool isRegularExpression, bool caseInsensitive)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
XNamespace w =
|
||||
"http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
||||
XName r = w + "r";
|
||||
XName ins = w + "ins";
|
||||
|
||||
RegexOptions options;
|
||||
Regex[] regularExpressions = null;
|
||||
if (isRegularExpression && contentSearchString != null)
|
||||
{
|
||||
if (caseInsensitive)
|
||||
options = RegexOptions.IgnoreCase | RegexOptions.Compiled;
|
||||
else
|
||||
options = RegexOptions.Compiled;
|
||||
regularExpressions = contentSearchString
|
||||
.Select(s => new Regex(s, options)).ToArray();
|
||||
}
|
||||
|
||||
var defaultStyleName = (string)document
|
||||
.MainDocumentPart
|
||||
.StyleDefinitionsPart
|
||||
.GetXDocument()
|
||||
.Root
|
||||
.Elements(w + "style")
|
||||
.Where(style =>
|
||||
(string)style.Attribute(w + "type") == "paragraph" &&
|
||||
(string)style.Attribute(w + "default") == "1")
|
||||
.First()
|
||||
.Attribute(w + "styleId");
|
||||
|
||||
var q1 = document
|
||||
.MainDocumentPart
|
||||
.GetXDocument()
|
||||
.Root
|
||||
.Element(w + "body")
|
||||
.Elements()
|
||||
.Select((p, i) =>
|
||||
{
|
||||
var styleNode = p
|
||||
.Elements(w + "pPr")
|
||||
.Elements(w + "pStyle")
|
||||
.FirstOrDefault();
|
||||
var styleName = styleNode != null ?
|
||||
(string)styleNode.Attribute(w + "val") :
|
||||
defaultStyleName;
|
||||
return new
|
||||
{
|
||||
Element = p,
|
||||
Index = i,
|
||||
StyleName = styleName
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
var q2 = q1
|
||||
.Select(i =>
|
||||
{
|
||||
string text = null;
|
||||
if (i.Element.Name == w + "p")
|
||||
text = i.Element.Elements()
|
||||
.Where(z => z.Name == r || z.Name == ins)
|
||||
.Descendants(w + "t")
|
||||
.StringConcatenate(element => (string)element);
|
||||
else
|
||||
text = i.Element
|
||||
.Descendants(w + "p")
|
||||
.StringConcatenate(p => p
|
||||
.Elements()
|
||||
.Where(z => z.Name == r || z.Name == ins)
|
||||
.Descendants(w + "t")
|
||||
.StringConcatenate(element => (string)element),
|
||||
Environment.NewLine
|
||||
);
|
||||
|
||||
return new
|
||||
{
|
||||
Element = i.Element,
|
||||
StyleName = i.StyleName,
|
||||
Index = i.Index,
|
||||
Text = text
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
var q3 = q2
|
||||
.Select(i =>
|
||||
new Commands.MatchInfo
|
||||
{
|
||||
ElementNumber = i.Index + 1,
|
||||
Content = i.Text,
|
||||
Style = ContainsAnyStyles(GetAllStyleIdsAndNames(document, i.StyleName).Distinct(), styleSearchString),
|
||||
Pattern = ContainsAnyContent(i.Text, contentSearchString, regularExpressions, isRegularExpression, caseInsensitive),
|
||||
IgnoreCase = caseInsensitive
|
||||
}
|
||||
)
|
||||
.Where(i => (styleSearchString == null || i.Style != null) && (contentSearchString == null || i.Pattern != null));
|
||||
return q3.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static OpenXmlPowerToolsDocument InsertXml(OpenXmlPowerToolsDocument doc, string[] partPaths, string insertionXpath, string content)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (Package package = streamDoc.GetPackage())
|
||||
{
|
||||
foreach (string partPath in partPaths)
|
||||
{
|
||||
Uri xmlPartUri;
|
||||
XmlDocument xmlDocument;
|
||||
PackagePart xmlPart = null;
|
||||
|
||||
// Searches for the given part
|
||||
xmlPartUri = new Uri(partPath, UriKind.Relative);
|
||||
//if (!document.Package.PartExists(xmlPartUri))
|
||||
//throw new ArgumentException("Specified part does not exist.");
|
||||
|
||||
xmlPart = package.GetPart(xmlPartUri);
|
||||
using (XmlReader xmlReader = XmlReader.Create(xmlPart.GetStream(FileMode.Open, FileAccess.Read)))
|
||||
{
|
||||
try
|
||||
{
|
||||
xmlDocument = new XmlDocument();
|
||||
xmlDocument.Load(xmlReader);
|
||||
}
|
||||
catch (XmlException)
|
||||
{
|
||||
xmlDocument = new XmlDocument();
|
||||
}
|
||||
}
|
||||
|
||||
// Looks into the XmlDocument for nodes at the specified path
|
||||
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(xmlDocument.NameTable);
|
||||
namespaceManager.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
|
||||
XmlNode insertionPoint =
|
||||
xmlDocument.SelectSingleNode(insertionXpath, namespaceManager);
|
||||
if (insertionPoint == null)
|
||||
throw new ArgumentException("Insertion point does not exist.");
|
||||
|
||||
StringReader r = new StringReader("<w:node xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>" + content + "</w:node>");
|
||||
|
||||
XmlNode nodoid = xmlDocument.ReadNode(XmlReader.Create(r));
|
||||
//doc.LoadXml("<w:node xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>" + xmlContent + "</w:node>");
|
||||
|
||||
|
||||
//// Inserts new contents into the given part
|
||||
XmlNode xmlNodeToInsert =
|
||||
nodoid;// doc.FirstChild;
|
||||
// xmlDocument.CreateElement("w","node", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
|
||||
|
||||
|
||||
//xmlNodeToInsert. .InnerXml = xmlContent;
|
||||
XmlNodeList nodes = xmlNodeToInsert.ChildNodes;
|
||||
if (nodes.Count > 0)
|
||||
for (int i = nodes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
XmlNode node = nodes[i];
|
||||
insertionPoint.ParentNode.InsertAfter(node, insertionPoint);
|
||||
}
|
||||
|
||||
// Writes the XmlDocument back to the part
|
||||
using (XmlWriter writer = XmlWriter.Create(xmlPart.GetStream(FileMode.Create, FileAccess.Write)))
|
||||
{
|
||||
xmlDocument.WriteTo(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
|
||||
public static OpenXmlPowerToolsDocument Lock(WmlDocument doc)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
//Finds the settings part
|
||||
XDocument settingsDocument;
|
||||
XElement documentProtectionElement = null;
|
||||
if (document.MainDocumentPart.DocumentSettingsPart == null)
|
||||
{
|
||||
//If settings part does not exist creates a new one
|
||||
DocumentSettingsPart settingsPart = document.MainDocumentPart.AddNewPart<DocumentSettingsPart>();
|
||||
settingsDocument = settingsPart.GetXDocument();
|
||||
settingsDocument.Add(new XElement(W.settings,
|
||||
new XAttribute(XNamespace.Xmlns + "w", W.w),
|
||||
new XAttribute(XNamespace.Xmlns + "r", R.r)));
|
||||
}
|
||||
else
|
||||
{
|
||||
//If the settings part does exist looks if documentProtection has been included
|
||||
settingsDocument = document.MainDocumentPart.DocumentSettingsPart.GetXDocument();
|
||||
documentProtectionElement = settingsDocument.Element(W.settings).Element(W.documentProtection);
|
||||
}
|
||||
|
||||
//Creates the documentProtection element, or edits it if it exists
|
||||
if (documentProtectionElement == null)
|
||||
{
|
||||
settingsDocument
|
||||
.Element(W.settings)
|
||||
.Add(
|
||||
new XElement(W.documentProtection,
|
||||
new XAttribute(W.edit, "readOnly")
|
||||
)
|
||||
);
|
||||
}
|
||||
else
|
||||
documentProtectionElement.SetAttributeValue(W.edit, "readOnly");
|
||||
document.MainDocumentPart.DocumentSettingsPart.PutXDocument();
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
public static void SetContent(WordprocessingDocument document, IEnumerable<XElement> contents)
|
||||
{
|
||||
MainDocumentPart mainDocumentPart = document.MainDocumentPart;
|
||||
if (mainDocumentPart == null)
|
||||
mainDocumentPart = document.AddMainDocumentPart();
|
||||
XDocument mainDocumentPartContentToInsert = new XDocument(
|
||||
new XElement(W.document,
|
||||
new XAttribute(XNamespace.Xmlns + "w", W.w),
|
||||
new XAttribute(XNamespace.Xmlns + "r", R.r),
|
||||
new XElement(W.body, contents)));
|
||||
XDocument mainDocumentPartContent = mainDocumentPart.GetXDocument();
|
||||
if (mainDocumentPartContent.Root == null)
|
||||
mainDocumentPartContent.Add(mainDocumentPartContentToInsert.Root);
|
||||
else
|
||||
mainDocumentPartContent.Root.ReplaceWith(mainDocumentPartContentToInsert.Root);
|
||||
mainDocumentPart.PutXDocument();
|
||||
}
|
||||
|
||||
public static IEnumerable<ValidationErrorInfo> ValidateXml(OpenXmlPowerToolsDocument document)
|
||||
{
|
||||
if (document is WmlDocument)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(document))
|
||||
using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
OpenXmlValidator validator = new OpenXmlValidator();
|
||||
return validator.Validate(doc);
|
||||
}
|
||||
}
|
||||
else if (document is SmlDocument)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(document))
|
||||
using (SpreadsheetDocument doc = streamDoc.GetSpreadsheetDocument())
|
||||
{
|
||||
OpenXmlValidator validator = new OpenXmlValidator();
|
||||
return validator.Validate(doc);
|
||||
}
|
||||
}
|
||||
else if (document is PmlDocument)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(document))
|
||||
using (PresentationDocument doc = streamDoc.GetPresentationDocument())
|
||||
{
|
||||
OpenXmlValidator validator = new OpenXmlValidator();
|
||||
return validator.Validate(doc);
|
||||
}
|
||||
}
|
||||
throw new PowerToolsDocumentException("Not an Open XML document.");
|
||||
}
|
||||
}
|
||||
}
|
456
omegapro/PowerTools/Classes/PtOpenXmlDocument.cs
Normal file
456
omegapro/PowerTools/Classes/PtOpenXmlDocument.cs
Normal file
@@ -0,0 +1,456 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.IO.Packaging;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
public class PowerToolsDocumentException : Exception
|
||||
{
|
||||
public PowerToolsDocumentException(string message) : base(message) { }
|
||||
}
|
||||
public class PowerToolsInvalidDataException : Exception
|
||||
{
|
||||
public PowerToolsInvalidDataException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
public class OpenXmlPowerToolsDocument
|
||||
{
|
||||
public string FileName { get; set; }
|
||||
public byte[] DocumentByteArray { get; set; }
|
||||
|
||||
public static OpenXmlPowerToolsDocument FromFileName(string fileName)
|
||||
{
|
||||
byte[] bytes = File.ReadAllBytes(fileName);
|
||||
Type type;
|
||||
try
|
||||
{
|
||||
type = GetDocumentType(bytes);
|
||||
}
|
||||
catch (FileFormatException)
|
||||
{
|
||||
throw new PowerToolsDocumentException("Not an Open XML document.");
|
||||
}
|
||||
if (type == typeof(WordprocessingDocument))
|
||||
return new WmlDocument(fileName, bytes);
|
||||
if (type == typeof(SpreadsheetDocument))
|
||||
return new SmlDocument(fileName, bytes);
|
||||
if (type == typeof(PresentationDocument))
|
||||
return new PmlDocument(fileName, bytes);
|
||||
if (type == typeof(Package))
|
||||
{
|
||||
OpenXmlPowerToolsDocument pkg = new OpenXmlPowerToolsDocument(bytes);
|
||||
pkg.FileName = fileName;
|
||||
return pkg;
|
||||
}
|
||||
throw new PowerToolsDocumentException("Not an Open XML document.");
|
||||
}
|
||||
|
||||
public static OpenXmlPowerToolsDocument FromDocument(OpenXmlPowerToolsDocument doc)
|
||||
{
|
||||
Type type = doc.GetDocumentType();
|
||||
if (type == typeof(WordprocessingDocument))
|
||||
return new WmlDocument(doc);
|
||||
if (type == typeof(SpreadsheetDocument))
|
||||
return new SmlDocument(doc);
|
||||
if (type == typeof(PresentationDocument))
|
||||
return new PmlDocument(doc);
|
||||
return null; // This should not be possible from a valid OpenXmlPowerToolsDocument object
|
||||
}
|
||||
|
||||
public OpenXmlPowerToolsDocument(OpenXmlPowerToolsDocument original)
|
||||
{
|
||||
DocumentByteArray = new byte[original.DocumentByteArray.Length];
|
||||
Array.Copy(original.DocumentByteArray, DocumentByteArray, original.DocumentByteArray.Length);
|
||||
FileName = original.FileName;
|
||||
}
|
||||
|
||||
public OpenXmlPowerToolsDocument(string fileName)
|
||||
{
|
||||
this.FileName = fileName;
|
||||
DocumentByteArray = File.ReadAllBytes(fileName);
|
||||
}
|
||||
|
||||
public OpenXmlPowerToolsDocument(byte[] byteArray)
|
||||
{
|
||||
DocumentByteArray = new byte[byteArray.Length];
|
||||
Array.Copy(byteArray, DocumentByteArray, byteArray.Length);
|
||||
this.FileName = null;
|
||||
}
|
||||
|
||||
public OpenXmlPowerToolsDocument(string fileName, MemoryStream memStream)
|
||||
{
|
||||
FileName = fileName;
|
||||
DocumentByteArray = new byte[memStream.Length];
|
||||
Array.Copy(memStream.GetBuffer(), DocumentByteArray, memStream.Length);
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
if (FileName == null)
|
||||
return "Unnamed Document";
|
||||
FileInfo file = new FileInfo(FileName);
|
||||
return file.Name;
|
||||
}
|
||||
|
||||
public void SaveAs(string fileName)
|
||||
{
|
||||
File.WriteAllBytes(fileName, DocumentByteArray);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
if (this.FileName == null)
|
||||
throw new InvalidOperationException("Attempting to Save a document that has no file name. Use SaveAs instead.");
|
||||
File.WriteAllBytes(this.FileName, DocumentByteArray);
|
||||
}
|
||||
|
||||
public void WriteByteArray(Stream stream)
|
||||
{
|
||||
stream.Write(DocumentByteArray, 0, DocumentByteArray.Length);
|
||||
}
|
||||
|
||||
public Type GetDocumentType()
|
||||
{
|
||||
return GetDocumentType(DocumentByteArray);
|
||||
}
|
||||
|
||||
private static Type GetDocumentType(byte[] bytes)
|
||||
{
|
||||
using (MemoryStream stream = new MemoryStream(bytes))
|
||||
using (Package package = Package.Open(stream, FileMode.Open))
|
||||
{
|
||||
PackageRelationship relationship = package.GetRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument").FirstOrDefault();
|
||||
if (relationship != null)
|
||||
{
|
||||
PackagePart part = package.GetPart(PackUriHelper.ResolvePartUri(relationship.SourceUri, relationship.TargetUri));
|
||||
switch (part.ContentType)
|
||||
{
|
||||
case "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml":
|
||||
return typeof(WordprocessingDocument);
|
||||
case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml":
|
||||
return typeof(SpreadsheetDocument);
|
||||
case "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml":
|
||||
return typeof(PresentationDocument);
|
||||
}
|
||||
return typeof(Package);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SavePartAs(OpenXmlPart part, string filePath)
|
||||
{
|
||||
Stream partStream = part.GetStream(FileMode.Open, FileAccess.Read);
|
||||
byte[] partContent = new byte[partStream.Length];
|
||||
partStream.Read(partContent, 0, (int)partStream.Length);
|
||||
|
||||
File.WriteAllBytes(filePath, partContent);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class WmlDocument : OpenXmlPowerToolsDocument
|
||||
{
|
||||
public WmlDocument(OpenXmlPowerToolsDocument original)
|
||||
: base(original)
|
||||
{
|
||||
if (GetDocumentType() != typeof(WordprocessingDocument))
|
||||
throw new PowerToolsDocumentException("Not a Wordprocessing document.");
|
||||
}
|
||||
|
||||
public WmlDocument(string fileName)
|
||||
: base(fileName)
|
||||
{
|
||||
if (GetDocumentType() != typeof(WordprocessingDocument))
|
||||
throw new PowerToolsDocumentException("Not a Wordprocessing document.");
|
||||
}
|
||||
|
||||
public WmlDocument(string fileName, byte[] byteArray)
|
||||
: base(byteArray)
|
||||
{
|
||||
FileName = fileName;
|
||||
if (GetDocumentType() != typeof(WordprocessingDocument))
|
||||
throw new PowerToolsDocumentException("Not a Wordprocessing document.");
|
||||
}
|
||||
|
||||
public WmlDocument(string fileName, MemoryStream memStream)
|
||||
: base(fileName, memStream)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public partial class SmlDocument : OpenXmlPowerToolsDocument
|
||||
{
|
||||
public SmlDocument(OpenXmlPowerToolsDocument original)
|
||||
: base(original)
|
||||
{
|
||||
if (GetDocumentType() != typeof(SpreadsheetDocument))
|
||||
throw new PowerToolsDocumentException("Not a Spreadsheet document.");
|
||||
}
|
||||
|
||||
public SmlDocument(string fileName)
|
||||
: base(fileName)
|
||||
{
|
||||
if (GetDocumentType() != typeof(SpreadsheetDocument))
|
||||
throw new PowerToolsDocumentException("Not a Spreadsheet document.");
|
||||
}
|
||||
|
||||
public SmlDocument(string fileName, byte[] byteArray)
|
||||
: base(byteArray)
|
||||
{
|
||||
FileName = fileName;
|
||||
if (GetDocumentType() != typeof(SpreadsheetDocument))
|
||||
throw new PowerToolsDocumentException("Not a Spreadsheet document.");
|
||||
}
|
||||
|
||||
public SmlDocument(string fileName, MemoryStream memStream)
|
||||
: base(fileName, memStream)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public partial class PmlDocument : OpenXmlPowerToolsDocument
|
||||
{
|
||||
public PmlDocument(OpenXmlPowerToolsDocument original)
|
||||
: base(original)
|
||||
{
|
||||
if (GetDocumentType() != typeof(PresentationDocument))
|
||||
throw new PowerToolsDocumentException("Not a Presentation document.");
|
||||
}
|
||||
|
||||
public PmlDocument(string fileName)
|
||||
: base(fileName)
|
||||
{
|
||||
if (GetDocumentType() != typeof(PresentationDocument))
|
||||
throw new PowerToolsDocumentException("Not a Presentation document.");
|
||||
}
|
||||
|
||||
public PmlDocument(string fileName, byte[] byteArray)
|
||||
: base(byteArray)
|
||||
{
|
||||
FileName = fileName;
|
||||
if (GetDocumentType() != typeof(PresentationDocument))
|
||||
throw new PowerToolsDocumentException("Not a Presentation document.");
|
||||
}
|
||||
|
||||
public PmlDocument(string fileName, MemoryStream memStream)
|
||||
: base(fileName, memStream)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class OpenXmlMemoryStreamDocument : IDisposable
|
||||
{
|
||||
private OpenXmlPowerToolsDocument Document;
|
||||
private MemoryStream DocMemoryStream;
|
||||
private Package DocPackage;
|
||||
|
||||
public OpenXmlMemoryStreamDocument(OpenXmlPowerToolsDocument doc)
|
||||
{
|
||||
Document = doc;
|
||||
DocMemoryStream = new MemoryStream();
|
||||
doc.WriteByteArray(DocMemoryStream);
|
||||
try
|
||||
{
|
||||
DocPackage = Package.Open(DocMemoryStream, FileMode.Open);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new PowerToolsDocumentException("Not an Open XML document.");
|
||||
}
|
||||
}
|
||||
|
||||
internal OpenXmlMemoryStreamDocument(MemoryStream stream)
|
||||
{
|
||||
DocMemoryStream = stream;
|
||||
try
|
||||
{
|
||||
DocPackage = Package.Open(DocMemoryStream, FileMode.Open);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new PowerToolsDocumentException("Not an Open XML document.");
|
||||
}
|
||||
}
|
||||
|
||||
public static OpenXmlMemoryStreamDocument CreateWordprocessingDocument()
|
||||
{
|
||||
MemoryStream stream = new MemoryStream();
|
||||
using (WordprocessingDocument doc = WordprocessingDocument.Create(stream, DocumentFormat.OpenXml.WordprocessingDocumentType.Document))
|
||||
{
|
||||
doc.AddMainDocumentPart();
|
||||
doc.MainDocumentPart.PutXDocument(new XDocument(
|
||||
new XElement(W.document,
|
||||
new XAttribute(XNamespace.Xmlns + "w", W.w),
|
||||
new XAttribute(XNamespace.Xmlns + "r", R.r),
|
||||
new XElement(W.body))));
|
||||
doc.Close();
|
||||
return new OpenXmlMemoryStreamDocument(stream);
|
||||
}
|
||||
}
|
||||
public static OpenXmlMemoryStreamDocument CreateSpreadsheetDocument()
|
||||
{
|
||||
MemoryStream stream = new MemoryStream();
|
||||
using (SpreadsheetDocument doc = SpreadsheetDocument.Create(stream, DocumentFormat.OpenXml.SpreadsheetDocumentType.Workbook))
|
||||
{
|
||||
doc.AddWorkbookPart();
|
||||
XNamespace ns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
|
||||
XNamespace relationshipsns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
||||
doc.WorkbookPart.PutXDocument(new XDocument(
|
||||
new XElement(ns + "workbook",
|
||||
new XAttribute("xmlns", ns),
|
||||
new XAttribute(XNamespace.Xmlns + "r", relationshipsns),
|
||||
new XElement(ns + "sheets"))));
|
||||
doc.Close();
|
||||
return new OpenXmlMemoryStreamDocument(stream);
|
||||
}
|
||||
}
|
||||
|
||||
public static OpenXmlMemoryStreamDocument CreatePackage()
|
||||
{
|
||||
MemoryStream stream = new MemoryStream();
|
||||
Package package = Package.Open(stream, FileMode.Create);
|
||||
package.Close();
|
||||
return new OpenXmlMemoryStreamDocument(stream);
|
||||
}
|
||||
|
||||
public Package GetPackage()
|
||||
{
|
||||
return DocPackage;
|
||||
}
|
||||
|
||||
public WordprocessingDocument GetWordprocessingDocument()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (GetDocumentType() != typeof(WordprocessingDocument))
|
||||
throw new PowerToolsDocumentException("Not a Wordprocessing document.");
|
||||
return WordprocessingDocument.Open(DocPackage);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new PowerToolsDocumentException("Not a Wordprocessing document.");
|
||||
}
|
||||
}
|
||||
public SpreadsheetDocument GetSpreadsheetDocument()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (GetDocumentType() != typeof(SpreadsheetDocument))
|
||||
throw new PowerToolsDocumentException("Not a Spreadsheet document.");
|
||||
return SpreadsheetDocument.Open(DocPackage);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new PowerToolsDocumentException("Not a Spreadsheet document.");
|
||||
}
|
||||
}
|
||||
|
||||
public PresentationDocument GetPresentationDocument()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (GetDocumentType() != typeof(PresentationDocument))
|
||||
throw new PowerToolsDocumentException("Not a Presentation document.");
|
||||
return PresentationDocument.Open(DocPackage);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new PowerToolsDocumentException("Not a Presentation document.");
|
||||
}
|
||||
}
|
||||
|
||||
public Type GetDocumentType()
|
||||
{
|
||||
PackageRelationship relationship = DocPackage.GetRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument").FirstOrDefault();
|
||||
if (relationship == null)
|
||||
throw new PowerToolsDocumentException("Not an Open XML Document.");
|
||||
PackagePart part = DocPackage.GetPart(PackUriHelper.ResolvePartUri(relationship.SourceUri, relationship.TargetUri));
|
||||
switch (part.ContentType)
|
||||
{
|
||||
case "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml":
|
||||
return typeof(WordprocessingDocument);
|
||||
case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml":
|
||||
return typeof(SpreadsheetDocument);
|
||||
case "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml":
|
||||
return typeof(PresentationDocument);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public OpenXmlPowerToolsDocument GetModifiedDocument()
|
||||
{
|
||||
DocPackage.Close();
|
||||
DocPackage = null;
|
||||
return new OpenXmlPowerToolsDocument((Document == null) ? null : Document.FileName, DocMemoryStream);
|
||||
}
|
||||
|
||||
public WmlDocument GetModifiedWmlDocument()
|
||||
{
|
||||
DocPackage.Close();
|
||||
DocPackage = null;
|
||||
return new WmlDocument((Document == null) ? null : Document.FileName, DocMemoryStream);
|
||||
}
|
||||
|
||||
public SmlDocument GetModifiedSmlDocument()
|
||||
{
|
||||
DocPackage.Close();
|
||||
DocPackage = null;
|
||||
return new SmlDocument((Document == null) ? null : Document.FileName, DocMemoryStream);
|
||||
}
|
||||
|
||||
public PmlDocument GetModifiedPmlDocument()
|
||||
{
|
||||
DocPackage.Close();
|
||||
DocPackage = null;
|
||||
return new PmlDocument((Document == null) ? null : Document.FileName, DocMemoryStream);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
~OpenXmlMemoryStreamDocument()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
private void Dispose(Boolean disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (DocPackage != null)
|
||||
{
|
||||
DocPackage.Close();
|
||||
}
|
||||
if (DocMemoryStream != null)
|
||||
{
|
||||
DocMemoryStream.Dispose();
|
||||
}
|
||||
}
|
||||
if (DocPackage == null && DocMemoryStream == null)
|
||||
return;
|
||||
DocPackage = null;
|
||||
DocMemoryStream = null;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
2775
omegapro/PowerTools/Classes/PtOpenXmlUtil.cs
Normal file
2775
omegapro/PowerTools/Classes/PtOpenXmlUtil.cs
Normal file
File diff suppressed because it is too large
Load Diff
219
omegapro/PowerTools/Classes/PtUtil.cs
Normal file
219
omegapro/PowerTools/Classes/PtUtil.cs
Normal file
@@ -0,0 +1,219 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license
|
||||
can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
public static class PtExtensions
|
||||
{
|
||||
public static string StringConcatenate(this IEnumerable<string> source)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (string s in source)
|
||||
sb.Append(s);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string StringConcatenate<T>(
|
||||
this IEnumerable<T> source,
|
||||
Func<T, string> projectionFunc)
|
||||
{
|
||||
return source.Aggregate(
|
||||
new StringBuilder(),
|
||||
(s, i) => s.Append(projectionFunc(i)),
|
||||
s => s.ToString());
|
||||
}
|
||||
|
||||
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(
|
||||
this IEnumerable<TFirst> first,
|
||||
IEnumerable<TSecond> second,
|
||||
Func<TFirst, TSecond, TResult> func)
|
||||
{
|
||||
var ie1 = first.GetEnumerator();
|
||||
var ie2 = second.GetEnumerator();
|
||||
while (ie1.MoveNext() && ie2.MoveNext())
|
||||
yield return func(ie1.Current, ie2.Current);
|
||||
}
|
||||
|
||||
public static IEnumerable<IGrouping<TKey, TSource>> GroupAdjacent<TSource, TKey>(
|
||||
this IEnumerable<TSource> source,
|
||||
Func<TSource, TKey> keySelector)
|
||||
{
|
||||
TKey last = default(TKey);
|
||||
bool haveLast = false;
|
||||
List<TSource> list = new List<TSource>();
|
||||
|
||||
foreach (TSource s in source)
|
||||
{
|
||||
TKey k = keySelector(s);
|
||||
if (haveLast)
|
||||
{
|
||||
if (!k.Equals(last))
|
||||
{
|
||||
yield return new GroupOfAdjacent<TSource, TKey>(list, last);
|
||||
list = new List<TSource>();
|
||||
list.Add(s);
|
||||
last = k;
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(s);
|
||||
last = k;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(s);
|
||||
last = k;
|
||||
haveLast = true;
|
||||
}
|
||||
}
|
||||
if (haveLast)
|
||||
yield return new GroupOfAdjacent<TSource, TKey>(list, last);
|
||||
}
|
||||
|
||||
private static void InitializeReverseDocumentOrder(XElement element)
|
||||
{
|
||||
XElement prev = null;
|
||||
foreach (XElement e in element.Elements())
|
||||
{
|
||||
e.AddAnnotation(new ReverseDocumentOrderInfo { PreviousSibling = prev });
|
||||
prev = e;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<XElement> ElementsBeforeSelfReverseDocumentOrder(
|
||||
this XElement element)
|
||||
{
|
||||
if (element.Annotation<ReverseDocumentOrderInfo>() == null)
|
||||
InitializeReverseDocumentOrder(element.Parent);
|
||||
XElement current = element;
|
||||
while (true)
|
||||
{
|
||||
XElement previousElement = current
|
||||
.Annotation<ReverseDocumentOrderInfo>()
|
||||
.PreviousSibling;
|
||||
if (previousElement == null)
|
||||
yield break;
|
||||
yield return previousElement;
|
||||
current = previousElement;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ToStringNewLineOnAttributes(this XElement element)
|
||||
{
|
||||
XmlWriterSettings settings = new XmlWriterSettings();
|
||||
settings.Indent = true;
|
||||
settings.OmitXmlDeclaration = true;
|
||||
settings.NewLineOnAttributes = true;
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
using (StringWriter stringWriter = new StringWriter(stringBuilder))
|
||||
using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter, settings))
|
||||
element.WriteTo(xmlWriter);
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
public static IEnumerable<XElement> DescendantsTrimmed(this XElement element,
|
||||
XName trimName)
|
||||
{
|
||||
return DescendantsTrimmed(element, e => e.Name == trimName);
|
||||
}
|
||||
|
||||
public static IEnumerable<XElement> DescendantsTrimmed(this XElement element,
|
||||
Func<XElement, bool> predicate)
|
||||
{
|
||||
Stack<IEnumerator<XElement>> iteratorStack = new Stack<IEnumerator<XElement>>();
|
||||
iteratorStack.Push(element.Elements().GetEnumerator());
|
||||
while (iteratorStack.Count > 0)
|
||||
{
|
||||
while (iteratorStack.Peek().MoveNext())
|
||||
{
|
||||
XElement currentXElement = iteratorStack.Peek().Current;
|
||||
if (predicate(currentXElement))
|
||||
{
|
||||
yield return currentXElement;
|
||||
continue;
|
||||
}
|
||||
yield return currentXElement;
|
||||
iteratorStack.Push(currentXElement.Elements().GetEnumerator());
|
||||
}
|
||||
iteratorStack.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TResult> Rollup<TSource, TResult>(
|
||||
this IEnumerable<TSource> source,
|
||||
TResult seed,
|
||||
Func<TSource, TResult, TResult> projection)
|
||||
{
|
||||
TResult nextSeed = seed;
|
||||
foreach (TSource src in source)
|
||||
{
|
||||
TResult projectedValue = projection(src, nextSeed);
|
||||
nextSeed = projectedValue;
|
||||
yield return projectedValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TSource> SequenceAt<TSource>(this TSource[] source, int index)
|
||||
{
|
||||
int i = index;
|
||||
while (i < source.Length)
|
||||
yield return source[i++];
|
||||
}
|
||||
}
|
||||
|
||||
public class ReverseDocumentOrderInfo
|
||||
{
|
||||
public XElement PreviousSibling;
|
||||
}
|
||||
|
||||
public class GroupOfAdjacent<TSource, TKey> : IEnumerable<TSource>, IGrouping<TKey, TSource>
|
||||
{
|
||||
public TKey Key { get; set; }
|
||||
private List<TSource> GroupList { get; set; }
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((System.Collections.Generic.IEnumerable<TSource>)this).GetEnumerator();
|
||||
}
|
||||
|
||||
System.Collections.Generic.IEnumerator<TSource>
|
||||
System.Collections.Generic.IEnumerable<TSource>.GetEnumerator()
|
||||
{
|
||||
foreach (var s in GroupList)
|
||||
yield return s;
|
||||
}
|
||||
|
||||
public GroupOfAdjacent(List<TSource> source, TKey key)
|
||||
{
|
||||
GroupList = source;
|
||||
Key = key;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class XEntity : XText
|
||||
{
|
||||
public override void WriteTo(XmlWriter writer)
|
||||
{
|
||||
writer.WriteEntityRef(this.Value);
|
||||
}
|
||||
public XEntity(string value) : base(value) { }
|
||||
}
|
||||
}
|
647
omegapro/PowerTools/Classes/ReferenceAdder.cs
Normal file
647
omegapro/PowerTools/Classes/ReferenceAdder.cs
Normal file
@@ -0,0 +1,647 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license
|
||||
can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.XPath;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
public partial class WmlDocument : OpenXmlPowerToolsDocument
|
||||
{
|
||||
public WmlDocument AddToc(string xPath, string switches, string title, int? rightTabPos)
|
||||
{
|
||||
return (WmlDocument)ReferenceAdder.AddToc(this, xPath, switches, title, rightTabPos);
|
||||
}
|
||||
public WmlDocument AddTof(string xPath, string switches, int? rightTabPos)
|
||||
{
|
||||
return (WmlDocument)ReferenceAdder.AddTof(this, xPath, switches, rightTabPos);
|
||||
}
|
||||
public WmlDocument AddToa(string xPath, string switches, int? rightTabPos)
|
||||
{
|
||||
return (WmlDocument)ReferenceAdder.AddToa(this, xPath, switches, rightTabPos);
|
||||
}
|
||||
}
|
||||
|
||||
public class ReferenceAdder
|
||||
{
|
||||
public static WmlDocument AddToc(WmlDocument document, string xPath, string switches, string title, int? rightTabPos)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(document))
|
||||
{
|
||||
using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
AddToc(doc, xPath, switches, title, rightTabPos);
|
||||
}
|
||||
return streamDoc.GetModifiedWmlDocument();
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddToc(WordprocessingDocument doc, string xPath, string switches, string title, int? rightTabPos)
|
||||
{
|
||||
UpdateFontTablePart(doc);
|
||||
UpdateStylesPartForToc(doc);
|
||||
UpdateStylesWithEffectsPartForToc(doc);
|
||||
|
||||
if (title == null)
|
||||
title = "Contents";
|
||||
if (rightTabPos == null)
|
||||
rightTabPos = 9350;
|
||||
|
||||
// {0} tocTitle (default = "Contents")
|
||||
// {1} rightTabPosition (default = 9350)
|
||||
// {2} switches
|
||||
|
||||
String xmlString =
|
||||
@"<w:sdt xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
|
||||
<w:sdtPr>
|
||||
<w:docPartObj>
|
||||
<w:docPartGallery w:val='Table of Contents'/>
|
||||
<w:docPartUnique/>
|
||||
</w:docPartObj>
|
||||
</w:sdtPr>
|
||||
<w:sdtEndPr>
|
||||
<w:rPr>
|
||||
<w:rFonts w:asciiTheme='minorHAnsi' w:cstheme='minorBidi' w:eastAsiaTheme='minorHAnsi' w:hAnsiTheme='minorHAnsi'/>
|
||||
<w:color w:val='auto'/>
|
||||
<w:sz w:val='22'/>
|
||||
<w:szCs w:val='22'/>
|
||||
<w:lang w:eastAsia='en-US'/>
|
||||
</w:rPr>
|
||||
</w:sdtEndPr>
|
||||
<w:sdtContent>
|
||||
<w:p>
|
||||
<w:pPr>
|
||||
<w:pStyle w:val='TOCHeading'/>
|
||||
</w:pPr>
|
||||
<w:r>
|
||||
<w:t>{0}</w:t>
|
||||
</w:r>
|
||||
</w:p>
|
||||
<w:p>
|
||||
<w:pPr>
|
||||
<w:pStyle w:val='TOC1'/>
|
||||
<w:tabs>
|
||||
<w:tab w:val='right' w:leader='dot' w:pos='{1}'/>
|
||||
</w:tabs>
|
||||
<w:rPr>
|
||||
<w:noProof/>
|
||||
</w:rPr>
|
||||
</w:pPr>
|
||||
<w:r>
|
||||
<w:fldChar w:fldCharType='begin' w:dirty='true'/>
|
||||
</w:r>
|
||||
<w:r>
|
||||
<w:instrText xml:space='preserve'> {2} </w:instrText>
|
||||
</w:r>
|
||||
<w:r>
|
||||
<w:fldChar w:fldCharType='separate'/>
|
||||
</w:r>
|
||||
</w:p>
|
||||
<w:p>
|
||||
<w:r>
|
||||
<w:rPr>
|
||||
<w:b/>
|
||||
<w:bCs/>
|
||||
<w:noProof/>
|
||||
</w:rPr>
|
||||
<w:fldChar w:fldCharType='end'/>
|
||||
</w:r>
|
||||
</w:p>
|
||||
</w:sdtContent>
|
||||
</w:sdt>";
|
||||
|
||||
XmlReader sdtReader = XmlReader.Create(new StringReader(String.Format(xmlString, title, rightTabPos, switches)));
|
||||
XElement sdt = XElement.Load(sdtReader);
|
||||
|
||||
XmlNamespaceManager namespaceManager;
|
||||
XDocument mainXDoc = doc.MainDocumentPart.GetXDocument(out namespaceManager);
|
||||
namespaceManager.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
|
||||
XElement addBefore = mainXDoc.XPathSelectElement(xPath, namespaceManager);
|
||||
if (addBefore == null)
|
||||
throw new OpenXmlPowerToolsException("XPath expression did not select an element");
|
||||
|
||||
addBefore.AddBeforeSelf(sdt);
|
||||
doc.MainDocumentPart.PutXDocument();
|
||||
|
||||
XDocument settingsXDoc = doc.MainDocumentPart.DocumentSettingsPart.GetXDocument();
|
||||
XElement updateFields = settingsXDoc.Descendants(W.updateFields).FirstOrDefault();
|
||||
if (updateFields != null)
|
||||
updateFields.Attribute(W.val).Value = "true";
|
||||
else
|
||||
{
|
||||
updateFields = new XElement(W.updateFields,
|
||||
new XAttribute(W.val, "true"));
|
||||
settingsXDoc.Root.Add(updateFields);
|
||||
}
|
||||
doc.MainDocumentPart.DocumentSettingsPart.PutXDocument();
|
||||
}
|
||||
|
||||
public static WmlDocument AddTof(WmlDocument document, string xPath, string switches, int? rightTabPos)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(document))
|
||||
{
|
||||
using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
AddTof(doc, xPath, switches, rightTabPos);
|
||||
}
|
||||
return streamDoc.GetModifiedWmlDocument();
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddTof(WordprocessingDocument doc, string xPath, string switches, int? rightTabPos)
|
||||
{
|
||||
UpdateFontTablePart(doc);
|
||||
UpdateStylesPartForTof(doc);
|
||||
UpdateStylesWithEffectsPartForTof(doc);
|
||||
|
||||
if (rightTabPos == null)
|
||||
rightTabPos = 9350;
|
||||
|
||||
// {0} rightTabPosition (default = 9350)
|
||||
// {1} switches
|
||||
|
||||
string xmlString =
|
||||
@"<w:p xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
|
||||
<w:pPr>
|
||||
<w:pStyle w:val='TableofFigures'/>
|
||||
<w:tabs>
|
||||
<w:tab w:val='right' w:leader='dot' w:pos='{0}'/>
|
||||
</w:tabs>
|
||||
<w:rPr>
|
||||
<w:noProof/>
|
||||
</w:rPr>
|
||||
</w:pPr>
|
||||
<w:r>
|
||||
<w:fldChar w:fldCharType='begin' dirty='true'/>
|
||||
</w:r>
|
||||
<w:r>
|
||||
<w:instrText xml:space='preserve'> {1} </w:instrText>
|
||||
</w:r>
|
||||
<w:r>
|
||||
<w:fldChar w:fldCharType='separate'/>
|
||||
</w:r>
|
||||
<w:r>
|
||||
<w:fldChar w:fldCharType='end'/>
|
||||
</w:r>
|
||||
</w:p>";
|
||||
XDocument mainXDoc = doc.MainDocumentPart.GetXDocument();
|
||||
|
||||
XmlReader paragraphReader = XmlReader.Create(new StringReader(String.Format(xmlString, rightTabPos, switches)));
|
||||
XElement paragraph = XElement.Load(paragraphReader);
|
||||
XmlNameTable nameTable = paragraphReader.NameTable;
|
||||
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(nameTable);
|
||||
namespaceManager.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
|
||||
XElement addBefore = mainXDoc.XPathSelectElement(xPath, namespaceManager);
|
||||
if (addBefore == null)
|
||||
throw new OpenXmlPowerToolsException("XPath expression did not select an element");
|
||||
|
||||
addBefore.AddBeforeSelf(paragraph);
|
||||
doc.MainDocumentPart.PutXDocument();
|
||||
|
||||
XDocument settingsXDoc = doc.MainDocumentPart.DocumentSettingsPart.GetXDocument();
|
||||
XElement updateFields = settingsXDoc.Descendants(W.updateFields).FirstOrDefault();
|
||||
if (updateFields != null)
|
||||
updateFields.Attribute(W.val).Value = "true";
|
||||
else
|
||||
{
|
||||
updateFields = new XElement(W.updateFields,
|
||||
new XAttribute(W.val, "true"));
|
||||
settingsXDoc.Root.Add(updateFields);
|
||||
}
|
||||
doc.MainDocumentPart.DocumentSettingsPart.PutXDocument();
|
||||
}
|
||||
|
||||
public static WmlDocument AddToa(WmlDocument document, string xPath, string switches, int? rightTabPos)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(document))
|
||||
{
|
||||
using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
AddToa(doc, xPath, switches, rightTabPos);
|
||||
}
|
||||
return streamDoc.GetModifiedWmlDocument();
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddToa(WordprocessingDocument doc, string xPath, string switches, int? rightTabPos)
|
||||
{
|
||||
UpdateFontTablePart(doc);
|
||||
UpdateStylesPartForToa(doc);
|
||||
UpdateStylesWithEffectsPartForToa(doc);
|
||||
|
||||
if (rightTabPos == null)
|
||||
rightTabPos = 9350;
|
||||
|
||||
// {0} rightTabPosition (default = 9350)
|
||||
// {1} switches
|
||||
|
||||
string xmlString =
|
||||
@"<w:p xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
|
||||
<w:pPr>
|
||||
<w:pStyle w:val='TOAHeading'/>
|
||||
<w:tabs>
|
||||
<w:tab w:val='right'
|
||||
w:leader='dot'
|
||||
w:pos='{0}'/>
|
||||
</w:tabs>
|
||||
<w:rPr>
|
||||
<w:rFonts w:asciiTheme='minorHAnsi'
|
||||
w:eastAsiaTheme='minorEastAsia'
|
||||
w:hAnsiTheme='minorHAnsi'
|
||||
w:cstheme='minorBidi'/>
|
||||
<w:b w:val='0'/>
|
||||
<w:bCs w:val='0'/>
|
||||
<w:noProof/>
|
||||
<w:sz w:val='22'/>
|
||||
<w:szCs w:val='22'/>
|
||||
</w:rPr>
|
||||
</w:pPr>
|
||||
<w:r>
|
||||
<w:fldChar w:fldCharType='begin'/>
|
||||
</w:r>
|
||||
<w:r>
|
||||
<w:instrText xml:space='preserve'> {1} </w:instrText>
|
||||
</w:r>
|
||||
<w:r>
|
||||
<w:fldChar w:fldCharType='separate'/>
|
||||
</w:r>
|
||||
<w:r>
|
||||
<w:fldChar w:fldCharType='end'/>
|
||||
</w:r>
|
||||
</w:p>";
|
||||
|
||||
XDocument mainXDoc = doc.MainDocumentPart.GetXDocument();
|
||||
|
||||
XmlReader paragraphReader = XmlReader.Create(new StringReader(String.Format(xmlString, rightTabPos, switches)));
|
||||
XElement paragraph = XElement.Load(paragraphReader);
|
||||
XmlNameTable nameTable = paragraphReader.NameTable;
|
||||
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(nameTable);
|
||||
namespaceManager.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
|
||||
XElement addBefore = mainXDoc.XPathSelectElement(xPath, namespaceManager);
|
||||
if (addBefore == null)
|
||||
throw new OpenXmlPowerToolsException("XPath expression did not select an element");
|
||||
|
||||
addBefore.AddBeforeSelf(paragraph);
|
||||
doc.MainDocumentPart.PutXDocument();
|
||||
|
||||
XDocument settingsXDoc = doc.MainDocumentPart.DocumentSettingsPart.GetXDocument();
|
||||
XElement updateFields = settingsXDoc.Descendants(W.updateFields).FirstOrDefault();
|
||||
if (updateFields != null)
|
||||
updateFields.Attribute(W.val).Value = "true";
|
||||
else
|
||||
{
|
||||
updateFields = new XElement(W.updateFields,
|
||||
new XAttribute(W.val, "true"));
|
||||
settingsXDoc.Root.Add(updateFields);
|
||||
}
|
||||
doc.MainDocumentPart.DocumentSettingsPart.PutXDocument();
|
||||
}
|
||||
|
||||
private static void AddElementIfMissing(XDocument partXDoc, XElement existing, string newElement)
|
||||
{
|
||||
if (existing != null)
|
||||
return;
|
||||
XElement newXElement = XElement.Parse(newElement);
|
||||
newXElement.Attributes().Where(a => a.IsNamespaceDeclaration).Remove();
|
||||
partXDoc.Root.Add(newXElement);
|
||||
}
|
||||
|
||||
private static void UpdateFontTablePart(WordprocessingDocument doc)
|
||||
{
|
||||
FontTablePart fontTablePart = doc.MainDocumentPart.FontTablePart;
|
||||
if (fontTablePart == null)
|
||||
throw new Exception("Todo need to insert font table part");
|
||||
XDocument fontTableXDoc = fontTablePart.GetXDocument();
|
||||
|
||||
AddElementIfMissing(fontTableXDoc,
|
||||
fontTableXDoc
|
||||
.Root
|
||||
.Elements(W.font)
|
||||
.Where(e => (string)e.Attribute(W.name) == "Tahoma")
|
||||
.FirstOrDefault(),
|
||||
@"<w:font w:name='Tahoma' xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
|
||||
<w:panose1 w:val='020B0604030504040204'/>
|
||||
<w:charset w:val='00'/>
|
||||
<w:family w:val='swiss'/>
|
||||
<w:pitch w:val='variable'/>
|
||||
<w:sig w:usb0='E1002EFF' w:usb1='C000605B' w:usb2='00000029' w:usb3='00000000' w:csb0='000101FF' w:csb1='00000000'/>
|
||||
</w:font>");
|
||||
|
||||
fontTablePart.PutXDocument();
|
||||
}
|
||||
|
||||
private static void UpdatePartForToc(OpenXmlPart part)
|
||||
{
|
||||
XDocument xDoc = part.GetXDocument();
|
||||
|
||||
AddElementIfMissing(
|
||||
xDoc,
|
||||
xDoc.Root.Elements(W.style)
|
||||
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "TOCHeading")
|
||||
.FirstOrDefault(),
|
||||
@"<w:style w:type='paragraph' w:styleId='TOCHeading' xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
|
||||
<w:name w:val='TOC Heading'/>
|
||||
<w:basedOn w:val='Heading1'/>
|
||||
<w:next w:val='Normal'/>
|
||||
<w:uiPriority w:val='39'/>
|
||||
<w:semiHidden/>
|
||||
<w:unhideWhenUsed/>
|
||||
<w:qFormat/>
|
||||
<w:pPr>
|
||||
<w:outlineLvl w:val='9'/>
|
||||
</w:pPr>
|
||||
<w:rPr>
|
||||
<w:lang w:eastAsia='ja-JP'/>
|
||||
</w:rPr>
|
||||
</w:style>");
|
||||
|
||||
AddElementIfMissing(
|
||||
xDoc,
|
||||
xDoc.Root.Elements(W.style)
|
||||
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "TOC1")
|
||||
.FirstOrDefault(),
|
||||
@"<w:style w:type='paragraph' w:styleId='TOC1' xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
|
||||
<w:name w:val='toc 1'/>
|
||||
<w:basedOn w:val='Normal'/>
|
||||
<w:next w:val='Normal'/>
|
||||
<w:autoRedefine/>
|
||||
<w:uiPriority w:val='39'/>
|
||||
<w:unhideWhenUsed/>
|
||||
<w:pPr>
|
||||
<w:spacing w:after='100'/>
|
||||
</w:pPr>
|
||||
</w:style>");
|
||||
|
||||
AddElementIfMissing(
|
||||
xDoc,
|
||||
xDoc.Root.Elements(W.style)
|
||||
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "TOC2")
|
||||
.FirstOrDefault(),
|
||||
@"<w:style w:type='paragraph' w:styleId='TOC2' xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
|
||||
<w:name w:val='toc 2'/>
|
||||
<w:basedOn w:val='Normal'/>
|
||||
<w:next w:val='Normal'/>
|
||||
<w:autoRedefine/>
|
||||
<w:uiPriority w:val='39'/>
|
||||
<w:unhideWhenUsed/>
|
||||
<w:pPr>
|
||||
<w:spacing w:after='100'/>
|
||||
<w:ind w:left='220'/>
|
||||
</w:pPr>
|
||||
</w:style>");
|
||||
|
||||
AddElementIfMissing(
|
||||
xDoc,
|
||||
xDoc.Root.Elements(W.style)
|
||||
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "TOC3")
|
||||
.FirstOrDefault(),
|
||||
@"<w:style w:type='paragraph' w:styleId='TOC3' xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
|
||||
<w:name w:val='toc 3'/>
|
||||
<w:basedOn w:val='Normal'/>
|
||||
<w:next w:val='Normal'/>
|
||||
<w:autoRedefine/>
|
||||
<w:uiPriority w:val='39'/>
|
||||
<w:unhideWhenUsed/>
|
||||
<w:pPr>
|
||||
<w:spacing w:after='100'/>
|
||||
<w:ind w:left='440'/>
|
||||
</w:pPr>
|
||||
</w:style>");
|
||||
|
||||
AddElementIfMissing(
|
||||
xDoc,
|
||||
xDoc.Root.Elements(W.style)
|
||||
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "TOC4")
|
||||
.FirstOrDefault(),
|
||||
@"<w:style w:type='paragraph' w:styleId='TOC4' xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
|
||||
<w:name w:val='toc 4'/>
|
||||
<w:basedOn w:val='Normal'/>
|
||||
<w:next w:val='Normal'/>
|
||||
<w:autoRedefine/>
|
||||
<w:uiPriority w:val='39'/>
|
||||
<w:unhideWhenUsed/>
|
||||
<w:pPr>
|
||||
<w:spacing w:after='100'/>
|
||||
<w:ind w:left='660'/>
|
||||
</w:pPr>
|
||||
</w:style>");
|
||||
|
||||
AddElementIfMissing(
|
||||
xDoc,
|
||||
xDoc.Root.Elements(W.style)
|
||||
.Where(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Hyperlink")
|
||||
.FirstOrDefault(),
|
||||
@"<w:style w:type='character' w:styleId='Hyperlink' xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
|
||||
<w:name w:val='Hyperlink'/>
|
||||
<w:basedOn w:val='DefaultParagraphFont'/>
|
||||
<w:uiPriority w:val='99'/>
|
||||
<w:unhideWhenUsed/>
|
||||
<w:rPr>
|
||||
<w:color w:val='0000FF' w:themeColor='hyperlink'/>
|
||||
<w:u w:val='single'/>
|
||||
</w:rPr>
|
||||
</w:style>");
|
||||
|
||||
AddElementIfMissing(
|
||||
xDoc,
|
||||
xDoc.Root.Elements(W.style)
|
||||
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "BalloonText")
|
||||
.FirstOrDefault(),
|
||||
@"<w:style w:type='paragraph' w:styleId='BalloonText' xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
|
||||
<w:name w:val='Balloon Text'/>
|
||||
<w:basedOn w:val='Normal'/>
|
||||
<w:link w:val='BalloonTextChar'/>
|
||||
<w:uiPriority w:val='99'/>
|
||||
<w:semiHidden/>
|
||||
<w:unhideWhenUsed/>
|
||||
<w:pPr>
|
||||
<w:spacing w:after='0' w:line='240' w:lineRule='auto'/>
|
||||
</w:pPr>
|
||||
<w:rPr>
|
||||
<w:rFonts w:ascii='Tahoma' w:hAnsi='Tahoma' w:cs='Tahoma'/>
|
||||
<w:sz w:val='16'/>
|
||||
<w:szCs w:val='16'/>
|
||||
</w:rPr>
|
||||
</w:style>");
|
||||
|
||||
AddElementIfMissing(
|
||||
xDoc,
|
||||
xDoc.Root.Elements(W.style)
|
||||
.Where(e => (string)e.Attribute(W.type) == "character" &&
|
||||
(bool?)e.Attribute(W.customStyle) == true && (string)e.Attribute(W.styleId) == "BalloonTextChar")
|
||||
.FirstOrDefault(),
|
||||
@"<w:style w:type='character' w:customStyle='1' w:styleId='BalloonTextChar' xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
|
||||
<w:name w:val='Balloon Text Char'/>
|
||||
<w:basedOn w:val='DefaultParagraphFont'/>
|
||||
<w:link w:val='BalloonText'/>
|
||||
<w:uiPriority w:val='99'/>
|
||||
<w:semiHidden/>
|
||||
<w:rPr>
|
||||
<w:rFonts w:ascii='Tahoma' w:hAnsi='Tahoma' w:cs='Tahoma'/>
|
||||
<w:sz w:val='16'/>
|
||||
<w:szCs w:val='16'/>
|
||||
</w:rPr>
|
||||
</w:style>");
|
||||
|
||||
part.PutXDocument();
|
||||
}
|
||||
|
||||
private static void UpdateStylesPartForToc(WordprocessingDocument doc)
|
||||
{
|
||||
StylesPart stylesPart = doc.MainDocumentPart.StyleDefinitionsPart;
|
||||
if (stylesPart == null)
|
||||
return;
|
||||
UpdatePartForToc(stylesPart);
|
||||
}
|
||||
|
||||
private static void UpdateStylesWithEffectsPartForToc(WordprocessingDocument doc)
|
||||
{
|
||||
StylesWithEffectsPart stylesWithEffectsPart = doc.MainDocumentPart.StylesWithEffectsPart;
|
||||
if (stylesWithEffectsPart == null)
|
||||
return;
|
||||
UpdatePartForToc(stylesWithEffectsPart);
|
||||
}
|
||||
|
||||
private static void UpdatePartForTof(OpenXmlPart part)
|
||||
{
|
||||
XDocument xDoc = part.GetXDocument();
|
||||
|
||||
AddElementIfMissing(
|
||||
xDoc,
|
||||
xDoc.Root.Elements(W.style)
|
||||
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "TableofFigures")
|
||||
.FirstOrDefault(),
|
||||
@"<w:style w:type='paragraph' w:styleId='TableofFigures' xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
|
||||
<w:name w:val='table of figures'/>
|
||||
<w:basedOn w:val='Normal'/>
|
||||
<w:next w:val='Normal'/>
|
||||
<w:uiPriority w:val='99'/>
|
||||
<w:unhideWhenUsed/>
|
||||
<w:pPr>
|
||||
<w:spacing w:after='0'/>
|
||||
</w:pPr>
|
||||
</w:style>");
|
||||
|
||||
AddElementIfMissing(
|
||||
xDoc,
|
||||
xDoc.Root.Elements(W.style)
|
||||
.Where(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Hyperlink")
|
||||
.FirstOrDefault(),
|
||||
@"<w:style w:type='character' w:styleId='Hyperlink' xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
|
||||
<w:name w:val='Hyperlink'/>
|
||||
<w:basedOn w:val='DefaultParagraphFont'/>
|
||||
<w:uiPriority w:val='99'/>
|
||||
<w:unhideWhenUsed/>
|
||||
<w:rPr>
|
||||
<w:color w:val='0000FF' w:themeColor='hyperlink'/>
|
||||
<w:u w:val='single'/>
|
||||
</w:rPr>
|
||||
</w:style>");
|
||||
part.PutXDocument();
|
||||
}
|
||||
|
||||
private static void UpdateStylesPartForTof(WordprocessingDocument doc)
|
||||
{
|
||||
StylesPart stylesPart = doc.MainDocumentPart.StyleDefinitionsPart;
|
||||
if (stylesPart == null)
|
||||
return;
|
||||
UpdatePartForTof(stylesPart);
|
||||
}
|
||||
|
||||
private static void UpdateStylesWithEffectsPartForTof(WordprocessingDocument doc)
|
||||
{
|
||||
StylesWithEffectsPart stylesWithEffectsPart = doc.MainDocumentPart.StylesWithEffectsPart;
|
||||
if (stylesWithEffectsPart == null)
|
||||
return;
|
||||
UpdatePartForTof(stylesWithEffectsPart);
|
||||
}
|
||||
|
||||
private static void UpdatePartForToa(OpenXmlPart part)
|
||||
{
|
||||
XDocument xDoc = part.GetXDocument();
|
||||
|
||||
AddElementIfMissing(
|
||||
xDoc,
|
||||
xDoc.Root.Elements(W.style)
|
||||
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "TableofAuthorities")
|
||||
.FirstOrDefault(),
|
||||
@"<w:style w:type='paragraph'
|
||||
w:styleId='TableofAuthorities'
|
||||
xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
|
||||
<w:name w:val='table of authorities'/>
|
||||
<w:basedOn w:val='Normal'/>
|
||||
<w:next w:val='Normal'/>
|
||||
<w:uiPriority w:val='99'/>
|
||||
<w:semiHidden/>
|
||||
<w:unhideWhenUsed/>
|
||||
<w:rsid w:val='0090569D'/>
|
||||
<w:pPr>
|
||||
<w:spacing w:after='0'/>
|
||||
<w:ind w:left='220'
|
||||
w:hanging='220'/>
|
||||
</w:pPr>
|
||||
</w:style>");
|
||||
|
||||
AddElementIfMissing(
|
||||
xDoc,
|
||||
xDoc.Root.Elements(W.style)
|
||||
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "TOAHeading")
|
||||
.FirstOrDefault(),
|
||||
@"<w:style w:type='paragraph'
|
||||
w:styleId='TOAHeading'
|
||||
xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
|
||||
<w:name w:val='toa heading'/>
|
||||
<w:basedOn w:val='Normal'/>
|
||||
<w:next w:val='Normal'/>
|
||||
<w:uiPriority w:val='99'/>
|
||||
<w:semiHidden/>
|
||||
<w:unhideWhenUsed/>
|
||||
<w:rsid w:val='0090569D'/>
|
||||
<w:pPr>
|
||||
<w:spacing w:before='120'/>
|
||||
</w:pPr>
|
||||
<w:rPr>
|
||||
<w:rFonts w:asciiTheme='majorHAnsi'
|
||||
w:eastAsiaTheme='majorEastAsia'
|
||||
w:hAnsiTheme='majorHAnsi'
|
||||
w:cstheme='majorBidi'/>
|
||||
<w:b/>
|
||||
<w:bCs/>
|
||||
<w:sz w:val='24'/>
|
||||
<w:szCs w:val='24'/>
|
||||
</w:rPr>
|
||||
</w:style>");
|
||||
|
||||
part.PutXDocument();
|
||||
}
|
||||
|
||||
private static void UpdateStylesPartForToa(WordprocessingDocument doc)
|
||||
{
|
||||
StylesPart stylesPart = doc.MainDocumentPart.StyleDefinitionsPart;
|
||||
if (stylesPart == null)
|
||||
return;
|
||||
UpdatePartForToa(stylesPart);
|
||||
}
|
||||
|
||||
private static void UpdateStylesWithEffectsPartForToa(WordprocessingDocument doc)
|
||||
{
|
||||
StylesWithEffectsPart stylesWithEffectsPart = doc.MainDocumentPart.StylesWithEffectsPart;
|
||||
if (stylesWithEffectsPart == null)
|
||||
return;
|
||||
UpdatePartForToa(stylesWithEffectsPart);
|
||||
}
|
||||
}
|
||||
}
|
1433
omegapro/PowerTools/Classes/RevisionAccepter.cs
Normal file
1433
omegapro/PowerTools/Classes/RevisionAccepter.cs
Normal file
File diff suppressed because it is too large
Load Diff
84
omegapro/PowerTools/Classes/SettingAccessor.cs
Normal file
84
omegapro/PowerTools/Classes/SettingAccessor.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to setting operations
|
||||
/// </summary>
|
||||
public class SettingAccessor
|
||||
{
|
||||
private static XNamespace ns;
|
||||
private static XNamespace settingsns;
|
||||
private static XNamespace relationshipns;
|
||||
|
||||
static SettingAccessor()
|
||||
{
|
||||
ns = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
||||
settingsns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings";
|
||||
relationshipns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Nodes list with displayBackgroundShape elements
|
||||
/// </summary>
|
||||
public static XElement DisplayBackgroundShapeElements(WordprocessingDocument document)
|
||||
{
|
||||
XDocument settingsDocument = document.MainDocumentPart.DocumentSettingsPart.GetXDocument();
|
||||
return settingsDocument.Descendants(settingsns + "displayBackgroundShape").FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a displayBackgroundShape element to the settings file
|
||||
/// </summary>
|
||||
public static void AddBackgroundShapeElement(WordprocessingDocument document)
|
||||
{
|
||||
XDocument settingsDocument = document.MainDocumentPart.DocumentSettingsPart.GetXDocument();
|
||||
settingsDocument.Root.Add(
|
||||
new XElement(ns + "displayBackgroundShape")
|
||||
);
|
||||
document.MainDocumentPart.DocumentSettingsPart.PutXDocument();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a the evenAndOddHeaders element, which allows to define distinct headers and footers for odd and even pages
|
||||
/// </summary>
|
||||
public static void AddEvenAndOddHeadersElement(WordprocessingDocument document)
|
||||
{
|
||||
XDocument settingsDocument = document.MainDocumentPart.DocumentSettingsPart.GetXDocument();
|
||||
if (settingsDocument.Descendants(ns + "evenAndOddHeaders").FirstOrDefault() == null)
|
||||
{
|
||||
settingsDocument.Root.Add(
|
||||
new XElement(ns + "evenAndOddHeaders"));
|
||||
document.MainDocumentPart.DocumentSettingsPart.PutXDocument();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an empty base structure for a settings part
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static XDocument CreateEmptySettings()
|
||||
{
|
||||
XDocument document =
|
||||
new XDocument(
|
||||
new XElement(ns + "settings",
|
||||
new XAttribute(XNamespace.Xmlns + "w", ns),
|
||||
new XAttribute(XNamespace.Xmlns + "r", relationshipns)
|
||||
)
|
||||
);
|
||||
return document;
|
||||
}
|
||||
}
|
||||
}
|
96
omegapro/PowerTools/Classes/SpreadSheetStyleAccessor.cs
Normal file
96
omegapro/PowerTools/Classes/SpreadSheetStyleAccessor.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to manage the Style Part for an SpreadSheetML document
|
||||
/// </summary>
|
||||
public class SpreadSheetStyleAccessor
|
||||
{
|
||||
private static XNamespace ns;
|
||||
|
||||
/// <summary>
|
||||
/// Static constructor
|
||||
/// </summary>
|
||||
static SpreadSheetStyleAccessor()
|
||||
{
|
||||
ns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// XDocument containing Xml content of the styles part
|
||||
/// </summary>
|
||||
public static XDocument GetStylesDocument(SpreadsheetDocument document)
|
||||
{
|
||||
if (document.WorkbookPart.WorkbookStylesPart != null)
|
||||
return document.WorkbookPart.WorkbookStylesPart.GetXDocument();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a new styles part inside the document
|
||||
/// </summary>
|
||||
/// <param name="newStylesDocument">Path of styles definition file</param>
|
||||
public static void SetStylePart(SpreadsheetDocument document, XDocument newStylesDocument)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Replaces XDocument with the style file to transfer
|
||||
XDocument stylesDocument = GetStylesDocument(document);
|
||||
if (stylesDocument == null)
|
||||
{
|
||||
WorkbookStylesPart stylesPart =
|
||||
document.WorkbookPart.AddNewPart<WorkbookStylesPart>();
|
||||
stylesDocument = stylesPart.GetXDocument();
|
||||
}
|
||||
if (stylesDocument.Root == null)
|
||||
stylesDocument.Add(newStylesDocument.Root);
|
||||
else
|
||||
stylesDocument.Root.ReplaceWith(newStylesDocument.Root);
|
||||
document.WorkbookPart.WorkbookStylesPart.PutXDocument();
|
||||
}
|
||||
catch (XmlException ex)
|
||||
{
|
||||
throw new XmlException("File specified is not a valid XML file", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the index inside the style part for a specific cell style
|
||||
/// </summary>
|
||||
/// <param name="cellStyle">Name for cell style to return the index of</param>
|
||||
/// <returns></returns>
|
||||
public static int GetCellStyleIndex(SpreadsheetDocument document, string cellStyle)
|
||||
{
|
||||
XDocument stylesXDocument = GetStylesDocument(document);
|
||||
if (stylesXDocument == null)
|
||||
return -1;
|
||||
var cellStyleXElement =
|
||||
stylesXDocument.Root
|
||||
.Element(ns + "cellStyles")
|
||||
.Elements(ns + "cellStyle")
|
||||
.Where(c=> c.Attribute("name").Value.ToLower().Equals(cellStyle.ToLower())).FirstOrDefault<XElement>();
|
||||
|
||||
if (cellStyleXElement != null)
|
||||
{
|
||||
return System.Convert.ToInt32(cellStyleXElement.Attribute("xfId").Value);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
180
omegapro/PowerTools/Classes/SpreadSheetTableAccessor.cs
Normal file
180
omegapro/PowerTools/Classes/SpreadSheetTableAccessor.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
using System;
|
||||
using System.Xml;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for working with the Table Definition Part for a SpreadsheetML document
|
||||
/// </summary>
|
||||
public class SpreadSheetTableAccessor
|
||||
{
|
||||
private static XNamespace ns;
|
||||
private static XNamespace relationshipns;
|
||||
|
||||
/// <summary>
|
||||
/// Static constructor
|
||||
/// </summary>
|
||||
static SpreadSheetTableAccessor()
|
||||
{
|
||||
ns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
|
||||
relationshipns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method for adding a new table definition part
|
||||
/// </summary>
|
||||
/// <param name="worksheet">Worksheet to add the table to</param>
|
||||
/// <param name="tableStyle">Style to be assigned to the table</param>
|
||||
/// <param name="useHeaders">Set a header row</param>
|
||||
/// <param name="fromColumn">Initial column for table</param>
|
||||
/// <param name="toColumn">Final column for table</param>
|
||||
/// <param name="fromRow">Intial row for table</param>
|
||||
/// <param name="toRow">Final row for table</param>
|
||||
public static OpenXmlPowerToolsDocument Add(SmlDocument doc, string worksheetName, string tableStyle, bool useHeaders, short fromColumn, short toColumn, int fromRow, int toRow)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (SpreadsheetDocument document = streamDoc.GetSpreadsheetDocument())
|
||||
{
|
||||
//Getting the id for this table
|
||||
int tableId = GetNextTableId(document);
|
||||
|
||||
//Set the table cell range
|
||||
string tableRange = string.Format("{0}{1}:{2}{3}", WorksheetAccessor.GetColumnId(fromColumn),
|
||||
fromRow,
|
||||
WorksheetAccessor.GetColumnId(toColumn),
|
||||
toRow);
|
||||
|
||||
//Creating a new id for the relationship between the table definition part and the worksheet
|
||||
string tableRelationShipId = "rId" + Guid.NewGuid();
|
||||
|
||||
//Create a new table definition part
|
||||
WorksheetPart worksheet = WorksheetAccessor.Get(document, worksheetName);
|
||||
TableDefinitionPart table = worksheet.AddNewPart<TableDefinitionPart>(tableRelationShipId);
|
||||
|
||||
//string tableColumns = string.Empty;
|
||||
XElement tableColumnsXElement = new XElement(ns + "tableColumns", new XAttribute("count", (toColumn - fromColumn) + 1));
|
||||
//Get the name for table column elements from the first table row
|
||||
string[] tableHeaders = GetTableHeaders(document, worksheet, fromRow, fromColumn, toColumn);
|
||||
for (int i = 0; i <= (toColumn - fromColumn); i++)
|
||||
{
|
||||
//Create the markup for the SpreadsheetML table column elements
|
||||
tableColumnsXElement.Add(
|
||||
new XElement(ns + "tableColumn", new XAttribute("id", i + 1), new XAttribute("name", tableHeaders[i])));
|
||||
|
||||
}
|
||||
|
||||
XElement tableXElement =
|
||||
new XElement(ns + "table",
|
||||
new XAttribute("xmlns", ns), //default namespace
|
||||
new XAttribute("id", tableId),
|
||||
new XAttribute("name", "Table" + tableId.ToString()),
|
||||
new XAttribute("displayName", "Table" + tableId.ToString()),
|
||||
new XAttribute("ref", tableRange),
|
||||
new XAttribute("totalsRowShown", "0"));
|
||||
|
||||
if (useHeaders)
|
||||
{
|
||||
tableXElement.Add(
|
||||
new XElement(ns + "autoFilter", new XAttribute("ref", tableRange)));
|
||||
}
|
||||
|
||||
tableXElement.Add(tableColumnsXElement);
|
||||
|
||||
tableXElement.Add(
|
||||
new XElement(ns + "tableStyleInfo",
|
||||
new XAttribute("name", tableStyle),
|
||||
new XAttribute("showFirstColumn", "0"),
|
||||
new XAttribute("showLastColumn", "0"),
|
||||
new XAttribute("showRowStripes", "0"),
|
||||
new XAttribute("showColumnStripes", "0")));
|
||||
|
||||
//Write the markup to the Table Definition Part Stream
|
||||
XmlWriter tablePartStreamWriter = XmlWriter.Create(table.GetStream());
|
||||
tableXElement.WriteTo(tablePartStreamWriter);
|
||||
|
||||
tablePartStreamWriter.Flush();
|
||||
tablePartStreamWriter.Close();
|
||||
|
||||
//Create or modify the table parts definition at worksheet (for setting the relationship id with the new table)
|
||||
XDocument worksheetMarkup = worksheet.GetXDocument();
|
||||
//Look for the tableParts element at worksheet markup
|
||||
XElement tablePartsElement = worksheetMarkup.Root.Element(ns + "tableParts");
|
||||
if (tablePartsElement != null)
|
||||
{
|
||||
//tableParts elements does exist at worksheet markup
|
||||
//increment the tableParts count attribute value
|
||||
short tableCount = System.Convert.ToInt16(tablePartsElement.Attribute("count").Value);
|
||||
tablePartsElement.SetAttributeValue("count", tableCount++.ToString());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//tableParts does not exist at worksheet markup
|
||||
//create a new tableParts element
|
||||
tablePartsElement = new XElement(ns + "tableParts",
|
||||
new XAttribute(ns + "count", "1"));
|
||||
|
||||
worksheetMarkup.Root.Add(tablePartsElement);
|
||||
}
|
||||
|
||||
//create the tablePart element
|
||||
XElement tablePartEntryElement = new XElement(ns + "tablePart",
|
||||
new XAttribute(relationshipns + "id", tableRelationShipId));
|
||||
|
||||
//add the new tablePart element to the worksheet tableParts element
|
||||
tablePartsElement.Add(tablePartEntryElement);
|
||||
worksheet.PutXDocument();
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the id for the next table to add to the SpreadSheetML document
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static int GetNextTableId(SpreadsheetDocument document)
|
||||
{
|
||||
int tableCount = 0;
|
||||
foreach (WorksheetPart worksheet in document.WorkbookPart.WorksheetParts)
|
||||
{ //Loop the worksheets to sum up the tables defined in each one
|
||||
tableCount += worksheet.TableDefinitionParts.Count();
|
||||
}
|
||||
return ++tableCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the values for the table header row (table initial row)
|
||||
/// </summary>
|
||||
/// <param name="worksheet">Worksheet where the table is being defined</param>
|
||||
/// <param name="row">Table initial row</param>
|
||||
/// <param name="fromColumn">Table initial column</param>
|
||||
/// <param name="toColumn">Table final column</param>
|
||||
/// <returns></returns>
|
||||
private static string[] GetTableHeaders(SpreadsheetDocument document, WorksheetPart worksheet, int row, short fromColumn, short toColumn)
|
||||
{
|
||||
List<string> tableHeaders = new List<string>();
|
||||
for (short c = fromColumn; c <= toColumn; c++)
|
||||
{
|
||||
tableHeaders.Add(WorksheetAccessor.GetValue(document, worksheet, c, row));
|
||||
}
|
||||
|
||||
return tableHeaders.ToArray<string>();
|
||||
}
|
||||
}
|
||||
}
|
216
omegapro/PowerTools/Classes/SpreadsheetDocumentManager.cs
Normal file
216
omegapro/PowerTools/Classes/SpreadsheetDocumentManager.cs
Normal file
@@ -0,0 +1,216 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages SpreadsheetDocument content
|
||||
/// </summary>
|
||||
public class SpreadsheetDocumentManager
|
||||
{
|
||||
private static XNamespace ns;
|
||||
private static XNamespace relationshipsns;
|
||||
private static int headerRow = 1;
|
||||
|
||||
static SpreadsheetDocumentManager()
|
||||
{
|
||||
ns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
|
||||
relationshipsns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a spreadsheet document from a value table
|
||||
/// </summary>
|
||||
/// <param name="filePath">Path to store the document</param>
|
||||
/// <param name="headerList">Contents of first row (header)</param>
|
||||
/// <param name="valueTable">Contents of data</param>
|
||||
/// <param name="initialRow">Row to start copying data from</param>
|
||||
/// <returns></returns>
|
||||
public static void Create(SpreadsheetDocument document, List<string> headerList, string[][] valueTable, int initialRow)
|
||||
{
|
||||
headerRow = initialRow;
|
||||
|
||||
//Creates a worksheet with given data
|
||||
WorksheetPart worksheet = WorksheetAccessor.Create(document, headerList, valueTable, headerRow);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a spreadsheet document with a chart from a value table
|
||||
/// </summary>
|
||||
/// <param name="filePath">Path to store the document</param>
|
||||
/// <param name="headerList">Contents of first row (header)</param>
|
||||
/// <param name="valueTable">Contents of data</param>
|
||||
/// <param name="chartType">Chart type</param>
|
||||
/// <param name="categoryColumn">Column to use as category for charting</param>
|
||||
/// <param name="columnsToChart">Columns to use as data series</param>
|
||||
/// <param name="initialRow">Row index to start copying data</param>
|
||||
/// <returns>SpreadsheetDocument</returns>
|
||||
public static void Create(SpreadsheetDocument document, List<string> headerList, string[][] valueTable, ChartType chartType, string categoryColumn, List<string> columnsToChart, int initialRow)
|
||||
{
|
||||
headerRow = initialRow;
|
||||
|
||||
//Creates worksheet with data
|
||||
WorksheetPart worksheet = WorksheetAccessor.Create(document, headerList, valueTable, headerRow);
|
||||
//Creates chartsheet with given series and category
|
||||
string sheetName = GetSheetName(worksheet, document);
|
||||
ChartsheetPart chartsheet =
|
||||
ChartsheetAccessor.Create(document,
|
||||
chartType,
|
||||
GetValueReferences(sheetName, categoryColumn, headerList, columnsToChart, valueTable),
|
||||
GetHeaderReferences(sheetName, categoryColumn, headerList, columnsToChart, valueTable),
|
||||
GetCategoryReference(sheetName, categoryColumn, headerList, valueTable)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal name of a worksheet from a document
|
||||
/// </summary>
|
||||
private static string GetSheetName(WorksheetPart worksheet, SpreadsheetDocument document)
|
||||
{
|
||||
//Gets the id of worksheet part
|
||||
string partId = document.WorkbookPart.GetIdOfPart(worksheet);
|
||||
XDocument workbookDocument = document.WorkbookPart.GetXDocument();
|
||||
//Gets the name from sheet tag related to worksheet
|
||||
string sheetName =
|
||||
workbookDocument.Root
|
||||
.Element(ns + "sheets")
|
||||
.Elements(ns + "sheet")
|
||||
.Where(
|
||||
t =>
|
||||
t.Attribute(relationshipsns + "id").Value == partId
|
||||
).First()
|
||||
.Attribute("name").Value;
|
||||
return sheetName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the range reference for category
|
||||
/// </summary>
|
||||
/// <param name="sheetName">worksheet to take data from</param>
|
||||
/// <param name="headerColumn">name of column used as category</param>
|
||||
/// <param name="headerList">column names from data</param>
|
||||
/// <param name="valueTable">Data values</param>
|
||||
/// <returns></returns>
|
||||
private static string GetCategoryReference(string sheetName, string headerColumn, List<string> headerList, string[][] valueTable)
|
||||
{
|
||||
int categoryColumn = headerList.IndexOf(headerColumn.ToUpper()) + 1;
|
||||
int numRows = valueTable.GetLength(0);
|
||||
|
||||
return GetRangeReference(
|
||||
sheetName,
|
||||
categoryColumn,
|
||||
headerRow + 1,
|
||||
categoryColumn,
|
||||
numRows + headerRow
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of range references for each of the series headers
|
||||
/// </summary>
|
||||
/// <param name="sheetName">worksheet to take data from</param>
|
||||
/// <param name="headerColumn">name of column used as category</param>
|
||||
/// <param name="headerList">column names from data</param>
|
||||
/// <param name="valueTable">Data values</param>
|
||||
/// <param name="colsToChart">Columns used as data series</param>
|
||||
/// <returns></returns>
|
||||
private static List<string> GetHeaderReferences(string sheetName, string headerColumn, List<string> headerList, List<string> colsToChart, string[][] valueTable)
|
||||
{
|
||||
List<string> valueReferenceList = new List<string>();
|
||||
|
||||
foreach (string column in colsToChart)
|
||||
{
|
||||
valueReferenceList.Add(
|
||||
GetRangeReference(
|
||||
sheetName,
|
||||
headerList.IndexOf(column.ToUpper()) + 1,
|
||||
headerRow
|
||||
)
|
||||
);
|
||||
}
|
||||
return valueReferenceList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of range references for each of the series values
|
||||
/// </summary>
|
||||
/// <param name="sheetName">worksheet to take data from</param>
|
||||
/// <param name="headerColumn">name of column used as category</param>
|
||||
/// <param name="headerList">column names from data</param>
|
||||
/// <param name="valueTable">Data values</param>
|
||||
/// <param name="colsToChart">Columns used as data series</param>
|
||||
/// <returns></returns>
|
||||
private static List<string> GetValueReferences(string sheetName, string headerColumn, List<string> headerList, List<string> colsToChart, string[][] valueTable)
|
||||
{
|
||||
List<string> valueReferenceList = new List<string>();
|
||||
int numRows = valueTable.GetLength(0);
|
||||
|
||||
foreach (string column in colsToChart)
|
||||
{
|
||||
int dataColumn = headerList.IndexOf(column.ToUpper()) + 1;
|
||||
valueReferenceList.Add(
|
||||
GetRangeReference(
|
||||
sheetName,
|
||||
dataColumn,
|
||||
headerRow + 1,
|
||||
dataColumn,
|
||||
numRows + headerRow
|
||||
)
|
||||
);
|
||||
}
|
||||
return valueReferenceList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a formatted representation of a cell range from a worksheet
|
||||
/// </summary>
|
||||
private static string GetRangeReference(string worksheet, int column, int row)
|
||||
{
|
||||
return string.Format("{0}!{1}{2}", worksheet, WorksheetAccessor.GetColumnId(column), row);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a formatted representation of a cell range from a worksheet
|
||||
/// </summary>
|
||||
private static string GetRangeReference(string worksheet, int startColumn, int startRow, int endColumn, int endRow)
|
||||
{
|
||||
return string.Format("{0}!{1}{2}:{3}{4}",
|
||||
worksheet,
|
||||
WorksheetAccessor.GetColumnId(startColumn),
|
||||
startRow,
|
||||
WorksheetAccessor.GetColumnId(endColumn),
|
||||
endRow
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an empty (base) workbook document
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static XDocument CreateEmptyWorkbook()
|
||||
{
|
||||
XDocument document =
|
||||
new XDocument(
|
||||
new XElement(ns + "workbook",
|
||||
new XAttribute("xmlns", ns),
|
||||
new XAttribute(XNamespace.Xmlns + "r", relationshipsns),
|
||||
new XElement(ns + "sheets")
|
||||
)
|
||||
);
|
||||
|
||||
return document;
|
||||
}
|
||||
}
|
||||
}
|
459
omegapro/PowerTools/Classes/StyleAccessor.cs
Normal file
459
omegapro/PowerTools/Classes/StyleAccessor.cs
Normal file
@@ -0,0 +1,459 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
using System.Collections.ObjectModel;
|
||||
using System;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to style operations
|
||||
/// </summary>
|
||||
public class StyleAccessor
|
||||
{
|
||||
//private XDocument xmlStylesDefinitionDocument;
|
||||
private static XNamespace ns;
|
||||
/// <summary>
|
||||
/// newStyleNameSuffic variable
|
||||
/// </summary>
|
||||
public static readonly string newStyleNameSuffix = "_1";
|
||||
|
||||
static StyleAccessor()
|
||||
{
|
||||
ns = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
||||
}
|
||||
|
||||
public static void CreateIndexStyles(WordprocessingDocument document, string stylesSourceFile, bool addDefaultStyles)
|
||||
{
|
||||
if (stylesSourceFile == "")
|
||||
{
|
||||
if (addDefaultStyles)
|
||||
{
|
||||
XElement Index1 = new XElement(ns + "style",
|
||||
new XAttribute(ns + "type", "paragraph"),
|
||||
new XAttribute(ns + "styleId", "Index1"),
|
||||
new XElement(ns + "name",
|
||||
new XAttribute(ns + "val", "index 1")),
|
||||
new XElement(ns + "basedOn",
|
||||
new XAttribute(ns + "val", "Normal")),
|
||||
new XElement(ns + "next",
|
||||
new XAttribute(ns + "val", "Normal")),
|
||||
new XElement(ns + "autoRedefine"),
|
||||
new XElement(ns + "semiHidden"),
|
||||
new XElement(ns + "unhideWhenUsed"),
|
||||
new XElement(ns + "pPr",
|
||||
new XElement(ns + "spacing",
|
||||
new XAttribute(ns + "after", "0"),
|
||||
new XAttribute(ns + "line", "240"),
|
||||
new XAttribute(ns + "lineRule", "auto")),
|
||||
new XElement(ns + "ind",
|
||||
new XAttribute(ns + "left", "220"),
|
||||
new XAttribute(ns + "hanging", "220"))));
|
||||
|
||||
AddStyleDefinition(document, Index1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// add the styles from the styles source file
|
||||
XDocument StyleXmlPart = GetStylesDocument(document);
|
||||
// the preffix must be empty, because the styles need to be recognized by the TOC
|
||||
XDocument stylesSource = XDocument.Load(stylesSourceFile);
|
||||
IEnumerable<XElement> IndexStyles = GetStyleHierarchy("Index1", stylesSource, string.Empty);
|
||||
AddStyleDefinition(document, IndexStyles);
|
||||
}
|
||||
document.MainDocumentPart.StyleDefinitionsPart.PutXDocument();
|
||||
}
|
||||
public static void CreateTOAStyles(WordprocessingDocument document, string stylesSourceFile, bool addDefaultStyles)
|
||||
{
|
||||
if (stylesSourceFile == "")
|
||||
{
|
||||
if (addDefaultStyles)
|
||||
{
|
||||
XElement TOAHeading = new XElement(ns + "style",
|
||||
new XAttribute(ns + "type", "paragraph"),
|
||||
new XAttribute(ns + "styleId", "TOAHeading"),
|
||||
new XElement(ns + "name",
|
||||
new XAttribute(ns + "val", "toa heading")),
|
||||
new XElement(ns + "basedOn",
|
||||
new XAttribute(ns + "val", "Normal")),
|
||||
new XElement(ns + "next",
|
||||
new XAttribute(ns + "val", "Normal")),
|
||||
new XElement(ns + "semiHidden"),
|
||||
new XElement(ns + "unhideWhenUsed"),
|
||||
new XElement(ns + "pPr",
|
||||
new XElement(ns + "spacing",
|
||||
new XAttribute(ns + "before", "120"))),
|
||||
new XElement(ns + "rPr",
|
||||
new XElement(ns + "b"),
|
||||
new XElement(ns + "bCs"),
|
||||
new XElement(ns + "sz",
|
||||
new XAttribute(ns + "val", 24)),
|
||||
new XElement(ns + "szCs",
|
||||
new XAttribute(ns + "val", 24))));
|
||||
|
||||
AddStyleDefinition(document, TOAHeading);
|
||||
|
||||
XElement tableOfAuthorities = new XElement(ns + "style",
|
||||
new XAttribute(ns + "type", "paragraph"),
|
||||
new XAttribute(ns + "styleId", "TableofAuthorities"),
|
||||
new XElement(ns + "name",
|
||||
new XAttribute(ns + "val", "table of authorities")),
|
||||
new XElement(ns + "basedOn",
|
||||
new XAttribute(ns + "val", "Normal")),
|
||||
new XElement(ns + "next",
|
||||
new XAttribute(ns + "val", "Normal")),
|
||||
new XElement(ns + "semiHidden"),
|
||||
new XElement(ns + "unhideWhenUsed"),
|
||||
new XElement(ns + "pPr",
|
||||
new XElement(ns + "spacing",
|
||||
new XAttribute(ns + "after", "0")),
|
||||
new XElement(ns + "ind",
|
||||
new XAttribute(ns + "left", "220"),
|
||||
new XAttribute(ns + "hanging", "220"))));
|
||||
|
||||
AddStyleDefinition(document, tableOfAuthorities);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// add the styles from the styles source file
|
||||
XDocument StyleXmlPart = GetStylesDocument(document);
|
||||
// the prefix must be empty, because the styles need to be recognized by the TOC
|
||||
XDocument stylesSource = XDocument.Load(stylesSourceFile);
|
||||
IEnumerable<XElement> TOAStyles = GetStyleHierarchy("TOAHeading", stylesSource, string.Empty);
|
||||
TOAStyles = TOAStyles.Concat(GetStyleHierarchy("TableofAuthorities", stylesSource, string.Empty));
|
||||
AddStyleDefinition(document, TOAStyles);
|
||||
}
|
||||
document.MainDocumentPart.StyleDefinitionsPart.PutXDocument();
|
||||
}
|
||||
private static XDocument GetStylesDocument(WordprocessingDocument document)
|
||||
{
|
||||
if (document.MainDocumentPart.StyleDefinitionsPart != null)
|
||||
return document.MainDocumentPart.StyleDefinitionsPart.GetXDocument();
|
||||
return null;
|
||||
}
|
||||
public static XDocument GetStylesDocument(WmlDocument doc)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
return GetStylesDocument(document);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the style hierarchy (link styles, next styles and basedOn styles) associated
|
||||
/// to the specified style, in a XElement collection
|
||||
/// </summary>
|
||||
/// <param name="styleName">Name of the style from where to get the hierarchy</param>
|
||||
/// <param name="stylesFile">File from where styles are taken </param>
|
||||
/// <param name="styleNameSuffix">Suffix of style name.</param>
|
||||
/// <returns>a collection containing the specified style and all the styles associated with it</returns>
|
||||
private static Collection<XElement> GetStyleHierarchy(string styleName, XDocument stylesFile, string styleNameSuffix)
|
||||
{
|
||||
try
|
||||
{
|
||||
Collection<XElement> stylesCollection = new Collection<XElement>();
|
||||
GetStyleHierarchyProcess(styleName, stylesFile, stylesCollection);
|
||||
|
||||
return stylesCollection;
|
||||
}
|
||||
catch (XmlException ex)
|
||||
{
|
||||
throw new ArgumentException("File specified is not a valid XML file", ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the xml of a specific style definition
|
||||
/// </summary>
|
||||
/// <param name="styleName">Name of the style to get from the styles file</param>
|
||||
/// <param name="xmlStyleDefinitions">Style definitions</param>
|
||||
/// <param name="stylesCollection">Collection of styles</param>
|
||||
private static void GetStyleHierarchyProcess(string styleName, XDocument xmlStyleDefinitions, Collection<XElement> stylesCollection)
|
||||
{
|
||||
XName style = ns + "style";
|
||||
XName styleId = ns + "styleId";
|
||||
|
||||
// the style name can come empty, because the given style could not have a link, basedOn or next style.
|
||||
// In those cases the stylename will come empty
|
||||
if (styleName != "")
|
||||
{
|
||||
// Creates a copy of the xmlStyleDefinition variable so the original xml will not be altered
|
||||
XElement actualStyle = new XElement(xmlStyleDefinitions.Root);
|
||||
actualStyle = actualStyle.Descendants().Where
|
||||
(
|
||||
tag =>
|
||||
(tag.Name == style) && (tag.Attribute(styleId).Value.ToUpper() == styleName.ToUpper())
|
||||
).ToList().FirstOrDefault();
|
||||
|
||||
if (actualStyle != null)
|
||||
{
|
||||
// Looks in the stylesCollection if the style has already been added
|
||||
IEnumerable<XElement> insertedStyles =
|
||||
stylesCollection.Where
|
||||
(
|
||||
tag =>
|
||||
(tag.Name == style) && (tag.Attribute(styleId).Value.ToUpper() == styleName.ToUpper())
|
||||
);
|
||||
|
||||
// If the style has not been inserted
|
||||
if (insertedStyles.Count() == 0)
|
||||
{
|
||||
stylesCollection.Add(actualStyle);
|
||||
GetStyleHierarchyProcess(GetLinkStyleId(actualStyle), xmlStyleDefinitions, stylesCollection);
|
||||
GetStyleHierarchyProcess(GetNextStyleId(actualStyle), xmlStyleDefinitions, stylesCollection);
|
||||
GetStyleHierarchyProcess(GetBasedOnStyleId(actualStyle), xmlStyleDefinitions, stylesCollection);
|
||||
|
||||
}
|
||||
// Changes the name of the style, so there would be no conflict with the original styles definition
|
||||
actualStyle.Attribute(styleId).Value = actualStyle.Attribute(styleId).Value + newStyleNameSuffix;
|
||||
}
|
||||
else
|
||||
throw new InvalidOperationException("Style or dependencies not found in the given style library.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the 'link' style associated to the given style
|
||||
/// </summary>
|
||||
/// <param name="xmlStyle">Xml to search for linked style</param>
|
||||
/// <returns>Name of the style</returns>
|
||||
private static string GetLinkStyleId(XElement xmlStyle)
|
||||
{
|
||||
XName val = ns + "val";
|
||||
string linkStyleId = "";
|
||||
XElement linkStyle = xmlStyle.Descendants(ns + "link").FirstOrDefault();
|
||||
if (linkStyle != null)
|
||||
{
|
||||
linkStyleId = linkStyle.Attribute(val).Value;
|
||||
// Changes the name of the attribute, because the new added style is being renamed
|
||||
linkStyle.Attribute(val).Value = linkStyle.Attribute(val).Value + newStyleNameSuffix;
|
||||
}
|
||||
return linkStyleId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the style tagged as 'next' associated to a given style
|
||||
/// </summary>
|
||||
/// <param name="xmlStyle">Xml to search for next style</param>
|
||||
/// <returns>Name of the style</returns>
|
||||
private static string GetNextStyleId(XElement xmlStyle)
|
||||
{
|
||||
XName val = ns + "val";
|
||||
string nextStyleId = "";
|
||||
XElement nextStyle = xmlStyle.Descendants(ns + "next").FirstOrDefault();
|
||||
if (nextStyle != null)
|
||||
{
|
||||
nextStyleId = nextStyle.Attribute(val).Value;
|
||||
// Changes the name of the attribute, because the new added style is being renamed
|
||||
nextStyle.Attribute(val).Value = nextStyle.Attribute(val).Value + newStyleNameSuffix;
|
||||
}
|
||||
return nextStyleId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the style tagged as 'basedOn' associated to a given style
|
||||
/// </summary>
|
||||
/// <param name="xmlStyle">Xml to search for basedOn style</param>
|
||||
/// <returns>Name of the style</returns>
|
||||
private static string GetBasedOnStyleId(XElement xmlStyle)
|
||||
{
|
||||
XName val = ns + "val";
|
||||
string basedOnStyleId = "";
|
||||
XElement basedOnStyle = xmlStyle.Descendants(ns + "basedOn").FirstOrDefault();
|
||||
if (basedOnStyle != null)
|
||||
{
|
||||
basedOnStyleId = basedOnStyle.Attribute(val).Value;
|
||||
// Change the name of the attribute, because the new added style is being renamed
|
||||
basedOnStyle.Attribute(val).Value = basedOnStyle.Attribute(val).Value + newStyleNameSuffix;
|
||||
}
|
||||
return basedOnStyleId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a new styles part inside the document
|
||||
/// </summary>
|
||||
/// <param name="newStylesDocument">Path of styles definition file</param>
|
||||
public static OpenXmlPowerToolsDocument SetStylePart(WmlDocument doc, XDocument newStylesDocument)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
// Replaces XDocument with the style file to transfer
|
||||
if (document.MainDocumentPart.StyleDefinitionsPart == null)
|
||||
document.MainDocumentPart.AddNewPart<StyleDefinitionsPart>();
|
||||
document.MainDocumentPart.StyleDefinitionsPart.PutXDocument(newStylesDocument);
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new style definition
|
||||
/// </summary>
|
||||
/// <param name="xmlStyleDefinition">Style definition</param>
|
||||
public static void AddStyleDefinition(WordprocessingDocument document, XElement xmlStyleDefinition)
|
||||
{
|
||||
// Inserts the new style
|
||||
XDocument stylesPart = GetStylesDocument(document);
|
||||
stylesPart.Root.Add(xmlStyleDefinition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a set of styles in the styles.xml file
|
||||
/// </summary>
|
||||
/// <param name="xmlStyleDefinitions">Collection of style definitions</param>
|
||||
public static void AddStyleDefinition(WordprocessingDocument document, IEnumerable<XElement> xmlStyleDefinitions)
|
||||
{
|
||||
XDocument stylesPart = GetStylesDocument(document);
|
||||
foreach (XElement xmlStyleDefinition in xmlStyleDefinitions)
|
||||
{
|
||||
stylesPart.Root.Add(xmlStyleDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert a style into a given xmlpath inside the document part
|
||||
/// </summary>
|
||||
/// <param name="xpathInsertionPoint">place where we are going to put the style</param>
|
||||
/// <param name="styleValue">name of the style</param>
|
||||
/// <param name="stylesSource">XDocument containing styles</param>
|
||||
public static OpenXmlPowerToolsDocument Insert(WmlDocument doc, string xpathInsertionPoint, string styleValue, XDocument stylesSource)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
XDocument xDocument = document.MainDocumentPart.GetXDocument();
|
||||
XmlDocument xmlMainDocument = new XmlDocument();
|
||||
xmlMainDocument.Load(xDocument.CreateReader());
|
||||
|
||||
// create the style element to add in the document, based upon the style name.
|
||||
// this is an example of an style element
|
||||
|
||||
// <w:pStyle w:val="Heading1" />
|
||||
|
||||
// so, in order to construct this, we have to know already if the style will be placed inside
|
||||
// a run or inside a paragraph. to know this we have to verify against the xpath, and know if
|
||||
// the query want to access a 'run' or a paragraph
|
||||
|
||||
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(xmlMainDocument.NameTable);
|
||||
namespaceManager.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
|
||||
|
||||
XmlNodeList insertionPoints = xmlMainDocument.SelectNodes(xpathInsertionPoint, namespaceManager);
|
||||
|
||||
if (insertionPoints.Count == 0)
|
||||
throw new ArgumentException("The xpath query did not return a valid location.");
|
||||
|
||||
foreach (XmlNode insertionPoint in insertionPoints)
|
||||
{
|
||||
XmlElement xmlStyle = null;
|
||||
|
||||
if (insertionPoint.LocalName == "r" || insertionPoint.LocalName == "p")
|
||||
{
|
||||
XmlNode propertiesElement = insertionPoint.SelectSingleNode(@"w:pPr|w:rPr", namespaceManager);
|
||||
|
||||
//if (propertiesElement != null)
|
||||
//{
|
||||
if (insertionPoint.Name == "w:p")
|
||||
{
|
||||
xmlStyle = xmlMainDocument.CreateElement("w", "pStyle", namespaceManager.LookupNamespace("w"));
|
||||
|
||||
// retrieve the suffix from the styleAccesor class
|
||||
xmlStyle.SetAttribute("val", namespaceManager.LookupNamespace("w"), styleValue + newStyleNameSuffix);
|
||||
|
||||
// check if the rPr or pPr element exist, if so, then add the style xml element
|
||||
// inside, if not, then add a new rPr or pPr element
|
||||
if (propertiesElement != null)
|
||||
{
|
||||
// check if there is already a style node and remove it
|
||||
XmlNodeList xmlStyleList = propertiesElement.SelectNodes("w:pStyle", namespaceManager);
|
||||
for (int i = 0; i < xmlStyleList.Count; i++)
|
||||
{
|
||||
propertiesElement.RemoveChild(xmlStyleList[i]);
|
||||
}
|
||||
propertiesElement.PrependChild(xmlStyle);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertiesElement = xmlMainDocument.CreateElement("w", "pPr", namespaceManager.LookupNamespace("w"));
|
||||
propertiesElement.PrependChild(xmlStyle);
|
||||
insertionPoint.PrependChild(propertiesElement);
|
||||
}
|
||||
}
|
||||
|
||||
if (insertionPoint.Name == "w:r")
|
||||
{
|
||||
xmlStyle = xmlMainDocument.CreateElement("w", "rStyle", namespaceManager.LookupNamespace("w"));
|
||||
xmlStyle.SetAttribute("val", namespaceManager.LookupNamespace("w"), styleValue + newStyleNameSuffix);
|
||||
if (propertiesElement != null)
|
||||
{
|
||||
// check if there is already a style node and remove it
|
||||
XmlNodeList xmlStyleList = propertiesElement.SelectNodes("w:rStyle", namespaceManager);
|
||||
for (int i = 0; i < xmlStyleList.Count; i++)
|
||||
{
|
||||
propertiesElement.RemoveChild(xmlStyleList[i]);
|
||||
}
|
||||
propertiesElement.PrependChild(xmlStyle);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertiesElement = xmlMainDocument.CreateElement("w", "rPr", namespaceManager.LookupNamespace("w"));
|
||||
propertiesElement.PrependChild(xmlStyle);
|
||||
insertionPoint.PrependChild(propertiesElement);
|
||||
}
|
||||
}
|
||||
//}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("The xpath query did not return a valid location.");
|
||||
}
|
||||
XDocument xNewDocument = new XDocument();
|
||||
using (XmlWriter xWriter = xNewDocument.CreateWriter())
|
||||
xmlMainDocument.WriteTo(xWriter);
|
||||
document.MainDocumentPart.PutXDocument(xNewDocument);
|
||||
|
||||
// the style has been added in the main document part, but now we have to add the
|
||||
// style definition in the styles definitions part. the style definition need to be
|
||||
// extracted from the given inputStyle file.
|
||||
|
||||
// Because a style can be linked with other styles and
|
||||
// can also be based on other styles, all the complete hierarchy of styles has
|
||||
// to be added
|
||||
Collection<XElement> styleHierarchy = StyleAccessor.GetStyleHierarchy(styleValue, stylesSource, newStyleNameSuffix);
|
||||
|
||||
// open the styles file in the document
|
||||
XDocument xmlStylesDefinitionDocument = StyleAccessor.GetStylesDocument(document);
|
||||
|
||||
XDocument xElem = new XDocument();
|
||||
xElem.Add(xmlStylesDefinitionDocument.Root);
|
||||
//insert the new style
|
||||
foreach (XElement xmlStyleDefinition in styleHierarchy)
|
||||
{
|
||||
xElem.Root.Add(xmlStyleDefinition);
|
||||
}
|
||||
document.MainDocumentPart.StyleDefinitionsPart.PutXDocument(xElem);
|
||||
}
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
425
omegapro/PowerTools/Classes/TextReplacer.cs
Normal file
425
omegapro/PowerTools/Classes/TextReplacer.cs
Normal file
@@ -0,0 +1,425 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
public partial class WmlDocument : OpenXmlPowerToolsDocument
|
||||
{
|
||||
public WmlDocument SearchAndReplace(string search, string replace, bool matchCase)
|
||||
{
|
||||
return TextReplacer.SearchAndReplace(this, search, replace, matchCase);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class PmlDocument : OpenXmlPowerToolsDocument
|
||||
{
|
||||
public PmlDocument SearchAndReplace(string search, string replace, bool matchCase)
|
||||
{
|
||||
return TextReplacer.SearchAndReplace(this, search, replace, matchCase);
|
||||
}
|
||||
}
|
||||
|
||||
public class TextReplacer
|
||||
{
|
||||
private class MatchSemaphore
|
||||
{
|
||||
public int MatchId;
|
||||
public MatchSemaphore(int matchId)
|
||||
{
|
||||
MatchId = matchId;
|
||||
}
|
||||
}
|
||||
|
||||
private static XObject CloneWithAnnotation(XNode node)
|
||||
{
|
||||
XElement element = node as XElement;
|
||||
if (element != null)
|
||||
{
|
||||
XElement newElement = new XElement(element.Name,
|
||||
element.Attributes(),
|
||||
element.Nodes().Select(n => CloneWithAnnotation(n)));
|
||||
if (element.Annotation<MatchSemaphore>() != null)
|
||||
newElement.AddAnnotation(element.Annotation<MatchSemaphore>());
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private static object WmlSearchAndReplaceTransform(XNode node,
|
||||
string search, string replace, bool matchCase)
|
||||
{
|
||||
XElement element = node as XElement;
|
||||
if (element != null)
|
||||
{
|
||||
if (element.Name == W.p)
|
||||
{
|
||||
string contents = element.Descendants(W.t).Select(t => (string)t).StringConcatenate();
|
||||
if (contents.Contains(search) ||
|
||||
(!matchCase && contents.ToUpper().Contains(search.ToUpper())))
|
||||
{
|
||||
XElement paragraphWithSplitRuns = new XElement(W.p,
|
||||
element.Attributes(),
|
||||
element.Nodes().Select(n => WmlSearchAndReplaceTransform(n, search,
|
||||
replace, matchCase)));
|
||||
XElement[] subRunArray = paragraphWithSplitRuns
|
||||
.Elements(W.r)
|
||||
.Where(e => {
|
||||
XElement subRunElement = e.Elements().FirstOrDefault(el => el.Name != W.rPr);
|
||||
if (subRunElement == null)
|
||||
return false;
|
||||
return W.SubRunLevelContent.Contains(subRunElement.Name);
|
||||
})
|
||||
.ToArray();
|
||||
int paragraphChildrenCount = subRunArray.Length;
|
||||
int matchId = 1;
|
||||
foreach (var pc in subRunArray
|
||||
.Take(paragraphChildrenCount - (search.Length - 1))
|
||||
.Select((c, i) => new { Child = c, Index = i, }))
|
||||
{
|
||||
var subSequence = subRunArray.SequenceAt(pc.Index).Take(search.Length);
|
||||
var zipped = subSequence.Zip(search, (pcp, c) => new
|
||||
{
|
||||
ParagraphChildProjection = pcp,
|
||||
CharacterToCompare = c,
|
||||
});
|
||||
bool dontMatch = zipped.Any(z => {
|
||||
if (z.ParagraphChildProjection.Annotation<MatchSemaphore>() != null)
|
||||
return true;
|
||||
bool b;
|
||||
if (matchCase)
|
||||
b = z.ParagraphChildProjection.Value != z.CharacterToCompare.ToString();
|
||||
else
|
||||
b = z.ParagraphChildProjection.Value.ToUpper() != z.CharacterToCompare.ToString().ToUpper();
|
||||
return b;
|
||||
});
|
||||
bool match = !dontMatch;
|
||||
if (match)
|
||||
{
|
||||
foreach (var item in subSequence)
|
||||
item.AddAnnotation(new MatchSemaphore(matchId));
|
||||
++matchId;
|
||||
}
|
||||
}
|
||||
|
||||
// The following code is locally impure, as this is the most expressive way to write it.
|
||||
XElement paragraphWithReplacedRuns = (XElement)CloneWithAnnotation(paragraphWithSplitRuns);
|
||||
for (int id = 1; id < matchId; ++id)
|
||||
{
|
||||
List<XElement> elementsToReplace = paragraphWithReplacedRuns
|
||||
.Elements()
|
||||
.Where(e => {
|
||||
var sem = e.Annotation<MatchSemaphore>();
|
||||
if (sem == null)
|
||||
return false;
|
||||
return sem.MatchId == id;
|
||||
})
|
||||
.ToList();
|
||||
elementsToReplace.First().AddBeforeSelf(
|
||||
new XElement(W.r,
|
||||
elementsToReplace.First().Elements(W.rPr),
|
||||
new XElement(W.t, replace)));
|
||||
elementsToReplace.Remove();
|
||||
}
|
||||
var groupedAdjacentRunsWithIdenticalFormatting =
|
||||
paragraphWithReplacedRuns
|
||||
.Elements()
|
||||
.GroupAdjacent(ce =>
|
||||
{
|
||||
if (ce.Name != W.r)
|
||||
return "DontConsolidate";
|
||||
if (ce.Elements().Where(e => e.Name != W.rPr).Count() != 1 ||
|
||||
ce.Element(W.t) == null)
|
||||
return "DontConsolidate";
|
||||
if (ce.Element(W.rPr) == null)
|
||||
return "";
|
||||
return ce.Element(W.rPr).ToString(SaveOptions.None);
|
||||
});
|
||||
XElement paragraphWithConsolidatedRuns = new XElement(W.p,
|
||||
groupedAdjacentRunsWithIdenticalFormatting.Select(g =>
|
||||
{
|
||||
if (g.Key == "DontConsolidate")
|
||||
return (object)g;
|
||||
string textValue = g.Select(r => r.Element(W.t).Value).StringConcatenate();
|
||||
XAttribute xs = null;
|
||||
if (textValue[0] == ' ' || textValue[textValue.Length - 1] == ' ')
|
||||
xs = new XAttribute(XNamespace.Xml + "space", "preserve");
|
||||
return new XElement(W.r,
|
||||
g.First().Elements(W.rPr),
|
||||
new XElement(W.t, xs, textValue));
|
||||
}));
|
||||
return paragraphWithConsolidatedRuns;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
if (element.Name == W.r && element.Elements(W.t).Any())
|
||||
{
|
||||
var collectionOfRuns = element.Elements()
|
||||
.Where(e => e.Name != W.rPr)
|
||||
.Select(e =>
|
||||
{
|
||||
if (e.Name == W.t)
|
||||
{
|
||||
string s = (string)e;
|
||||
IEnumerable<XElement> collectionOfSubRuns = s.Select(c =>
|
||||
{
|
||||
XElement newRun = new XElement(W.r,
|
||||
element.Elements(W.rPr),
|
||||
new XElement(W.t,
|
||||
c == ' ' ?
|
||||
new XAttribute(XNamespace.Xml + "space", "preserve") :
|
||||
null, c));
|
||||
return newRun;
|
||||
});
|
||||
return (object)collectionOfSubRuns;
|
||||
}
|
||||
else
|
||||
{
|
||||
XElement newRun = new XElement(W.r,
|
||||
element.Elements(W.rPr),
|
||||
e);
|
||||
return newRun;
|
||||
}
|
||||
});
|
||||
return collectionOfRuns;
|
||||
}
|
||||
return new XElement(element.Name,
|
||||
element.Attributes(),
|
||||
element.Nodes().Select(n => WmlSearchAndReplaceTransform(n,
|
||||
search, replace, matchCase)));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private static void WmlSearchAndReplaceInXDocument(XDocument xDocument, string search,
|
||||
string replace, bool matchCase)
|
||||
{
|
||||
XElement newRoot = (XElement)WmlSearchAndReplaceTransform(xDocument.Root,
|
||||
search, replace, matchCase);
|
||||
xDocument.Elements().First().ReplaceWith(newRoot);
|
||||
}
|
||||
|
||||
public static WmlDocument SearchAndReplace(WmlDocument doc, string search, string replace, bool matchCase)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
SearchAndReplace(document, search, replace, matchCase);
|
||||
}
|
||||
return streamDoc.GetModifiedWmlDocument();
|
||||
}
|
||||
}
|
||||
|
||||
public static void SearchAndReplace(WordprocessingDocument wordDoc, string search,
|
||||
string replace, bool matchCase)
|
||||
{
|
||||
if (RevisionAccepter.HasTrackedRevisions(wordDoc))
|
||||
throw new InvalidDataException(
|
||||
"Search and replace will not work with documents " +
|
||||
"that contain revision tracking.");
|
||||
XDocument xDoc;
|
||||
xDoc = wordDoc.MainDocumentPart.DocumentSettingsPart.GetXDocument();
|
||||
if (xDoc.Descendants(W.trackRevisions).Any())
|
||||
throw new InvalidDataException("Revision tracking is turned on for document.");
|
||||
|
||||
xDoc = wordDoc.MainDocumentPart.GetXDocument();
|
||||
WmlSearchAndReplaceInXDocument(xDoc, search, replace, matchCase);
|
||||
wordDoc.MainDocumentPart.PutXDocument();
|
||||
foreach (var part in wordDoc.MainDocumentPart.HeaderParts)
|
||||
{
|
||||
xDoc = part.GetXDocument();
|
||||
WmlSearchAndReplaceInXDocument(xDoc, search, replace, matchCase);
|
||||
part.PutXDocument();
|
||||
}
|
||||
foreach (var part in wordDoc.MainDocumentPart.FooterParts)
|
||||
{
|
||||
xDoc = part.GetXDocument();
|
||||
WmlSearchAndReplaceInXDocument(xDoc, search, replace, matchCase);
|
||||
part.PutXDocument();
|
||||
}
|
||||
if (wordDoc.MainDocumentPart.EndnotesPart != null)
|
||||
{
|
||||
xDoc = wordDoc.MainDocumentPart.EndnotesPart.GetXDocument();
|
||||
WmlSearchAndReplaceInXDocument(xDoc, search, replace, matchCase);
|
||||
wordDoc.MainDocumentPart.EndnotesPart.PutXDocument();
|
||||
}
|
||||
if (wordDoc.MainDocumentPart.FootnotesPart != null)
|
||||
{
|
||||
xDoc = wordDoc.MainDocumentPart.FootnotesPart.GetXDocument();
|
||||
WmlSearchAndReplaceInXDocument(xDoc, search, replace, matchCase);
|
||||
wordDoc.MainDocumentPart.FootnotesPart.PutXDocument();
|
||||
}
|
||||
}
|
||||
|
||||
private static object PmlReplaceTextTransform(XNode node, string search, string replace,
|
||||
bool matchCase)
|
||||
{
|
||||
XElement element = node as XElement;
|
||||
if (element != null)
|
||||
{
|
||||
if (element.Name == A.p)
|
||||
{
|
||||
string contents = element.Descendants(A.t).Select(t => (string)t).StringConcatenate();
|
||||
if (contents.Contains(search) ||
|
||||
(!matchCase && contents.ToUpper().Contains(search.ToUpper())))
|
||||
{
|
||||
XElement paragraphWithSplitRuns = new XElement(A.p,
|
||||
element.Attributes(),
|
||||
element.Nodes().Select(n => PmlReplaceTextTransform(n, search,
|
||||
replace, matchCase)));
|
||||
XElement[] subRunArray = paragraphWithSplitRuns
|
||||
.Elements(A.r)
|
||||
.Where(e =>
|
||||
{
|
||||
XElement subRunElement = e.Elements().FirstOrDefault(el => el.Name != A.rPr);
|
||||
if (subRunElement == null)
|
||||
return false;
|
||||
return subRunElement.Name == A.t;
|
||||
})
|
||||
.ToArray();
|
||||
int paragraphChildrenCount = subRunArray.Length;
|
||||
int matchId = 1;
|
||||
foreach (var pc in subRunArray
|
||||
.Take(paragraphChildrenCount - (search.Length - 1))
|
||||
.Select((c, i) => new { Child = c, Index = i, }))
|
||||
{
|
||||
var subSequence = subRunArray.SequenceAt(pc.Index).Take(search.Length);
|
||||
var zipped = subSequence.Zip(search, (pcp, c) => new
|
||||
{
|
||||
ParagraphChildProjection = pcp,
|
||||
CharacterToCompare = c,
|
||||
});
|
||||
bool dontMatch = zipped.Any(z =>
|
||||
{
|
||||
if (z.ParagraphChildProjection.Annotation<MatchSemaphore>() != null)
|
||||
return true;
|
||||
bool b;
|
||||
if (matchCase)
|
||||
b = z.ParagraphChildProjection.Value != z.CharacterToCompare.ToString();
|
||||
else
|
||||
b = z.ParagraphChildProjection.Value.ToUpper() != z.CharacterToCompare.ToString().ToUpper();
|
||||
return b;
|
||||
});
|
||||
bool match = !dontMatch;
|
||||
if (match)
|
||||
{
|
||||
foreach (var item in subSequence)
|
||||
item.AddAnnotation(new MatchSemaphore(matchId));
|
||||
++matchId;
|
||||
}
|
||||
}
|
||||
|
||||
// The following code is locally impure, as this is the most expressive way to write it.
|
||||
XElement paragraphWithReplacedRuns = (XElement)CloneWithAnnotation(paragraphWithSplitRuns);
|
||||
for (int id = 1; id < matchId; ++id)
|
||||
{
|
||||
List<XElement> elementsToReplace = paragraphWithReplacedRuns
|
||||
.Elements()
|
||||
.Where(e =>
|
||||
{
|
||||
var sem = e.Annotation<MatchSemaphore>();
|
||||
if (sem == null)
|
||||
return false;
|
||||
return sem.MatchId == id;
|
||||
})
|
||||
.ToList();
|
||||
elementsToReplace.First().AddBeforeSelf(
|
||||
new XElement(A.r,
|
||||
elementsToReplace.First().Elements(A.rPr),
|
||||
new XElement(A.t, replace)));
|
||||
elementsToReplace.Remove();
|
||||
}
|
||||
|
||||
var groupedAdjacentRunsWithIdenticalFormatting =
|
||||
paragraphWithReplacedRuns
|
||||
.Elements()
|
||||
.GroupAdjacent(ce =>
|
||||
{
|
||||
if (ce.Name != A.r)
|
||||
return "DontConsolidate";
|
||||
if (ce.Elements().Where(e => e.Name != A.rPr).Count() != 1 ||
|
||||
ce.Element(A.t) == null)
|
||||
return "DontConsolidate";
|
||||
if (ce.Element(A.rPr) == null)
|
||||
return "";
|
||||
return ce.Element(A.rPr).ToString(SaveOptions.None);
|
||||
});
|
||||
XElement paragraphWithConsolidatedRuns = new XElement(A.p,
|
||||
groupedAdjacentRunsWithIdenticalFormatting.Select(g =>
|
||||
{
|
||||
if (g.Key == "DontConsolidate")
|
||||
return (object)g;
|
||||
string textValue = g.Select(r => r.Element(A.t).Value).StringConcatenate();
|
||||
return new XElement(A.r,
|
||||
g.First().Elements(A.rPr),
|
||||
new XElement(A.t, textValue));
|
||||
}));
|
||||
return paragraphWithConsolidatedRuns;
|
||||
}
|
||||
}
|
||||
if (element.Name == A.r && element.Elements(A.t).Any())
|
||||
{
|
||||
var collectionOfRuns = element.Elements()
|
||||
.Where(e => e.Name != A.rPr)
|
||||
.Select(e =>
|
||||
{
|
||||
if (e.Name == A.t)
|
||||
{
|
||||
string s = (string)e;
|
||||
IEnumerable<XElement> collectionOfSubRuns = s.Select(c =>
|
||||
{
|
||||
XElement newRun = new XElement(A.r,
|
||||
element.Elements(A.rPr),
|
||||
new XElement(A.t, c));
|
||||
return newRun;
|
||||
});
|
||||
return (object)collectionOfSubRuns;
|
||||
}
|
||||
else
|
||||
{
|
||||
XElement newRun = new XElement(A.r,
|
||||
element.Elements(A.rPr),
|
||||
e);
|
||||
return newRun;
|
||||
}
|
||||
});
|
||||
return collectionOfRuns;
|
||||
}
|
||||
return new XElement(element.Name,
|
||||
element.Attributes(),
|
||||
element.Nodes().Select(n => PmlReplaceTextTransform(n, search, replace, matchCase)));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
public static PmlDocument SearchAndReplace(PmlDocument doc, string search, string replace, bool matchCase)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (PresentationDocument document = streamDoc.GetPresentationDocument())
|
||||
{
|
||||
SearchAndReplace(document, search, replace, matchCase);
|
||||
}
|
||||
return streamDoc.GetModifiedPmlDocument();
|
||||
}
|
||||
}
|
||||
|
||||
public static void SearchAndReplace(PresentationDocument pDoc, string search,
|
||||
string replace, bool matchCase)
|
||||
{
|
||||
PresentationPart presentationPart = pDoc.PresentationPart;
|
||||
foreach (var slidePart in presentationPart.SlideParts)
|
||||
{
|
||||
XDocument slideXDoc = slidePart.GetXDocument();
|
||||
XElement root = slideXDoc.Root;
|
||||
XElement newRoot = (XElement)PmlReplaceTextTransform(root, search, replace, matchCase);
|
||||
slidePart.PutXDocument(new XDocument(newRoot));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
214
omegapro/PowerTools/Classes/ThemeAccessor.cs
Normal file
214
omegapro/PowerTools/Classes/ThemeAccessor.cs
Normal file
@@ -0,0 +1,214 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
using System.IO.Packaging;
|
||||
using System;
|
||||
using System.Xml;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to theme operations
|
||||
/// </summary>
|
||||
public class ThemeAccessor
|
||||
{
|
||||
private static XNamespace ns;
|
||||
private static XNamespace drawingns;
|
||||
private static XNamespace relationshipns;
|
||||
private static string mainDocumentRelationshipType;
|
||||
private static string themeRelationshipType;
|
||||
|
||||
static ThemeAccessor()
|
||||
{
|
||||
ns = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
||||
drawingns = "http://schemas.openxmlformats.org/drawingml/2006/main";
|
||||
relationshipns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
||||
mainDocumentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
|
||||
themeRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the document theme
|
||||
/// </summary>
|
||||
public static OpenXmlPowerToolsDocument GetTheme(WmlDocument doc)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument sourceStreamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
using (WordprocessingDocument document = sourceStreamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
// Loads the theme part main file
|
||||
ThemePart theme = document.MainDocumentPart.ThemePart;
|
||||
if (theme != null)
|
||||
{
|
||||
XDocument themeDocument = theme.GetXDocument();
|
||||
|
||||
// Creates the theme package (thmx file)
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = OpenXmlMemoryStreamDocument.CreatePackage())
|
||||
{
|
||||
using (Package themePackage = streamDoc.GetPackage())
|
||||
{
|
||||
// Creates the theme manager part on the new package and loads default content
|
||||
PackagePart newThemeManagerPart = themePackage.CreatePart(new Uri("/theme/theme/themeManager.xml", UriKind.RelativeOrAbsolute), "application/vnd.openxmlformats-officedocument.themeManager+xml");
|
||||
themePackage.CreateRelationship(newThemeManagerPart.Uri, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument");
|
||||
using (XmlWriter xWriter = XmlWriter.Create(newThemeManagerPart.GetStream(FileMode.Create, FileAccess.Write)))
|
||||
{
|
||||
CreateEmptyThemeManager().WriteTo(xWriter);
|
||||
xWriter.Flush();
|
||||
}
|
||||
|
||||
// Creates the main theme part
|
||||
PackagePart newThemePart = themePackage.CreatePart(new Uri("/theme/theme/" + theme.Uri.OriginalString.Substring(theme.Uri.OriginalString.LastIndexOf('/') + 1), UriKind.RelativeOrAbsolute), theme.ContentType);
|
||||
newThemeManagerPart.CreateRelationship(newThemePart.Uri, TargetMode.Internal, theme.RelationshipType);
|
||||
|
||||
// Gets embeded part references
|
||||
var embeddedItems =
|
||||
themeDocument
|
||||
.Descendants()
|
||||
.Attributes(relationshipns + "embed");
|
||||
|
||||
foreach (IdPartPair partId in theme.Parts)
|
||||
{
|
||||
OpenXmlPart part = partId.OpenXmlPart;
|
||||
|
||||
// Creates the new media part inside the destination package
|
||||
PackagePart newPart = themePackage.CreatePart(new Uri("/theme/media/" + part.Uri.OriginalString.Substring(part.Uri.OriginalString.LastIndexOf('/') + 1), UriKind.RelativeOrAbsolute), part.ContentType);
|
||||
PackageRelationship relationship =
|
||||
newThemePart.CreateRelationship(newPart.Uri, TargetMode.Internal, part.RelationshipType);
|
||||
|
||||
// Copies binary content from original part to destination part
|
||||
Stream partStream = part.GetStream(FileMode.Open, FileAccess.Read);
|
||||
Stream newPartStream = newPart.GetStream(FileMode.Create, FileAccess.Write);
|
||||
byte[] fileContent = new byte[partStream.Length];
|
||||
partStream.Read(fileContent, 0, (int)partStream.Length);
|
||||
newPartStream.Write(fileContent, 0, (int)partStream.Length);
|
||||
newPartStream.Flush();
|
||||
|
||||
// Replaces old embed part reference with the freshly created one
|
||||
XAttribute relationshipAttribute = embeddedItems.FirstOrDefault(e => e.Value == theme.GetIdOfPart(part));
|
||||
if (relationshipAttribute != null)
|
||||
relationshipAttribute.Value = relationship.Id;
|
||||
}
|
||||
|
||||
// Writes the updated theme XDocument into the destination package
|
||||
using (XmlWriter newThemeWriter = XmlWriter.Create(newThemePart.GetStream(FileMode.Create, FileAccess.Write)))
|
||||
themeDocument.WriteTo(newThemeWriter);
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an empty theme manager document
|
||||
/// </summary>
|
||||
private static XDocument CreateEmptyThemeManager() {
|
||||
return new XDocument(
|
||||
new XElement(drawingns+"themeManager",
|
||||
new XAttribute(XNamespace.Xmlns + "a", drawingns)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the document theme
|
||||
/// </summary>
|
||||
/// <param name="theme">Theme package</param>
|
||||
public static OpenXmlPowerToolsDocument SetTheme(WmlDocument doc, OpenXmlPowerToolsDocument themeDoc)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument themeStream = new OpenXmlMemoryStreamDocument(themeDoc))
|
||||
using (Package theme = themeStream.GetPackage())
|
||||
{
|
||||
// Gets the theme manager part
|
||||
PackageRelationship themeManagerRelationship =
|
||||
theme.GetRelationshipsByType(mainDocumentRelationshipType).FirstOrDefault();
|
||||
if (themeManagerRelationship != null)
|
||||
{
|
||||
PackagePart themeManagerPart = theme.GetPart(themeManagerRelationship.TargetUri);
|
||||
|
||||
// Gets the theme main part
|
||||
PackageRelationship themeRelationship =
|
||||
themeManagerPart.GetRelationshipsByType(themeRelationshipType).FirstOrDefault();
|
||||
if (themeRelationship != null)
|
||||
{
|
||||
PackagePart themePart = theme.GetPart(themeRelationship.TargetUri);
|
||||
XDocument newThemeDocument = XDocument.Load(XmlReader.Create(themePart.GetStream(FileMode.Open, FileAccess.Read)));
|
||||
|
||||
// Removes existing theme part from document
|
||||
if (document.MainDocumentPart.ThemePart != null)
|
||||
document.MainDocumentPart.DeletePart(document.MainDocumentPart.ThemePart);
|
||||
|
||||
// Creates a new theme part
|
||||
ThemePart documentThemePart = document.MainDocumentPart.AddNewPart<ThemePart>();
|
||||
|
||||
var embeddedItems =
|
||||
newThemeDocument
|
||||
.Descendants()
|
||||
.Attributes(relationshipns + "embed");
|
||||
foreach (PackageRelationship imageRelationship in themePart.GetRelationships())
|
||||
{
|
||||
// Adds an image part to the theme part and stores contents inside
|
||||
PackagePart imagePart = theme.GetPart(imageRelationship.TargetUri);
|
||||
ImagePart newImagePart =
|
||||
documentThemePart.AddImagePart(GetImagePartType(imagePart.ContentType));
|
||||
newImagePart.FeedData(imagePart.GetStream(FileMode.Open, FileAccess.Read));
|
||||
|
||||
// Updates relationship references into the theme XDocument
|
||||
XAttribute relationshipAttribute = embeddedItems.FirstOrDefault(e => e.Value == imageRelationship.Id);
|
||||
if (relationshipAttribute != null)
|
||||
relationshipAttribute.Value = documentThemePart.GetIdOfPart(newImagePart);
|
||||
}
|
||||
documentThemePart.PutXDocument(newThemeDocument);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the image type representation for a mimetype
|
||||
/// </summary>
|
||||
/// <param name="imageContentType">Content mimetype</param>
|
||||
/// <returns>Image type</returns>
|
||||
private static ImagePartType GetImagePartType(string imageContentType)
|
||||
{
|
||||
switch (imageContentType) {
|
||||
case "image/jpeg":
|
||||
return ImagePartType.Jpeg;
|
||||
case "image/emf":
|
||||
return ImagePartType.Emf;
|
||||
case "image/gif":
|
||||
return ImagePartType.Gif;
|
||||
case "image/ico":
|
||||
return ImagePartType.Icon;
|
||||
case "image/pcx":
|
||||
return ImagePartType.Pcx;
|
||||
case "image/png":
|
||||
return ImagePartType.Png;
|
||||
case "image/tiff":
|
||||
return ImagePartType.Tiff;
|
||||
case "image/wmf":
|
||||
return ImagePartType.Wmf;
|
||||
default:
|
||||
return ImagePartType.Bmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
247
omegapro/PowerTools/Classes/WatermarkAccessor.cs
Normal file
247
omegapro/PowerTools/Classes/WatermarkAccessor.cs
Normal file
@@ -0,0 +1,247 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to watermark operations
|
||||
/// </summary>
|
||||
public class WatermarkAccessor
|
||||
{
|
||||
private static XNamespace ns;
|
||||
private static XNamespace officens;
|
||||
private static XNamespace vmlns;
|
||||
private static XNamespace relationshipsns;
|
||||
private static string diagonalWatermarkStyle = "position:absolute;margin-left:0;margin-top:0;width:527.85pt;height:131.95pt;rotation:315;z-index:-251656192;mso-position-horizontal:center;mso-position-horizontal-relative:margin;mso-position-vertical:center;mso-position-vertical-relative:margin";
|
||||
private static string defaultWatermarkStyle = "position:absolute;margin-left:0;margin-top:0;width:468pt;height:117pt;z-index:-251652096;mso-position-horizontal:center;mso-position-horizontal-relative:margin;mso-position-vertical:center;mso-position-vertical-relative:margin";
|
||||
|
||||
static WatermarkAccessor()
|
||||
{
|
||||
ns = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
||||
officens = "urn:schemas-microsoft-com:office:office";
|
||||
vmlns = "urn:schemas-microsoft-com:vml";
|
||||
relationshipsns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a watermark text inside a document
|
||||
/// </summary>
|
||||
/// <param name="watermarkText">text to show in the watermark</param>
|
||||
/// <param name="diagonalOrientation">specify that the text orientation will be in a diagonal way</param>
|
||||
public static OpenXmlPowerToolsDocument InsertWatermark(WmlDocument doc, string watermarkText, bool diagonalOrientation)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
Collection<XDocument> headers = new Collection<XDocument>();
|
||||
|
||||
if (HeaderAccessor.GetHeaderReference(document, HeaderType.First, 0) == null)
|
||||
headers.Add(HeaderAccessor.AddNewHeader(document, HeaderType.First));
|
||||
else
|
||||
headers.Add(HeaderAccessor.GetHeader(document, HeaderType.First, 0));
|
||||
|
||||
if (HeaderAccessor.GetHeaderReference(document, HeaderType.Even, 0) == null)
|
||||
headers.Add(HeaderAccessor.AddNewHeader(document, HeaderType.Even));
|
||||
else
|
||||
headers.Add(HeaderAccessor.GetHeader(document, HeaderType.Even, 0));
|
||||
|
||||
if (HeaderAccessor.GetHeaderReference(document, HeaderType.Default, 0) == null)
|
||||
headers.Add(HeaderAccessor.AddNewHeader(document, HeaderType.Default));
|
||||
else
|
||||
headers.Add(HeaderAccessor.GetHeader(document, HeaderType.Default, 0));
|
||||
|
||||
foreach (XDocument header in headers)
|
||||
{
|
||||
var runElement = header.Descendants(ns + "r").FirstOrDefault();
|
||||
if (runElement == null)
|
||||
{
|
||||
header.Root.Add(
|
||||
new XElement(ns + "sdt",
|
||||
new XElement(ns + "sdtContent",
|
||||
new XElement(ns + "p",
|
||||
new XElement(ns + "pPr",
|
||||
new XElement(ns + "pStyle",
|
||||
new XAttribute(ns + "val", "Header")
|
||||
)
|
||||
),
|
||||
runElement = new XElement(ns + "r")
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
runElement.AddBeforeSelf(CreateWatermarkVml(watermarkText, diagonalOrientation));
|
||||
}
|
||||
|
||||
HeaderAccessor.GetHeaderPart(document, HeaderType.First, 0).PutXDocument();
|
||||
HeaderAccessor.GetHeaderPart(document, HeaderType.Even, 0).PutXDocument();
|
||||
HeaderAccessor.GetHeaderPart(document, HeaderType.Default, 0).PutXDocument();
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the markup for watermark displaying
|
||||
/// </summary>
|
||||
/// <param name="watermarkText">Text to include in markup</param>
|
||||
/// <param name="diagonalOrientation">Orientation of text</param>
|
||||
/// <returns>Watermark markup</returns>
|
||||
private static XElement CreateWatermarkVml(string watermarkText, bool diagonalOrientation)
|
||||
{
|
||||
return new XElement(ns + "r",
|
||||
new XElement(ns + "pict",
|
||||
new XElement(vmlns + "shapetype",
|
||||
new XAttribute("id", "_x0000_t136"),
|
||||
new XAttribute("coordsize", "21600,21600"),
|
||||
new XAttribute(officens + "spt", "136"),
|
||||
new XAttribute("adj", "10800"),
|
||||
new XAttribute("path", "m@7,l@8,m@5,21600l@6,21600e"),
|
||||
new XElement(vmlns + "formulas",
|
||||
new XElement(vmlns + "f",
|
||||
new XAttribute("eqn", "sum #0 0 10800")
|
||||
),
|
||||
new XElement(vmlns + "f",
|
||||
new XAttribute("eqn", "prod #0 2 1")
|
||||
),
|
||||
new XElement(vmlns + "f",
|
||||
new XAttribute("eqn", "sum 21600 0 @1")
|
||||
),
|
||||
new XElement(vmlns + "f",
|
||||
new XAttribute("eqn", "sum 0 0 @2")
|
||||
),
|
||||
new XElement(vmlns + "f",
|
||||
new XAttribute("eqn", "sum 21600 0 @3")
|
||||
),
|
||||
new XElement(vmlns + "f",
|
||||
new XAttribute("eqn", "if @0 @3 0")
|
||||
),
|
||||
new XElement(vmlns + "f",
|
||||
new XAttribute("eqn", "if @0 21600 @1")
|
||||
),
|
||||
new XElement(vmlns + "f",
|
||||
new XAttribute("eqn", "if @0 0 @2")
|
||||
),
|
||||
new XElement(vmlns + "f",
|
||||
new XAttribute("eqn", "if @0 @4 21600")
|
||||
),
|
||||
new XElement(vmlns + "f",
|
||||
new XAttribute("eqn", "mid @5 @6")
|
||||
),
|
||||
new XElement(vmlns + "f",
|
||||
new XAttribute("eqn", "mid @8 @5")
|
||||
),
|
||||
new XElement(vmlns + "f",
|
||||
new XAttribute("eqn", "mid @7 @8")
|
||||
),
|
||||
new XElement(vmlns + "f",
|
||||
new XAttribute("eqn", "mid @6 @7")
|
||||
),
|
||||
new XElement(vmlns + "f",
|
||||
new XAttribute("eqn", "sum @6 0 @5")
|
||||
)
|
||||
),
|
||||
new XElement(vmlns + "path",
|
||||
new XAttribute("textpathok", "t"),
|
||||
new XAttribute(officens + "connecttype", "custom"),
|
||||
new XAttribute(officens + "connectlocs", "@9,0;@10,10800;@11,21600;@12,10800"),
|
||||
new XAttribute(officens + "connectangles", "270,180,90,0")
|
||||
),
|
||||
new XElement(vmlns + "textpath",
|
||||
new XAttribute("on", "t"),
|
||||
new XAttribute("fitshape", "t")
|
||||
),
|
||||
new XElement(vmlns + "handles",
|
||||
new XElement(vmlns + "h",
|
||||
new XAttribute("position", "#0,bottomRight"),
|
||||
new XAttribute("xrange", "6629,14971")
|
||||
)
|
||||
),
|
||||
new XElement(officens + "lock",
|
||||
new XAttribute(vmlns + "ext", "edit"),
|
||||
new XAttribute("text", "t"),
|
||||
new XAttribute("shapetype", "t")
|
||||
)
|
||||
),
|
||||
new XElement(vmlns + "shape",
|
||||
new XAttribute("id", "PowerPlusWaterMarkObject98078923"),
|
||||
new XAttribute(officens + "spid", "_x0000_s2055"),
|
||||
new XAttribute("type", "#_x0000_t136"),
|
||||
new XAttribute("style", diagonalOrientation ? diagonalWatermarkStyle : defaultWatermarkStyle),
|
||||
new XAttribute(officens + "allowincell", "f"),
|
||||
new XAttribute("fillcolor", "silver"),
|
||||
new XAttribute("stroked", "f"),
|
||||
new XElement(vmlns + "fill",
|
||||
new XAttribute("opacity", ".5")
|
||||
),
|
||||
new XElement(vmlns + "textpath",
|
||||
new XAttribute("style", "font-family:"Calibri";font-size:1pt"),
|
||||
new XAttribute("string", watermarkText)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text related to watermark from a document
|
||||
/// </summary>
|
||||
/// <returns>Watermark text</returns>
|
||||
public static string GetWatermarkText(WmlDocument doc)
|
||||
{
|
||||
IEnumerable<XElement> watermarkDescription = GetWatermark(doc);
|
||||
if (watermarkDescription != null)
|
||||
return
|
||||
watermarkDescription
|
||||
.Descendants(vmlns + "shape")
|
||||
.Descendants(vmlns + "textpath")
|
||||
.First()
|
||||
.Attribute("string")
|
||||
.Value;
|
||||
else
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the document structure related to watermark description
|
||||
/// </summary>
|
||||
/// <returns>Document structure related to watermark description</returns>
|
||||
public static IEnumerable<XElement> GetWatermark(WmlDocument doc)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
|
||||
{
|
||||
// to get the watermark text, we have to look inside the document
|
||||
// get the default header reference and get the header reference id part
|
||||
XElement defaultHeaderReference = HeaderAccessor.GetHeaderReference(document, HeaderType.Default, 0);
|
||||
if (defaultHeaderReference != null)
|
||||
{
|
||||
string headerReferenceId = defaultHeaderReference.Attribute(relationshipsns + "id").Value;
|
||||
OpenXmlPart headerPart = document.MainDocumentPart.GetPartById(headerReferenceId);
|
||||
if (headerPart != null)
|
||||
{
|
||||
XDocument headerPartXml = headerPart.GetXDocument();
|
||||
return headerPartXml.Descendants(ns + "pict");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
840
omegapro/PowerTools/Classes/WordprocessingDocumentManager.cs
Normal file
840
omegapro/PowerTools/Classes/WordprocessingDocumentManager.cs
Normal file
@@ -0,0 +1,840 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages WordprocessingDocument content
|
||||
/// </summary>
|
||||
public class WordprocessingDocumentManager
|
||||
{
|
||||
private static XNamespace ns;
|
||||
private static XNamespace relationshipsns;
|
||||
private const string ValAttrName = "w:val";
|
||||
|
||||
static WordprocessingDocumentManager()
|
||||
{
|
||||
ns = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
||||
relationshipsns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the xml of a main document part content, with an empty body.
|
||||
/// </summary>
|
||||
/// <returns>the XDocument result</returns>
|
||||
private static XDocument CreateMainDocumentPartXml()
|
||||
{
|
||||
return new XDocument(
|
||||
new XElement(ns + "document",
|
||||
new XAttribute(XNamespace.Xmlns + "w", ns),
|
||||
new XAttribute(XNamespace.Xmlns + "r", relationshipsns),
|
||||
new XElement(ns + "body")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the xml of a main document part content, adding some text inside
|
||||
/// </summary>
|
||||
/// <param name="text">Text to add inside the document</param>
|
||||
private static XDocument CreateMainDocumentPartXml(string text)
|
||||
{
|
||||
return new XDocument(
|
||||
new XElement(ns + "document",
|
||||
new XAttribute(XNamespace.Xmlns + "w", ns),
|
||||
new XAttribute(XNamespace.Xmlns + "r", relationshipsns),
|
||||
new XElement(ns + "body",
|
||||
new XElement(ns + "p",
|
||||
new XElement(ns + "r",
|
||||
new XElement(ns + "t", text)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the Xml of a main document part content, adding inside the body element a group of xml elements
|
||||
/// </summary>
|
||||
/// <param name="contents">XElement list with contents to add inside a document</param>
|
||||
/// <returns>XDocument result</returns>
|
||||
private static XDocument CreateMainDocumentPartXml(IEnumerable<XElement> contents)
|
||||
{
|
||||
return new XDocument(
|
||||
new XElement(ns + "document",
|
||||
new XAttribute(XNamespace.Xmlns + "w", ns),
|
||||
new XAttribute(XNamespace.Xmlns + "r", relationshipsns),
|
||||
new XElement(ns + "body",
|
||||
contents
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static void LoadRelatedPart(XmlDocument mainDoc, XmlNode node, Stream stream)
|
||||
{
|
||||
XmlDocument partDoc = new XmlDocument();
|
||||
partDoc.Load(stream);
|
||||
XmlNode partNode = mainDoc.ImportNode(partDoc.DocumentElement, true);
|
||||
if (partNode != null)
|
||||
node.AppendChild(partNode);
|
||||
}
|
||||
private static XmlNamespaceManager createNameSpaceManager(XmlNameTable nameTable)
|
||||
{
|
||||
XmlNamespaceManager nameSpaceManager = new XmlNamespaceManager(nameTable);
|
||||
|
||||
nameSpaceManager.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
|
||||
|
||||
nameSpaceManager.AddNamespace("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships");
|
||||
nameSpaceManager.AddNamespace("a", "http://schemas.openxmlformats.org/drawingml/2006/main");
|
||||
nameSpaceManager.AddNamespace("pic", "http://schemas.openxmlformats.org/drawingml/2006/picture");
|
||||
nameSpaceManager.AddNamespace("wp", "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing");
|
||||
|
||||
nameSpaceManager.AddNamespace("v", "urn:schemas-microsoft-com:vml");
|
||||
nameSpaceManager.AddNamespace("w10", "urn:schemas-microsoft-com:office:word");
|
||||
nameSpaceManager.AddNamespace("o", "urn:schemas-microsoft-com:office:office");
|
||||
|
||||
return nameSpaceManager;
|
||||
}
|
||||
|
||||
#if false
|
||||
private class ListLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// constructor used when abstract list levels are instantiated
|
||||
/// </summary>
|
||||
public ListLevel(XmlNode levelNode, XmlNamespaceManager nsm)
|
||||
{
|
||||
this.id = getAttributeValue(levelNode, "w:ilvl");
|
||||
|
||||
XmlNode startValueNode = levelNode.SelectSingleNode("w:start", nsm);
|
||||
|
||||
if (startValueNode != null)
|
||||
{
|
||||
string startValueString = getAttributeValue(startValueNode, ValAttrName);
|
||||
|
||||
if (!String.IsNullOrEmpty(startValueString))
|
||||
{
|
||||
this.startValue = System.Convert.ToUInt32(startValueString, CultureInfo.InvariantCulture) - 1; // as I have to increment counters before each instance (to keep sub-list numbering from advancing too early, set startValue one lower;
|
||||
}
|
||||
|
||||
}
|
||||
this.started = false;
|
||||
|
||||
XmlNode levelTextNode = levelNode.SelectSingleNode("w:lvlText", nsm);
|
||||
|
||||
if (levelTextNode != null)
|
||||
{
|
||||
this.levelText = getAttributeValue(levelTextNode, ValAttrName);
|
||||
}
|
||||
|
||||
XmlNode fontNode = levelNode.SelectSingleNode(".//w:rFonts", nsm);
|
||||
|
||||
if (fontNode != null)
|
||||
{
|
||||
this.font = getAttributeValue(fontNode, "w:hAnsi");
|
||||
}
|
||||
|
||||
XmlNode enumTypeNode = levelNode.SelectSingleNode("w:numFmt", nsm);
|
||||
|
||||
if (enumTypeNode != null)
|
||||
{
|
||||
string type = getAttributeValue(enumTypeNode, ValAttrName);
|
||||
|
||||
// w:numFmt="bullet" indicates a bulleted list
|
||||
this.isBullet = String.Compare(type, "bullet", StringComparison.OrdinalIgnoreCase) == 0;
|
||||
// w:numFmt="lowerLetter" indicates letter conversion instead of number
|
||||
this.isUpperLetter = String.Compare(type, "upperLetter", StringComparison.OrdinalIgnoreCase) == 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// copy constructor
|
||||
/// </summary>
|
||||
/// <param name="masterCopy"></param>
|
||||
public ListLevel(ListLevel masterCopy)
|
||||
{
|
||||
this.abstractLevel = masterCopy;
|
||||
this.id = masterCopy.ID;
|
||||
this.levelText = masterCopy.LevelText;
|
||||
this.startValue = masterCopy.StartValue;
|
||||
this.started = false;
|
||||
this.font = masterCopy.Font;
|
||||
this.isBullet = masterCopy.IsBullet;
|
||||
this.isUpperLetter = masterCopy.IsUpperLetter;
|
||||
}
|
||||
|
||||
private ListLevel abstractLevel;
|
||||
|
||||
/// <summary>
|
||||
/// Get overridden values
|
||||
/// </summary>
|
||||
/// <param name="levelNode"></param>
|
||||
/// <param name="nsm"></param>
|
||||
public void SetOverrides(XmlNode levelNode, XmlNamespaceManager nsm)
|
||||
{
|
||||
XmlNode startValueNode = levelNode.SelectSingleNode("w:start", nsm);
|
||||
|
||||
if (startValueNode != null)
|
||||
{
|
||||
string startValueString = getAttributeValue(startValueNode, ValAttrName);
|
||||
this.startValue = System.Convert.ToUInt32(startValueString, CultureInfo.InvariantCulture) - 1; // as I have to increment counters before each instance (to keep sub-list numbering from advancing too early, set startValue one lower
|
||||
}
|
||||
|
||||
XmlNode levelTextNode = levelNode.SelectSingleNode("w:lvlText", nsm);
|
||||
|
||||
if (levelTextNode != null)
|
||||
{
|
||||
this.levelText = getAttributeValue(levelTextNode, ValAttrName);
|
||||
}
|
||||
|
||||
XmlNode fontNode = levelNode.SelectSingleNode("//w:rFonts", nsm);
|
||||
|
||||
if (fontNode != null)
|
||||
{
|
||||
this.font = getAttributeValue(fontNode, "w:hAnsi");
|
||||
}
|
||||
|
||||
XmlNode enumTypeNode = levelNode.SelectSingleNode("w:numFmt", nsm);
|
||||
|
||||
if (enumTypeNode != null)
|
||||
{
|
||||
string type = getAttributeValue(enumTypeNode, ValAttrName);
|
||||
|
||||
// w:numFmt="bullet" indicates a bulleted list
|
||||
this.isBullet = String.Compare(type, "bullet", StringComparison.OrdinalIgnoreCase) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
public string FormatValue()
|
||||
{
|
||||
if (isUpperLetter)
|
||||
return Convert.ToChar(('A' + CurrentValue - 1)).ToString(CultureInfo.InvariantCulture);
|
||||
return CurrentValue.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
private string id;
|
||||
|
||||
/// <summary>
|
||||
/// returns the ID of the level
|
||||
/// </summary>
|
||||
public string ID
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
|
||||
private UInt32 startValue;
|
||||
private bool started;
|
||||
|
||||
/// <summary>
|
||||
/// start value of that level
|
||||
/// </summary>
|
||||
public UInt32 StartValue
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.startValue;
|
||||
}
|
||||
}
|
||||
|
||||
private UInt32 counter;
|
||||
|
||||
/// <summary>
|
||||
/// returns the current count of list items of that level
|
||||
/// </summary>
|
||||
public UInt32 CurrentValue
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.abstractLevel != null)
|
||||
return this.abstractLevel.CurrentValue;
|
||||
return this.counter;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// increments the current count of list items of that level
|
||||
/// </summary>
|
||||
public void IncrementCounter()
|
||||
{
|
||||
if (!started)
|
||||
{
|
||||
ResetCounter();
|
||||
this.started = true;
|
||||
}
|
||||
if (this.abstractLevel != null)
|
||||
this.abstractLevel.counter++;
|
||||
else
|
||||
this.counter++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// resets the counter to the start value
|
||||
/// </summary>
|
||||
/// <id guid="823b5a3c-7501-4746-8dc4-7b098de5947a" />
|
||||
/// <owner alias="ROrleth" />
|
||||
public void ResetCounter()
|
||||
{
|
||||
if (this.abstractLevel != null)
|
||||
this.abstractLevel.counter = this.startValue;
|
||||
else
|
||||
this.counter = this.startValue;
|
||||
}
|
||||
|
||||
private string levelText;
|
||||
|
||||
/// <summary>
|
||||
/// returns the indicated lvlText value
|
||||
/// </summary>
|
||||
public string LevelText
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.levelText;
|
||||
}
|
||||
}
|
||||
|
||||
private string font;
|
||||
|
||||
/// <summary>
|
||||
/// returns the font name
|
||||
/// </summary>
|
||||
public string Font
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.font;
|
||||
}
|
||||
}
|
||||
|
||||
private bool isBullet;
|
||||
|
||||
/// <summary>
|
||||
/// returns whether the enumeration type is a bulleted list or not
|
||||
/// </summary>
|
||||
public bool IsBullet
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.isBullet;
|
||||
}
|
||||
}
|
||||
|
||||
private bool isUpperLetter;
|
||||
|
||||
/// <summary>
|
||||
/// returns whether the enumeration type is upper-case letter or not
|
||||
/// </summary>
|
||||
public bool IsUpperLetter
|
||||
{
|
||||
get { return this.isUpperLetter; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// private helper class to deal with abstract number lists
|
||||
/// </summary>
|
||||
private class AbstractListNumberingDefinition
|
||||
{
|
||||
private Dictionary<string, ListLevel> listLevels;
|
||||
|
||||
/// <summary>
|
||||
/// constructor
|
||||
/// </summary>
|
||||
/// <param name="abstractNumNode"></param>
|
||||
/// <param name="nsm"></param>
|
||||
public AbstractListNumberingDefinition(XmlNode abstractNumNode, XmlNamespaceManager nsm)
|
||||
{
|
||||
string abstractNumString = getAttributeValue(abstractNumNode, "w:abstractNumId");
|
||||
|
||||
if (!String.IsNullOrEmpty(abstractNumString))
|
||||
{
|
||||
this.abstractNumDefId = abstractNumString;
|
||||
|
||||
this.readListLevelsFromAbsNode(abstractNumNode, nsm);
|
||||
|
||||
// find out whether there is a linked abstractNum definition that this needs to be populated from later on
|
||||
XmlNode linkedStyleNode = abstractNumNode.SelectSingleNode("./w:numStyleLink", nsm);
|
||||
|
||||
if (linkedStyleNode != null)
|
||||
{
|
||||
this.linkedStyleId = getAttributeValue(linkedStyleNode, ValAttrName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// update the level definitions from a linked abstractNum node
|
||||
/// </summary>
|
||||
/// <param name="linkedNode">
|
||||
/// </param>
|
||||
/// <param name="nsm">
|
||||
/// </param>
|
||||
/// <id guid="36473168-7947-41ea-8210-839bf07eded7" />
|
||||
/// <owner alias="ROrleth" />
|
||||
public void UpdateDefinitionFromLinkedStyle(XmlNode linkedNode, XmlNamespaceManager nsm)
|
||||
{
|
||||
if (!this.HasLinkedStyle)
|
||||
return;
|
||||
|
||||
this.readListLevelsFromAbsNode(linkedNode, nsm);
|
||||
}
|
||||
|
||||
/// <id guid="0e05c34c-f257-4c76-8916-3059af84e333" />
|
||||
/// <owner alias="ROrleth" />
|
||||
private void readListLevelsFromAbsNode(XmlNode absNumNode, XmlNamespaceManager nsm)
|
||||
{
|
||||
XmlNodeList levelNodes = absNumNode.SelectNodes("./w:lvl", nsm);
|
||||
|
||||
if (this.listLevels == null)
|
||||
{
|
||||
this.listLevels = new Dictionary<string, ListLevel>(levelNodes.Count);
|
||||
}
|
||||
|
||||
// loop through the levels it defines and instantiate those
|
||||
foreach (XmlNode levelNode in levelNodes)
|
||||
{
|
||||
ListLevel level = new ListLevel(levelNode, nsm);
|
||||
|
||||
this.listLevels[level.ID] = level;
|
||||
}
|
||||
}
|
||||
|
||||
private string linkedStyleId;
|
||||
|
||||
/// <summary>
|
||||
/// returnts the ID of the linked style
|
||||
/// </summary>
|
||||
/// <id guid="ae2caeec-2d86-4e5f-b816-d508f6f2c893" />
|
||||
/// <owner alias="ROrleth" />
|
||||
public string LinkedStyleId
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.linkedStyleId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// indicates whether there is a linked style
|
||||
/// </summary>
|
||||
/// <id guid="75d74788-9839-448e-ae23-02d40e013d98" />
|
||||
/// <owner alias="ROrleth" />
|
||||
public bool HasLinkedStyle
|
||||
{
|
||||
get
|
||||
{
|
||||
return !String.IsNullOrEmpty(this.linkedStyleId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private string abstractNumDefId;
|
||||
|
||||
/// <summary>
|
||||
/// returns the ID of this abstract number list definition
|
||||
/// </summary>
|
||||
public string ID
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.abstractNumDefId;
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<String, ListLevel> ListLevels
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.listLevels;
|
||||
}
|
||||
}
|
||||
|
||||
public int LevelCount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.ListLevels != null)
|
||||
return this.listLevels.Count;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// private helper class to deal with number lists
|
||||
/// </summary>
|
||||
private class ListNumberingDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// constructor
|
||||
/// </summary>
|
||||
/// <param name="numNode"></param>
|
||||
/// <param name="nsm"></param>
|
||||
/// <param name="abstractListDefinitions"></param>
|
||||
public ListNumberingDefinition(XmlNode numNode, XmlNamespaceManager nsm, Dictionary<string, AbstractListNumberingDefinition> abstractListDefinitions)
|
||||
{
|
||||
this.listNumberId = getAttributeValue(numNode, "w:numId");
|
||||
|
||||
XmlNode abstractNumNode = numNode.SelectSingleNode("./w:abstractNumId", nsm);
|
||||
|
||||
if (abstractNumNode != null)
|
||||
{
|
||||
this.abstractListDefinition = abstractListDefinitions[getAttributeValue(abstractNumNode, ValAttrName)];
|
||||
|
||||
// Create local overrides for the list number levels
|
||||
overrideLevels = new Dictionary<string, ListLevel>();
|
||||
|
||||
// propagate the level overrides into the current list number level definition
|
||||
XmlNodeList levelOverrideNodes = numNode.SelectNodes("./w:lvlOverride", nsm);
|
||||
|
||||
if (levelOverrideNodes != null)
|
||||
{
|
||||
foreach (XmlNode overrideNode in levelOverrideNodes)
|
||||
{
|
||||
string overrideLevelId = getAttributeValue(overrideNode, "w:ilvl");
|
||||
XmlNode node = overrideNode.SelectSingleNode("./w:lvl", nsm);
|
||||
if (node == null)
|
||||
node = overrideNode;
|
||||
|
||||
if (!String.IsNullOrEmpty(overrideLevelId))
|
||||
{
|
||||
ListLevel newLevel = new ListLevel(this.abstractListDefinition.ListLevels[overrideLevelId]);
|
||||
newLevel.SetOverrides(node, nsm);
|
||||
overrideLevels.Add(overrideLevelId, newLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private AbstractListNumberingDefinition abstractListDefinition;
|
||||
private Dictionary<String, ListLevel> overrideLevels;
|
||||
|
||||
/// <summary>
|
||||
/// increment the occurrence count of the specified level, reset the occurrence count of derived levels
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
public void IncrementCounter(string level)
|
||||
{
|
||||
FindLevel(level).IncrementCounter();
|
||||
|
||||
// here's a bit where the decision to use strings as level IDs was bad - I need to loop through the derived levels and reset their counters
|
||||
UInt32 levelNumber = System.Convert.ToUInt32(level, CultureInfo.InvariantCulture) + 1;
|
||||
string levelString = levelNumber.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
while (LevelExists(levelString))
|
||||
{
|
||||
FindLevel(levelString).ResetCounter();
|
||||
levelNumber++;
|
||||
levelString = levelNumber.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
private string listNumberId;
|
||||
|
||||
/// <summary>
|
||||
/// numId of this list numbering schema
|
||||
/// </summary>
|
||||
public string ListNumberId
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.listNumberId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns a string containing the current state of the counters, up to the indicated level
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
/// <returns></returns>
|
||||
public string GetCurrentNumberString(string level)
|
||||
{
|
||||
string formatString = FindLevel(level).LevelText;
|
||||
StringBuilder result = new StringBuilder();
|
||||
string temp = string.Empty;
|
||||
|
||||
for (int i = 0; i < formatString.Length; i++)
|
||||
{
|
||||
temp = formatString.Substring(i, 1);
|
||||
|
||||
if (String.CompareOrdinal(temp, "%") == 0)
|
||||
{
|
||||
if (i < formatString.Length - 1)
|
||||
{
|
||||
string formatStringLevel = formatString.Substring(i + 1, 1);
|
||||
// as it turns out, in the format string, the level is 1-based
|
||||
UInt32 levelId = System.Convert.ToUInt32(formatStringLevel, CultureInfo.InvariantCulture) - 1;
|
||||
result.Append(FindLevel(levelId.ToString(CultureInfo.InvariantCulture)).FormatValue());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Append(temp);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieve the font name that was specified for the list string
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
/// <returns></returns>
|
||||
public string GetFont(string level)
|
||||
{
|
||||
return FindLevel(level).Font;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieve whether the level was a bullet list type
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsBullet(string level)
|
||||
{
|
||||
return FindLevel(level).IsBullet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns whether the specific level ID exists - in testing we've seen some referential integrity issues due to Word bugs
|
||||
/// </summary>
|
||||
/// <param name="level">
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// </returns>
|
||||
/// <id guid="b94c13b8-7273-4f6a-927b-178d685fbe0f" />
|
||||
/// <owner alias="ROrleth" />
|
||||
public bool LevelExists(string level)
|
||||
{
|
||||
if (this.overrideLevels.ContainsKey(level))
|
||||
return true;
|
||||
return this.abstractListDefinition.ListLevels.ContainsKey(level);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns whether the specific level ID exists - in testing we've seen some referential integrity issues due to Word bugs
|
||||
/// </summary>
|
||||
public ListLevel FindLevel(string level)
|
||||
{
|
||||
if (this.overrideLevels.ContainsKey(level))
|
||||
return this.overrideLevels[level];
|
||||
return this.abstractListDefinition.ListLevels[level];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// private helper class to deal with number definitions in styles
|
||||
/// </summary>
|
||||
private class StyleDefinition
|
||||
{
|
||||
private string m_Name;
|
||||
private string m_LevelId;
|
||||
private string m_NumId;
|
||||
|
||||
/// <summary>
|
||||
/// constructor
|
||||
/// </summary>
|
||||
/// <param name="styleNode"></param>
|
||||
/// <param name="nsm"></param>
|
||||
/// <param name="styles"></param>
|
||||
public StyleDefinition(XmlNode styleNode, XmlNamespaceManager nsm, Dictionary<string, StyleDefinition> styles)
|
||||
{
|
||||
m_Name = getAttributeValue(styleNode.ParentNode.ParentNode, "w:styleId");
|
||||
XmlNode child = styleNode.ParentNode.ParentNode.SelectSingleNode("./w:basedOn", nsm);
|
||||
m_LevelId = "0";
|
||||
m_NumId = null;
|
||||
if (child != null)
|
||||
{
|
||||
string basedOnName = getAttributeValue(child, ValAttrName);
|
||||
if (styles.ContainsKey(basedOnName))
|
||||
{
|
||||
StyleDefinition basedOn = styles[basedOnName];
|
||||
if (basedOn != null)
|
||||
{
|
||||
m_LevelId = basedOn.m_LevelId;
|
||||
m_NumId = basedOn.m_NumId;
|
||||
}
|
||||
}
|
||||
}
|
||||
child = styleNode.SelectSingleNode("./w:ilvl", nsm);
|
||||
if (child != null)
|
||||
m_LevelId = getAttributeValue(child, ValAttrName);
|
||||
child = styleNode.SelectSingleNode("./w:numId", nsm);
|
||||
if (child != null)
|
||||
m_NumId = getAttributeValue(child, ValAttrName);
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return m_Name; }
|
||||
}
|
||||
public void AddNumbering(XmlDocument mainDoc, XmlNode parent)
|
||||
{
|
||||
XmlNode numNode = mainDoc.CreateNode(XmlNodeType.Element, "w:numPr", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
|
||||
XmlNode child = mainDoc.CreateNode(XmlNodeType.Element, "w:ilvl", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
|
||||
XmlAttribute attr = mainDoc.CreateAttribute(ValAttrName, "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
|
||||
attr.Value = m_LevelId;
|
||||
child.Attributes.Append(attr);
|
||||
numNode.AppendChild(child);
|
||||
child = mainDoc.CreateNode(XmlNodeType.Element, "w:numId", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
|
||||
attr = mainDoc.CreateAttribute(ValAttrName, "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
|
||||
attr.Value = m_NumId;
|
||||
child.Attributes.Append(attr);
|
||||
numNode.AppendChild(child);
|
||||
parent.AppendChild(numNode);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// count occurrences of numbered lists, save that as a hint on the numbered list node
|
||||
/// </summary>
|
||||
/// <param name="mainDoc"></param>
|
||||
/// <param name="nsm"></param>
|
||||
private static void HandleNumberedLists(XmlDocument mainDoc, XmlNamespaceManager nsm)
|
||||
{
|
||||
// count the number of different list numbering schemes
|
||||
XmlNodeList numberNodes = mainDoc.SelectNodes("/w:document/w:numbering/w:num", nsm);
|
||||
|
||||
if (numberNodes.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// initialize the abstract number list
|
||||
XmlNodeList abstractNumNodes = mainDoc.SelectNodes("/w:document/w:numbering/w:abstractNum", nsm);
|
||||
Dictionary<string, AbstractListNumberingDefinition> abstractListDefinitions = new Dictionary<string, AbstractListNumberingDefinition>(abstractNumNodes.Count);
|
||||
Dictionary<string, ListNumberingDefinition> instanceListDefinitions = new Dictionary<string, ListNumberingDefinition>(numberNodes.Count);
|
||||
|
||||
// store the abstract list type definitions
|
||||
foreach (XmlNode abstractNumNode in abstractNumNodes)
|
||||
{
|
||||
AbstractListNumberingDefinition absNumDef = new AbstractListNumberingDefinition(abstractNumNode, nsm);
|
||||
|
||||
abstractListDefinitions[absNumDef.ID] = absNumDef;
|
||||
}
|
||||
|
||||
// now go through the abstract list definitions and update those that are linked to other styles
|
||||
foreach (KeyValuePair<string, AbstractListNumberingDefinition> absNumDef in abstractListDefinitions)
|
||||
{
|
||||
if (absNumDef.Value.HasLinkedStyle)
|
||||
{
|
||||
// find the linked style
|
||||
string linkStyleXPath = "/w:document/w:numbering/w:abstractNum/w:styleLink[@w:val=\"" + absNumDef.Value.LinkedStyleId + "\"]";
|
||||
XmlNode linkedStyleNode = mainDoc.SelectSingleNode(linkStyleXPath, nsm);
|
||||
|
||||
if (linkedStyleNode != null)
|
||||
{
|
||||
absNumDef.Value.UpdateDefinitionFromLinkedStyle(linkedStyleNode.ParentNode, nsm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// instantiate the list number definitions
|
||||
foreach (XmlNode numNode in numberNodes)
|
||||
{
|
||||
ListNumberingDefinition listDef = new ListNumberingDefinition(numNode, nsm, abstractListDefinitions);
|
||||
|
||||
instanceListDefinitions[listDef.ListNumberId] = listDef;
|
||||
}
|
||||
|
||||
// Get styles with numbering definitions
|
||||
XmlNodeList stylesWithNumbers = mainDoc.SelectNodes("/w:document/w:styles/w:style/w:pPr/w:numPr", nsm);
|
||||
Dictionary<string, StyleDefinition> styleDefinitions = new Dictionary<string, StyleDefinition>(stylesWithNumbers.Count);
|
||||
foreach (XmlNode styleNode in stylesWithNumbers)
|
||||
{
|
||||
// Check to see if it is based on a style that is not in the definitions list yet?
|
||||
StyleDefinition styleDef = new StyleDefinition(styleNode, nsm, styleDefinitions);
|
||||
styleDefinitions[styleDef.Name] = styleDef;
|
||||
}
|
||||
|
||||
XmlNodeList styleParagraphs = mainDoc.SelectNodes("//w:pPr/w:pStyle", nsm);
|
||||
foreach (XmlNode paragraph in styleParagraphs)
|
||||
{
|
||||
string styleName = getAttributeValue(paragraph, ValAttrName);
|
||||
if (!String.IsNullOrEmpty(styleName) && styleDefinitions.ContainsKey(styleName))
|
||||
{
|
||||
XmlNode oldNode = paragraph.ParentNode.SelectSingleNode("./w:numPr", nsm);
|
||||
if (oldNode == null)
|
||||
styleDefinitions[styleName].AddNumbering(mainDoc, paragraph.ParentNode);
|
||||
}
|
||||
}
|
||||
|
||||
XmlNodeList listNodes = mainDoc.SelectNodes("//w:numPr/w:ilvl", nsm);
|
||||
|
||||
foreach (XmlNode node in listNodes)
|
||||
{
|
||||
string levelId = getAttributeValue(node, ValAttrName);
|
||||
XmlNode numIdNode = node.ParentNode.SelectSingleNode("./w:numId", nsm);
|
||||
|
||||
if (!String.IsNullOrEmpty(levelId) && numIdNode != null)
|
||||
{
|
||||
string numId = getAttributeValue(numIdNode, ValAttrName);
|
||||
|
||||
if (!String.IsNullOrEmpty(numId) && instanceListDefinitions.ContainsKey(numId) && instanceListDefinitions[numId].LevelExists(levelId))
|
||||
{
|
||||
XmlAttribute counterAttr = mainDoc.CreateAttribute("numString");
|
||||
|
||||
instanceListDefinitions[numId].IncrementCounter(levelId);
|
||||
counterAttr.Value = instanceListDefinitions[numId].GetCurrentNumberString(levelId) + " ";
|
||||
|
||||
node.Attributes.Append(counterAttr);
|
||||
|
||||
string font = instanceListDefinitions[numId].GetFont(levelId);
|
||||
|
||||
if (!String.IsNullOrEmpty(font))
|
||||
{
|
||||
XmlAttribute fontAttr = mainDoc.CreateAttribute("numFont");
|
||||
|
||||
fontAttr.Value = font;
|
||||
|
||||
node.Attributes.Append(fontAttr);
|
||||
}
|
||||
|
||||
if (instanceListDefinitions[numId].IsBullet(levelId))
|
||||
{
|
||||
XmlAttribute bulletAttr = mainDoc.CreateAttribute("isBullet");
|
||||
|
||||
bulletAttr.Value = "true";
|
||||
|
||||
node.Attributes.Append(bulletAttr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
internal static string getAttributeValue(XmlNode node, string name)
|
||||
{
|
||||
string value = string.Empty;
|
||||
|
||||
XmlAttribute attribute = node.Attributes[name];
|
||||
if (attribute != null && attribute.Value != null)
|
||||
{
|
||||
value = attribute.Value;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
550
omegapro/PowerTools/Classes/WorksheetAccessor.cs
Normal file
550
omegapro/PowerTools/Classes/WorksheetAccessor.cs
Normal file
@@ -0,0 +1,550 @@
|
||||
/***************************************************************************
|
||||
|
||||
Copyright (c) Microsoft Corporation 2011.
|
||||
|
||||
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
|
||||
|
||||
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
using System.Xml;
|
||||
|
||||
namespace OpenXmlPowerTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to worksheet operations
|
||||
/// </summary>
|
||||
public class WorksheetAccessor
|
||||
{
|
||||
private static XNamespace ns;
|
||||
private static XNamespace relationshipsns;
|
||||
|
||||
static WorksheetAccessor()
|
||||
{
|
||||
ns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
|
||||
relationshipsns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a worksheet located at a specific index
|
||||
/// </summary>
|
||||
/// <param name="worksheetIndex">Index for the worksheet to be returned</param>
|
||||
/// <returns></returns>
|
||||
public static WorksheetPart Get(SpreadsheetDocument document, int worksheetIndex)
|
||||
{
|
||||
return document.WorkbookPart.WorksheetParts.ElementAt(worksheetIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the worksheet corresponding to the specified name
|
||||
/// </summary>
|
||||
/// <param name="worksheetName">Name for the worksheet to be returned</param>
|
||||
/// <returns></returns>
|
||||
public static WorksheetPart Get(SpreadsheetDocument document, string worksheetName)
|
||||
{
|
||||
XDocument workbook = document.WorkbookPart.GetXDocument();
|
||||
XElement worksheetXelement = workbook.Root.Element(ns + "sheets")
|
||||
.Elements(ns + "sheet")
|
||||
.Where(s => s.Attribute("name").Value.ToLower().Equals(worksheetName.ToLower())).FirstOrDefault();
|
||||
return (WorksheetPart)document.WorkbookPart
|
||||
.GetPartById(worksheetXelement.Attribute(relationshipsns + "id").Value);
|
||||
|
||||
//return parentDocument.Document.WorkbookPart.WorksheetParts.FirstOrDefault(worksheet => worksheet.Uri.OriginalString.Split(new string[]{"/"},StringSplitOptions.RemoveEmptyEntries).Last<string>().ToLower().Equals(worksheetName.ToLower()+".xml"));
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a given worksheet to the document
|
||||
/// </summary>
|
||||
/// <param name="worksheet">Worksheet document to add</param>
|
||||
/// <returns>Worksheet part just added</returns>
|
||||
public static WorksheetPart Add(SpreadsheetDocument doc, XDocument worksheet)
|
||||
{
|
||||
// Associates base content to a new worksheet part
|
||||
WorkbookPart workbook = doc.WorkbookPart;
|
||||
WorksheetPart worksheetPart = workbook.AddNewPart<WorksheetPart>();
|
||||
worksheetPart.PutXDocument(worksheet);
|
||||
|
||||
// Associates the worksheet part to the workbook part
|
||||
XDocument document = doc.WorkbookPart.GetXDocument();
|
||||
int sheetId =
|
||||
document.Root
|
||||
.Element(ns + "sheets")
|
||||
.Elements(ns + "sheet")
|
||||
.Count() + 1;
|
||||
|
||||
int worksheetCount =
|
||||
document.Root
|
||||
.Element(ns + "sheets")
|
||||
.Elements(ns + "sheet")
|
||||
.Where(
|
||||
t =>
|
||||
t.Attribute("name").Value.StartsWith("sheet", StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
.Count() + 1;
|
||||
|
||||
// Adds content to workbook document to reference worksheet document
|
||||
document.Root
|
||||
.Element(ns + "sheets")
|
||||
.Add(
|
||||
new XElement(ns + "sheet",
|
||||
new XAttribute("name", string.Format("sheet{0}", worksheetCount)),
|
||||
new XAttribute("sheetId", sheetId),
|
||||
new XAttribute(relationshipsns + "id", workbook.GetIdOfPart(worksheetPart))
|
||||
)
|
||||
);
|
||||
doc.WorkbookPart.PutXDocument();
|
||||
return worksheetPart;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates element structure needed to describe an empty worksheet
|
||||
/// </summary>
|
||||
/// <returns>Document with contents for an empty worksheet</returns>
|
||||
private static XDocument CreateEmptyWorksheet()
|
||||
{
|
||||
XDocument document =
|
||||
new XDocument(
|
||||
new XElement(ns + "worksheet",
|
||||
new XAttribute("xmlns", ns),
|
||||
new XAttribute(XNamespace.Xmlns + "r", relationshipsns),
|
||||
new XElement(ns + "sheetData")
|
||||
)
|
||||
);
|
||||
return document;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a value to a cell inside a worksheet document
|
||||
/// </summary>
|
||||
/// <param name="worksheet">document to add values</param>
|
||||
/// <param name="row">Row</param>
|
||||
/// <param name="column">Column</param>
|
||||
/// <param name="value">Value to add</param>
|
||||
private static void AddValue(XDocument worksheet, int row, int column, string value)
|
||||
{
|
||||
//Set the cell reference
|
||||
string cellReference = GetColumnId(column) + row.ToString();
|
||||
double numericValue;
|
||||
//Determining if value for cell is text or numeric
|
||||
bool valueIsNumeric = double.TryParse(value, out numericValue);
|
||||
|
||||
//Creating the new cell element (markup)
|
||||
XElement newCellXElement = valueIsNumeric ?
|
||||
new XElement(ns + "c",
|
||||
new XAttribute("r", cellReference),
|
||||
new XElement(ns + "v", numericValue)
|
||||
)
|
||||
:
|
||||
new XElement(ns + "c",
|
||||
new XAttribute("r", cellReference),
|
||||
new XAttribute("t", "inlineStr"),
|
||||
new XElement(ns + "is",
|
||||
new XElement(ns + "t", value)
|
||||
)
|
||||
);
|
||||
|
||||
// Find the row containing the cell to add the value to
|
||||
XName rowName = "r";
|
||||
XElement rowElement =
|
||||
worksheet.Root
|
||||
.Element(ns + "sheetData")
|
||||
.Elements(ns + "row")
|
||||
.Where(
|
||||
t => t.Attribute(rowName).Value == row.ToString()
|
||||
)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (rowElement == null)
|
||||
{
|
||||
//row element does not exist
|
||||
//create a new one
|
||||
rowElement = CreateEmptyRow(row);
|
||||
|
||||
//row elements must appear in order inside sheetData element
|
||||
if (worksheet.Root
|
||||
.Element(ns + "sheetData").HasElements)
|
||||
{ //if there are more rows already defined at sheetData element
|
||||
//find the row with the inmediate higher index for the row containing the cell to set the value to
|
||||
XElement rowAfterElement = FindRowAfter(worksheet, row);
|
||||
//if there is a row with an inmediate higher index already defined at sheetData
|
||||
if (rowAfterElement != null)
|
||||
{
|
||||
//add the new row before the row with an inmediate higher index
|
||||
rowAfterElement.AddBeforeSelf(rowElement);
|
||||
}
|
||||
else
|
||||
{ //this row is going to be the one with the highest index (add it as the last element for sheetData)
|
||||
worksheet.Root.Element(ns + "sheetData").Elements(ns + "row").Last().AddAfterSelf(rowElement);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //there are no other rows already defined at sheetData
|
||||
//Add a new row elemento to sheetData
|
||||
worksheet
|
||||
.Root
|
||||
.Element(ns + "sheetData")
|
||||
.Add(
|
||||
rowElement //= CreateEmptyRow(row)
|
||||
);
|
||||
}
|
||||
|
||||
//Add the new cell to the row Element
|
||||
rowElement.Add(newCellXElement);
|
||||
}
|
||||
else
|
||||
{
|
||||
//row containing the cell to set the value to is already defined at sheetData
|
||||
//look if cell already exist at that row
|
||||
XElement currentCellXElement = rowElement
|
||||
.Elements(ns + "c")
|
||||
.Where(
|
||||
t => t.Attribute("r").Value == cellReference
|
||||
).FirstOrDefault();
|
||||
|
||||
if (currentCellXElement == null)
|
||||
{ //cell element does not exist at row indicated as parameter
|
||||
//find the inmediate right column for the cell to set the value to
|
||||
XElement columnAfterXElement = FindColumAfter(worksheet, row, column);
|
||||
if (columnAfterXElement != null)
|
||||
{
|
||||
//Insert the new cell before the inmediate right column
|
||||
columnAfterXElement.AddBeforeSelf(newCellXElement);
|
||||
}
|
||||
else
|
||||
{ //There is no inmediate right cell
|
||||
//Add the new cell as the last element for the row
|
||||
rowElement.Add(newCellXElement);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//cell alreay exist
|
||||
//replace the current cell with that with the new value
|
||||
currentCellXElement.ReplaceWith(newCellXElement);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the row with the inmediate higher index for a specific row
|
||||
/// </summary>
|
||||
/// <param name="worksheet">Worksheet for finding the row</param>
|
||||
/// <param name="row">Row index to look for inmediate higher row</param>
|
||||
/// <returns></returns>
|
||||
private static XElement FindRowAfter(XDocument worksheet, int row)
|
||||
{
|
||||
XElement rowAfterXElement = worksheet.Root
|
||||
.Element(ns + "sheetData")
|
||||
.Elements(ns + "row").FirstOrDefault(r => System.Convert.ToInt32(r.Attribute("r").Value) > row);
|
||||
|
||||
return rowAfterXElement;
|
||||
}
|
||||
|
||||
private static XElement FindColumAfter(XDocument worksheet, int row, int column)
|
||||
{
|
||||
XElement columnAfterXElement = worksheet.Root
|
||||
.Element(ns + "sheetData")
|
||||
.Elements(ns + "row").FirstOrDefault(r => System.Convert.ToInt32(r.Attribute("r").Value) == row)
|
||||
.Elements(ns + "c").FirstOrDefault(c =>
|
||||
GetColumnNumberFromCellReference(c.Attribute("r").Value, row) > GetColumnNumberFromCellReference(GetColumnId(column) + row, row));
|
||||
|
||||
return columnAfterXElement;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the column number from the cell reference received as parameter
|
||||
/// </summary>
|
||||
/// <param name="cellReference">Cell reference to obtain the column number from</param>
|
||||
/// <param name="row">Row containing the cell to obtain the column number from</param>
|
||||
/// <returns></returns>
|
||||
private static int GetColumnNumberFromCellReference(string cellReference, int row)
|
||||
{
|
||||
int columnNumber = 0;
|
||||
//Removing row number from cell reference
|
||||
string columnReference = cellReference.Remove(cellReference.Length - row.ToString().Length);
|
||||
|
||||
int charPosition = 1;
|
||||
int charValue = 0;
|
||||
foreach (char c in columnReference)
|
||||
{
|
||||
//Getting the Unicode value for the current char in cell reference
|
||||
charValue = System.Convert.ToInt32(c);
|
||||
if (charPosition < columnReference.Length)
|
||||
{ //we have not reached the last character in cell reference
|
||||
//we need to multiply the charValue (from 0 to 25) by 26 and add the result of powering 26 to current char position in cell reference
|
||||
//65 is the Unicode value for "A" letter
|
||||
//26 is the number of letters in English alphabet
|
||||
columnNumber += (((charValue - 65) * 26) + (System.Convert.ToInt32(Math.Pow(26, charPosition++))));
|
||||
}
|
||||
else
|
||||
{ //This is the last character in cell reference
|
||||
//we substract 64 instead of 65 because we want to get a one-based index for last character (instead of a zero-based index for previous characters)
|
||||
columnNumber += (charValue - 64);
|
||||
}
|
||||
}
|
||||
return columnNumber;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates tags to describe an empty row
|
||||
/// </summary>
|
||||
/// <param name="row"></param>
|
||||
/// <returns></returns>
|
||||
private static XElement CreateEmptyRow(int row)
|
||||
{
|
||||
XElement rowElement =
|
||||
new XElement(ns + "row",
|
||||
new XAttribute("r", row.ToString())
|
||||
);
|
||||
return rowElement;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the column Id for a given column number
|
||||
/// </summary>
|
||||
/// <param name="columnNumber">Column number</param>
|
||||
/// <returns>Column Id</returns>
|
||||
public static string GetColumnId(int columnNumber)
|
||||
{
|
||||
int alfa = (int)'Z' - (int)'A' + 1;
|
||||
if (columnNumber <= alfa)
|
||||
return ((char)((int)'A' + columnNumber - 1)).ToString();
|
||||
else
|
||||
return
|
||||
GetColumnId(
|
||||
(int)((columnNumber - 1) / alfa)
|
||||
) +
|
||||
(
|
||||
(char)(
|
||||
(int)'A' + (int)((columnNumber - 1) % alfa)
|
||||
)
|
||||
).ToString();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a worksheet document and inserts data into it
|
||||
/// </summary>
|
||||
/// <param name="headerList">List of values that will act as the header</param>
|
||||
/// <param name="valueTable">Values for worksheet content</param>
|
||||
/// <param name="headerRow">Header row</param>
|
||||
/// <returns></returns>
|
||||
internal static WorksheetPart Create(SpreadsheetDocument document, List<string> headerList, string[][] valueTable, int headerRow)
|
||||
{
|
||||
XDocument xDocument = CreateEmptyWorksheet();
|
||||
for (int i = 0; i < headerList.Count; i++)
|
||||
{
|
||||
AddValue(xDocument, headerRow, i + 1, headerList[i]);
|
||||
}
|
||||
int rows = valueTable.GetLength(0);
|
||||
int cols = valueTable[0].GetLength(0);
|
||||
|
||||
for (int i = 0; i < rows; i++)
|
||||
{
|
||||
for (int j = 0; j < cols; j++)
|
||||
{
|
||||
AddValue(xDocument, i + headerRow + 1, j + 1, valueTable[i][j]);
|
||||
}
|
||||
}
|
||||
WorksheetPart part = Add(document, xDocument);
|
||||
return part;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get value for a cell in a worksheet
|
||||
/// </summary>
|
||||
/// <param name="worksheet"></param>
|
||||
/// <param name="column"></param>
|
||||
/// <param name="row"></param>
|
||||
/// <remarks>Author:Johann Granados Company: Staff DotNet Creation Date: 8/30/2008</remarks>
|
||||
/// <returns></returns>
|
||||
public static string GetValue(SpreadsheetDocument document, WorksheetPart worksheet, short column, int row)
|
||||
{
|
||||
XDocument worksheetXDocument = XDocument.Load(new XmlTextReader(worksheet.GetStream()));
|
||||
XElement cellValueXElement = GetCell(worksheetXDocument, column, row);
|
||||
|
||||
if (cellValueXElement != null)
|
||||
{
|
||||
if (cellValueXElement.Element(ns + "v") != null)
|
||||
{
|
||||
return GetSharedString(document, System.Convert.ToInt32(cellValueXElement.Value));
|
||||
}
|
||||
else
|
||||
{
|
||||
return cellValueXElement.Element(ns + "is").Element(ns + "t").Value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
private static string GetSharedString(SpreadsheetDocument document, int index)
|
||||
{
|
||||
XDocument sharedStringsXDocument = XDocument.Load(new XmlTextReader(document.WorkbookPart.SharedStringTablePart.GetStream()));
|
||||
return sharedStringsXDocument.Root.Elements().ElementAt<XElement>(index).Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value for a specific cell
|
||||
/// </summary>
|
||||
/// <param name="worksheet">Worksheet part containing the cell to be affected</param>
|
||||
/// <param name="fromColumn">Initial column for setting the value</param>
|
||||
/// <param name="fromRow">Initial row for setting the value</param>
|
||||
/// <param name="toColumn">Final column for setting the value</param>
|
||||
/// <param name="toRow">Final row for setting the value</param>
|
||||
/// <param name="value">Cell value</param>
|
||||
public static OpenXmlPowerToolsDocument SetCellValue(SmlDocument doc, string worksheetName, int fromRow, int toRow, int fromColumn, int toColumn, string value)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (SpreadsheetDocument document = streamDoc.GetSpreadsheetDocument())
|
||||
{
|
||||
WorksheetPart worksheet = Get(document, worksheetName);
|
||||
XDocument worksheetXDocument = XDocument.Load(new XmlTextReader(worksheet.GetStream()));
|
||||
for (int row = fromRow; row <= toRow; row++)
|
||||
{
|
||||
for (int col = fromColumn; col <= toColumn; col++)
|
||||
{
|
||||
AddValue(worksheetXDocument, row, col, value);
|
||||
}
|
||||
}
|
||||
|
||||
XmlWriter worksheetWriter = XmlTextWriter.Create(worksheet.GetStream(System.IO.FileMode.Create));
|
||||
worksheetXDocument.WriteTo(worksheetWriter);
|
||||
worksheetWriter.Flush();
|
||||
worksheetWriter.Close();
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply a cell style to a specific cell
|
||||
/// </summary>
|
||||
/// <param name="worksheet">worksheet containing the cell to be affected</param>
|
||||
/// <param name="fromColumn">Starting Cell Column</param>
|
||||
/// <param name="toColumn">Ending Cell Column</param>
|
||||
/// <param name="fromRow">Starting Cell Row</param>
|
||||
/// <param name="toRow">Ending Cell Row</param>
|
||||
/// <param name="cellStyle">Cell Style</param>
|
||||
public static OpenXmlPowerToolsDocument SetCellStyle(SmlDocument doc, string worksheetName, short fromColumn, short toColumn, int fromRow, int toRow, string cellStyle)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (SpreadsheetDocument document = streamDoc.GetSpreadsheetDocument())
|
||||
{
|
||||
WorksheetPart worksheet = Get(document, worksheetName);
|
||||
XDocument worksheetXDocument = XDocument.Load(new XmlTextReader(worksheet.GetStream()));
|
||||
|
||||
for (int row = fromRow; row <= toRow; row++)
|
||||
{
|
||||
for (short col = fromColumn; col <= toColumn; col++)
|
||||
{
|
||||
XElement cellXelement = GetCell(worksheetXDocument, col, row);
|
||||
cellXelement.SetAttributeValue("s", SpreadSheetStyleAccessor.GetCellStyleIndex(document, cellStyle));
|
||||
}
|
||||
}
|
||||
|
||||
XmlWriter worksheetWriter = XmlTextWriter.Create(worksheet.GetStream(System.IO.FileMode.Create));
|
||||
worksheetXDocument.WriteTo(worksheetWriter);
|
||||
worksheetWriter.Flush();
|
||||
worksheetWriter.Close();
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the width for a range of columns
|
||||
/// </summary>
|
||||
/// <param name="worksheet">Worksheet containing the columns to be affected</param>
|
||||
/// <param name="fromColumn">Initial column to affect</param>
|
||||
/// <param name="toColumn">Final column to affect</param>
|
||||
/// <param name="width">Column width</param>
|
||||
public static OpenXmlPowerToolsDocument SetColumnWidth(SmlDocument doc, string worksheetName, short fromColumn, short toColumn, int width)
|
||||
{
|
||||
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
|
||||
{
|
||||
using (SpreadsheetDocument document = streamDoc.GetSpreadsheetDocument())
|
||||
{
|
||||
WorksheetPart worksheet = Get(document, worksheetName);
|
||||
//Get the worksheet markup
|
||||
XDocument worksheetXDocument = XDocument.Load(new XmlTextReader(worksheet.GetStream()));
|
||||
//Look for worksheet cols element
|
||||
XElement colsXElement = worksheetXDocument.Root.Element(ns + "cols");
|
||||
if (colsXElement == null)
|
||||
{
|
||||
//cols elements does not exist
|
||||
//create a new one
|
||||
colsXElement = new XElement(ns + "cols");
|
||||
//create a new col element (for setting the width)
|
||||
//the col element could span more than one column -span is controlled by min (initial column) and max (final column) attributes
|
||||
colsXElement.Add(new XElement(ns + "col",
|
||||
new XAttribute("min", fromColumn.ToString()),
|
||||
new XAttribute("max", toColumn.ToString()),
|
||||
new XAttribute("width", width.ToString()),
|
||||
new XAttribute("customWidth", "1")));
|
||||
|
||||
//cols element must be added before worksheet sheetData element
|
||||
worksheetXDocument.Root.Element(ns + "sheetData").AddBeforeSelf(colsXElement);
|
||||
}
|
||||
else
|
||||
{
|
||||
//look for a col element for the column range indicated for fromColumn and toColumn
|
||||
XElement colXElement = colsXElement.Elements(ns + "col")
|
||||
.Where(c => (System.Convert.ToInt32(c.Attribute("min").Value) == fromColumn) && (System.Convert.ToInt32(c.Attribute("max").Value) == toColumn)).FirstOrDefault();
|
||||
if (colXElement != null)
|
||||
{
|
||||
//col element does exist
|
||||
//change its width value
|
||||
colXElement.SetAttributeValue("width", width);
|
||||
}
|
||||
else
|
||||
{
|
||||
//col element does not exist
|
||||
//create a new one
|
||||
colsXElement.Add(new XElement(ns + "col",
|
||||
new XAttribute("min", fromColumn.ToString()),
|
||||
new XAttribute("max", toColumn.ToString()),
|
||||
new XAttribute("width", width.ToString()),
|
||||
new XAttribute("customWidth", "1")));
|
||||
}
|
||||
}
|
||||
//Update the worksheet part markup at worksheet part stream
|
||||
XmlWriter worksheetWriter = XmlTextWriter.Create(worksheet.GetStream(System.IO.FileMode.Create));
|
||||
worksheetXDocument.WriteTo(worksheetWriter);
|
||||
worksheetWriter.Flush();
|
||||
worksheetWriter.Close();
|
||||
}
|
||||
return streamDoc.GetModifiedDocument();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a XElement representing the markup for a specific cell in a SpreadsheetML document
|
||||
/// </summary>
|
||||
/// <param name="worksheetXDocument">Worksheet markup</param>
|
||||
/// <param name="column">Cell column</param>
|
||||
/// <param name="row">Cell row</param>
|
||||
/// <returns></returns>
|
||||
private static XElement GetCell(XDocument worksheetXDocument, short column, int row)
|
||||
{
|
||||
return worksheetXDocument.Root
|
||||
.Element(ns + "sheetData")
|
||||
.Elements(ns + "row")
|
||||
.Where(
|
||||
r => r.Attribute("r").Value.Equals(row.ToString())).FirstOrDefault<XElement>()
|
||||
.Elements(ns + "c").Where(c => c.Attribute("r").Value.Equals(GetColumnId(column) + row.ToString())).FirstOrDefault<XElement>();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user