Вставка изображения.
Исходный код на C# был сгенерирован программой OpenXMLSDKToolV25 с https://www.microsoft.com/en-US/download/details.aspx?id=30425 из файлов Excel созданных вручную.
В коде x++ были оставлены только обязательные элементы ImagePart, без которых при открытии вновь созданного отчета выходит ошибка Method ‘add’ in COM object of class ‘Workbooks’ returned error code 0x800A03EC (<unknown>) which means: Add method of Workbooks class failed. / Метод «add» в COM-объекте класса «Workbooks» возвратил код ошибки 0x800A03EC (<неизвестно>), который означает: Метод Add из класса Workbooks завершен неверно. Cannot create Microsoft Office Excel workbook. / Невозможно создать рабочую книгу Microsoft Office Excel.
Был создан для примера класс наследник XMLExcelReport_Standard_RU для вывода отчетов в Excel.
class XMLReport_Example extends XMLExcelReport_Standard_RU
{
DocumentFormat.OpenXml.Packaging.DrawingsPart drawingsPart;
DocumentFormat.OpenXml.Drawing.Spreadsheet.WorksheetDrawing worksheetDrawing;
DocumentFormat.OpenXml.Spreadsheet.Worksheet worksheet;
DocumentFormat.OpenXml.Packaging.WorksheetPart worksheetPart;
DocumentFormat.OpenXml.Spreadsheet.Drawing drawing;
OXMLWorkBook_RU oxmlWorkBook_RU;
}
Для того чтобы вставить изображение нужен объект DrawingsPart, который отвечает за упаковку раздела /xl/drawings, а также worksheetDrawing и drawing. Данные объекты в SpreadsheetDocument должны быть одни. Ниже пример добавления этих объектов в шаблон отчета
System.Type type;
;
type = CLRInterop::getType('DocumentFormat.OpenXml.Packaging.DrawingsPart');
oxmlWorkBook_RU = document.workbook();
worksheetPart = oxmlWorkBook_RU.getWorksheet().worksheetPart();
worksheet = worksheetPart.get_Worksheet();
drawingsPart = OXML_RU::invokeGeneric( worksheetPart
, 'AddNewPart'
, type
, new Array(Types::Class)
, true);
worksheetDrawing = new DocumentFormat.OpenXml.Drawing.Spreadsheet.WorksheetDrawing();
worksheetDrawing.AddNamespaceDeclaration("xdr", "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing");
worksheetDrawing.AddNamespaceDeclaration("a", "http://schemas.openxmlformats.org/drawingml/2006/main");
drawing = new DocumentFormat.OpenXml.Spreadsheet.Drawing();
drawing.set_Id(new DocumentFormat.OpenXml.StringValue(worksheetPart.GetIdOfPart(drawingsPart)));
OXML_RU::appendChild(worksheet, drawing);
А imagePart создается для каждого изображения отдельный, поэтому его создание было вынесено в отдельный метод, который вызывается из перекрытого метода fillBody[1].
public void insertImage(
Filename _fileName
, int64 _posX
, int64 _posY
, int64 _width
, int64 _height
)
{
DocumentFormat.OpenXml.Packaging.ImagePart imagePart;
DocumentFormat.OpenXml.Drawing.Spreadsheet.ShapeProperties shapeProperties;
DocumentFormat.OpenXml.Drawing.Spreadsheet.NonVisualPictureProperties nonVisualPictureProperties;
DocumentFormat.OpenXml.Drawing.Spreadsheet.NonVisualDrawingProperties nonVisualDrawingProperties;
DocumentFormat.OpenXml.Drawing.Spreadsheet.BlipFill blipFill;
DocumentFormat.OpenXml.Drawing.Spreadsheet.Position position;
DocumentFormat.OpenXml.Drawing.Spreadsheet.Extent extent;
DocumentFormat.OpenXml.Drawing.Spreadsheet.Picture picture;
DocumentFormat.OpenXml.Drawing.Spreadsheet.AbsoluteAnchor AbsoluteAnchor;
DocumentFormat.OpenXml.Drawing.Stretch stretch;
DocumentFormat.OpenXml.Drawing.Blip blip;
DocumentFormat.OpenXml.Drawing.Transform2D transform2D;
DocumentFormat.OpenXml.Drawing.Extents extents;
DocumentFormat.OpenXml.Drawing.PresetGeometry presetGeometry;
System.IO.FileStream fileStream;
;
imagePart = drawingsPart.AddImagePart(DocumentFormat.OpenXml.Packaging.ImagePartType::Jpeg);
fileStream = new System.IO.FileStream(_fileName, System.IO.FileMode::Open);
imagePart.FeedData(fileStream);
fileStream.Close();
nonVisualDrawingProperties = new DocumentFormat.OpenXml.Drawing.Spreadsheet.NonVisualDrawingProperties();
nonVisualDrawingProperties.set_Id(OXML_RU::setUInt32Value(i * 22));
nonVisualDrawingProperties.set_Name(new DocumentFormat.OpenXml.StringValue(strFmt("%1%2", 'Pic', i * 22)));
nonVisualPictureProperties = new DocumentFormat.OpenXml.Drawing.Spreadsheet.NonVisualPictureProperties();
nonVisualPictureProperties.set_NonVisualDrawingProperties(nonVisualDrawingProperties);
nonVisualPictureProperties.set_NonVisualPictureDrawingProperties(new DocumentFormat.OpenXml.Drawing.Spreadsheet.NonVisualPictureDrawingProperties());
stretch = new DocumentFormat.OpenXml.Drawing.Stretch();
stretch.set_FillRectangle(new DocumentFormat.OpenXml.Drawing.FillRectangle());
blip = new DocumentFormat.OpenXml.Drawing.Blip();
blip.set_Embed(new DocumentFormat.OpenXml.StringValue(drawingsPart.GetIdOfPart(imagePart)));
blip.set_CompressionState(OXMLExp::getEnumValue(DocumentFormat.OpenXml.Drawing.BlipCompressionValues::Print));
blipFill = new DocumentFormat.OpenXml.Drawing.Spreadsheet.BlipFill();
blipFill.set_Blip(blip);
blipFill.set_SourceRectangle(new DocumentFormat.OpenXml.Drawing.SourceRectangle());
OXML_RU::appendChild(blipFill, stretch);
extents = new DocumentFormat.OpenXml.Drawing.Extents();
extents.set_Cx(new DocumentFormat.OpenXml.Int64Value(_width));
extents.set_Cy(new DocumentFormat.OpenXml.Int64Value(_height));
transform2D = new DocumentFormat.OpenXml.Drawing.Transform2D();
transform2D.set_Extents(extents);
shapeProperties = new DocumentFormat.OpenXml.Drawing.Spreadsheet.ShapeProperties();
shapeProperties.set_BlackWhiteMode(OXMLExp::getEnumValue(DocumentFormat.OpenXml.Drawing.BlackWhiteModeValues::Auto));
shapeProperties.set_Transform2D(transform2D);
presetGeometry = new DocumentFormat.OpenXml.Drawing.PresetGeometry();
presetGeometry.set_AdjustValueList(new DocumentFormat.OpenXml.Drawing.AdjustValueList());
presetGeometry.set_Preset(OXMLExp::getEnumValue(DocumentFormat.OpenXml.Drawing.ShapeTypeValues::Rectangle));
OXML_RU::appendChild(shapeProperties, presetGeometry);
OXML_RU::appendChild(shapeProperties, new DocumentFormat.OpenXml.Drawing.NoFill());
picture = new DocumentFormat.OpenXml.Drawing.Spreadsheet.Picture();
picture.set_NonVisualPictureProperties(nonVisualPictureProperties);
picture.set_BlipFill(blipFill);
picture.set_ShapeProperties(shapeProperties);
position = new DocumentFormat.OpenXml.Drawing.Spreadsheet.Position();
position.set_X(new DocumentFormat.OpenXml.Int64Value(_posX));
position.set_Y(new DocumentFormat.OpenXml.Int64Value(_posY));
extent = new DocumentFormat.OpenXml.Drawing.Spreadsheet.Extent();
extent.set_Cx(extents.get_Cx());
extent.set_Cy(extents.get_Cy());
absoluteAnchor = new DocumentFormat.OpenXml.Drawing.Spreadsheet.AbsoluteAnchor();
absoluteAnchor.set_Position(position);
absoluteAnchor.set_Extent(extent);
OXML_RU::appendChild(absoluteAnchor, picture);
OXML_RU::appendChild(absoluteAnchor, new DocumentFormat.OpenXml.Drawing.Spreadsheet.ClientData());
OXML_RU::appendChild(worksheetDrawing, absoluteAnchor);
}
После чего нужно вызвать worksheetDrawing.Save(drawingsPart).
Если необходимо вставлять в файл Excel с картинкой или диаграммой (т.е. имеется /xl/drawings) разрывы страниц, то нужно в класс OXMLWorksheet_RU добавить код, который вставляет в worksheet rowBreaks перед drawing[2].
protected void saveBreaks()
{
DocumentFormat.OpenXml.Spreadsheet.Break pageBreak;
SetEnumerator enumerator;
int idx;
DocumentFormat.OpenXml.Spreadsheet.Drawing drawing; //09.10.2017
if (this.rowBreaks().elements())
{
enumerator = this.rowBreaks().getEnumerator();
//BP deviation documented
rowBreaks = new DocumentFormat.OpenXml.Spreadsheet.RowBreaks();
//09.10.2017 -->
drawing = OXMLExp::getFirstChildType(worksheet.get_ChildElements(), 'DocumentFormat.OpenXml.Spreadsheet.Drawing');
if (!drawing)
{
//09.10.2017 <--
OXML_RU::appendChild(worksheet, rowBreaks);
//09.10.2017 -->
}
else
{
OXMLExp::insert(worksheet, rowBreaks, drawing, 'InsertBefore');
}
//09.10.2017 <--
Код метода OXMLExp::getFirstChildType
public static System.Object getFirstChildType(
System.Collections.IEnumerable _ie
, str _type)
{
System.Object objectCompare, obj;
System.Collections.IEnumerator ienum;
System.Type type, childType;
;
ienum = _ie.GetEnumerator();
type = CLRInterop::getType(_type);
while (ienum.MoveNext())
{
objectCompare = ienum.get_Current();
childType = objectCompare.GetType();
if (childType.Equals(type))
{
return objectCompare;
}
}
return obj;
}
Метод OXMLExp::insert
public static System.Object insert(
System.Object _parent
, System.Object _child
, System.Object _childRef
, str _methodName
)
{
Array arrayT;
System.Type type;
System.Object child;
arrayT = new Array(Types::Class);
arrayT.value(1, _child);
arrayT.value(2, _childRef);
type = _child.GetType();
child = OXML_RU::invokeGeneric( _parent
, _methodName
, type
, arrayT
);
return child;
}
Диаграмма Excel.
Для вставки диаграммы в стандартные шаблоны Excel, которые используются классами наследниками XMLExcelReport_RU, также необходимо создавать /xl/charts/styles и /xl/charts/colors, для создания которых в Open XML SDK нет классов, что видно в автоматически генерируемом коде утилитой OpenXMLSDKToolV25. Раздел формируется данными в виде строки. (ниже приведен код добавления xl/charts/colors)
ExtendedPart extendedPart1 = chartPart1.AddExtendedPart("http://schemas.microsoft.com/office/2011/relationships/chartColorStyle", "application/vnd.ms-office.chartcolorstyle+xml", "xml", "rId2");
GenerateExtendedPart1Content(extendedPart1);
// Generates content of part.
private void GeneratePartContent(ExtendedPart part)
{
System.IO.Stream data = GetBinaryDataStream(partData);
part.FeedData(data);
data.Close();
}
#region Binary Data
private string partData = "PGNzOmNoYXJ0U3R5b……NoYXJ0U3R5bGU+";
private System.IO.Stream GetBinaryDataStream(string base64String)
{
return new System.IO.MemoryStream(System.Convert.FromBase64String(base64String));
}
Поэтому для использования диаграммы было принято решение создавать диаграмму в шаблоне, а потом в коде менять только диапазон данных и подписей. Т.к. диаграмма уже будет в шаблоне, то создавать новые drawingsPart и worksheetDrawing не нужно.
oxmlWorkBook_RU = document.workbook(); worksheetPart = oxmlWorkBook_RU.getWorksheet().worksheetPart(); worksheet = worksheetPart.get_Worksheet(); drawingsPart = worksheetPart.get_DrawingsPart(); worksheetDrawing = drawingsPart.get_WorksheetDrawing();
Метод изменения диапазонов диаграммы. Метод рассчитан для вызова один раз, например в методе createReport, также можно динамически рассчитывать внутри класса наследника XMLExcelReport_RU количество вставляемых записей и передавать их методу, и использовать вместо макроса #Rows.
#define.Rows(3)
private void updateChartSeries()
{
DocumentFormat.OpenXml.Drawing.Charts.BarChartSeries barChartSeries;
DocumentFormat.OpenXml.Drawing.Charts.CategoryAxisData categoryAxisData;
DocumentFormat.OpenXml.Drawing.Charts.Formula formula;
DocumentFormat.OpenXml.Drawing.Charts.Values values;
DocumentFormat.OpenXml.Drawing.Charts.NumberReference numberReference;
DocumentFormat.OpenXml.Drawing.Charts.BarChart barChart;
DocumentFormat.OpenXml.Drawing.Charts.PlotArea plotArea;
DocumentFormat.OpenXml.Drawing.Charts.Chart chart;
DocumentFormat.OpenXml.Drawing.Charts.ChartSpace chartSpace;
DocumentFormat.OpenXml.Packaging.ChartPart chartPart;
System.Collections.IEnumerable ie;
System.Collections.IEnumerator ienum;
SetEnumerator setEnum;
int curCol;
container columnChar;
;
columnChar = ['CZ', 'DA'];
ie = drawingsPart.get_ChartParts();
ienum = ie.GetEnumerator();
ienum.MoveNext();
chartPart = ienum.get_Current();
chartSpace = chartPart.get_ChartSpace();
chart = OXMLExp::getFirstChildType(chartSpace.get_ChildElements(), 'DocumentFormat.OpenXml.Drawing.Charts.Chart');
plotArea = chart.get_PlotArea();
barChart = OXMLExp::getFirstChildType(plotArea.get_ChildElements(), 'DocumentFormat.OpenXml.Drawing.Charts.BarChart');
setEnum = OXMLExp::getSetEnumChildType(barChart.get_ChildElements(), 'DocumentFormat.OpenXml.Drawing.Charts.BarChartSeries');
curCol = 1;
while (setEnum.moveNext())
{
barChartSeries = setEnum.current();
categoryAxisData = OXMLExp::getFirstChildType(barChartSeries.get_ChildElements(), 'DocumentFormat.OpenXml.Drawing.Charts.CategoryAxisData');
numberReference = categoryAxisData.get_NumberReference();
formula = numberReference.get_Formula();
formula.set_Text(strFmt('Invoice4Paym!$DB$1:$DB$%1', #Rows));
values = OXMLExp::getFirstChildType(barChartSeries.get_ChildElements(), 'DocumentFormat.OpenXml.Drawing.Charts.Values');
numberReference = values.get_NumberReference();
formula = numberReference.get_Formula();
formula.set_Text(strFmt('Invoice4Paym!$%1$1:$%1$%2', conPeek(columnChar, curCol), #Rows));
curCol ++;
}
}
Метод OXMLExp::getSetEnumChildType
public static SetEnumerator getSetEnumChildType(
System.Collections.IEnumerable _ie
, str _type)
{
System.Object objectCompare;
System.Collections.IEnumerator ienum;
System.Type type, childType;
Set set;
;
ienum = _ie.GetEnumerator();
type = CLRInterop::getType(_type);
set = new Set(Types::Class);
while (ienum.MoveNext())
{
objectCompare = ienum.get_Current();
childType = objectCompare.GetType();
if (childType.Equals(type))
{
set.add(objectCompare);
}
}
return set.getEnumerator();
}
Все комментарии
Чтобы оставить комментарий, необходимо войти или зарегистрироваться.
Пока нет комментариев. Будьте первым!