/***************************************************************************
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
{
///
/// Chart types available
///
public enum ChartType
{
///
/// Bar
///
Bar,
///
/// Column
///
Column,
///
/// Line
///
Line,
///
/// Area
///
Area,
///
/// Pie
///
Pie
}
///
/// Provides access to chartsheet operations
///
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";
}
///
/// Creates a chartsheet part from given data
///
/// Type of chart to generate
/// Values to represent in the chart
/// Columns to be used as series
/// Column to be used as category
/// Chartsheet part with contents related
public static ChartsheetPart Create(SpreadsheetDocument parentDocument, ChartType chartType, List values, List headerReference, string categoryReference)
{
//Creates base content and associates it to a new chartsheet part
WorkbookPart workbook = parentDocument.WorkbookPart;
ChartsheetPart chartsheetPart = workbook.AddNewPart();
XDocument chartsheetDocument = CreateEmptyChartsheet();
chartsheetPart.PutXDocument(chartsheetDocument);
//Creates a base drawings part and associates it to the chartsheet part
DrawingsPart drawingsPart = chartsheetPart.AddNewPart();
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();
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;
}
///
/// Creates element structure needed to describe an empty worksheet
///
/// Document with contents for an empty worksheet
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;
}
///
/// Creates tags to describe an empty row
///
///
///
private static XElement CreateEmptyRow(int row)
{
XElement rowElement =
new XElement(ns + "row",
new XAttribute("r", row)
);
return rowElement;
}
///
/// Creates element structure needed to describe an empty drawing
///
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")
)
)
);
}
///
/// Creates element structure needed to describe an empty chart
///
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")
)
)
)
);
}
///
/// Creates element structure needed to describe a chart with data related
///
private static XDocument CreateChart(ChartType chartType, IEnumerable seriesReferences, IEnumerable 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;
}
///
/// Creates element structure needed to describe a column chart with data related
///
private static XElement CreateColumnChart(IEnumerable seriesReferences, IEnumerable 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)
)
);
}
///
/// Creates element structure needed to describe a bar chart with data related
///
private static XElement CreateBarChart(IEnumerable seriesReferences, IEnumerable 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)
)
);
}
///
/// Creates element structure needed to describe a line chart with data related
///
private static XElement CreateLineChart(IEnumerable seriesReferences, IEnumerable 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)
)
);
}
///
/// Creates element structure needed to describe an area chart with data related
///
private static XElement CreateAreaChart(IEnumerable seriesReferences, IEnumerable 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)
)
);
}
///
/// Creates element structure needed to describe a pie chart with data related
///
private static XElement CreatePieChart(IEnumerable seriesReferences, IEnumerable 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)
)
);
}
///
/// Creates element structure needed to describe a data series
///
private static IEnumerable CreateSeries(IEnumerable seriesReference, IEnumerable seriesTitles, string category)
{
List seriesList = new List();
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;
}
///
/// Creates element structure needed to describe a category axis
///
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)
)
);
}
///
/// Creates element structure needed to describe a value axis
///
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")
)
);
}
}
}