0

我正在尝试在生成电子表格后将图像添加到由 closedxml 使用 openxml 生成的 excel 文档中(因为 closedxml 目前无法插入图像)。

我可以正确生成 excel 文件,并且打开正常,但是当我尝试将图像插入并尝试在 excel 中打开它时会出错。

我使用了一个小型控制台应用程序,发现错误是

Error 1
Description: The element has unexpected child element 'http://schemas.openxmlfor
mats.org/spreadsheetml/2006/main:drawing'.
Path: /x:worksheet[1]
Part: /xl/worksheets/sheet.xml
-------------------------------------------

电子表格如下所示:

<?xml version="1.0" encoding="utf-8"?>
<x:worksheet xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
    <x:sheetPr>
        <x:outlinePr summaryBelow="1" summaryRight="1" />
    </x:sheetPr>
    <x:dimension ref="A1:M4" />
    <x:sheetViews>
        <x:sheetView tabSelected="0" workbookViewId="0" />
    </x:sheetViews>
    <x:sheetFormatPr defaultRowHeight="15" />
    <x:cols>
        ...
    </x:cols>
    <x:sheetData>
        ...
    </x:sheetData>
    <x:printOptions horizontalCentered="0" verticalCentered="0" headings="0" gridLines="0" />
    <x:pageMargins left="0.75" right="0.75" top="0.75" bottom="0.5" header="0.5" footer="0.75" />
    <x:pageSetup paperSize="1" scale="100" pageOrder="downThenOver" orientation="default" blackAndWhite="0" draft="0" cellComments="none" errors="displayed" />
    <x:headerFooter />
    <x:tableParts count="0" />
    <x:drawing r:id="R701e4d0efd7143ee" />
</x:worksheet>

采取

 
section out 似乎允许文件正确验证。

我也尝试过手动创建一个空白的 excel 文档并添加一个图像,它看起来非常相似:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">
    <dimension ref="A1"/>
    <sheetViews>
        <sheetView tabSelected="1" workbookViewId="0">
            <selection activeCell="D14" sqref="D14"/>
        </sheetView>
    </sheetViews>
    <sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/>
    <sheetData/>
    <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
    <drawing r:id="rId1"/>
</worksheet>

我注意到的一件事是它们没有生成的命名空间(x),但我不知道这是否导致问题。

谁能看到我错过了什么?

我用来插入图像的代码如下:

public static void InsertImage(this Worksheet ws, long x, long y, long? width, long? height, string sImagePath)
{
    try
    {
        var wsp = ws.WorksheetPart;
        var dp = default(DrawingsPart);
        var imgp = default(ImagePart);
        var wsd = default(WorksheetDrawing);

        var ipt = default(ImagePartType);
        switch (sImagePath.Substring(sImagePath.LastIndexOf('.') + 1).ToLower())
        {
            case "png":
                ipt = ImagePartType.Png;
                break; // TODO: might not be correct. Was : Exit Select

                break;
            case "jpg":
            case "jpeg":
                ipt = ImagePartType.Jpeg;
                break; // TODO: might not be correct. Was : Exit Select

                break;
            case "gif":
                ipt = ImagePartType.Gif;
                break; // TODO: might not be correct. Was : Exit Select

                break;
            default:
                return;
        }

        if (wsp.DrawingsPart == null)
        {
            //----- no drawing part exists, add a new one

            dp = wsp.AddNewPart<DrawingsPart>();
            imgp = dp.AddImagePart(ipt, wsp.GetIdOfPart(dp));
            wsd = new WorksheetDrawing();
        }
        else
        {
            //----- use existing drawing part

            dp = wsp.DrawingsPart;
            imgp = dp.AddImagePart(ipt);
            dp.CreateRelationshipToPart(imgp);
            wsd = dp.WorksheetDrawing;
        }

        using (var fs = new FileStream(sImagePath, FileMode.Open))
        {
            imgp.FeedData(fs);
        }

        var imageNumber = 1;
        if (imageNumber == 1)
        {
            var drawing = new Drawing();
            drawing.Id = dp.GetIdOfPart(imgp);
            ws.Append(drawing);
        }

        var nvdp = new NonVisualDrawingProperties();
        nvdp.Id = new UInt32Value(Convert.ToUInt32(1024 + imageNumber));
        nvdp.Name = "Picture " + imageNumber.ToString();
        nvdp.Description = "Picture";
        var picLocks = new PictureLocks();
        picLocks.NoChangeAspect = true;
        picLocks.NoChangeArrowheads = true;
        var nvpdp = new NonVisualPictureDrawingProperties();
        nvpdp.PictureLocks = picLocks;
        var nvpp = new NonVisualPictureProperties();
        nvpp.NonVisualDrawingProperties = nvdp;
        nvpp.NonVisualPictureDrawingProperties = nvpdp;

        var stretch = new Stretch();
        stretch.FillRectangle = new FillRectangle();

        var blipFill = new BlipFill();
        var blip = new Blip();
        blip.Embed = dp.GetIdOfPart(imgp);
        blip.CompressionState = BlipCompressionValues.Print;
        blipFill.Blip = blip;
        blipFill.SourceRectangle = new SourceRectangle();
        blipFill.Append(stretch);

        var t2d = new Transform2D();
        var offset = new Offset();
        offset.X = 0;
        offset.Y = 0;
        t2d.Offset = offset;
        var bm = new Bitmap(sImagePath);

        var extents = new Extents();

        if (width == null)
        {
            extents.Cx = Convert.ToInt64(bm.Width)*
                         Convert.ToInt64(Math.Truncate(Convert.ToSingle(914400)/bm.HorizontalResolution));
        }
        else
        {
            extents.Cx = width;
        }

        if (height == null)
        {
            extents.Cy = Convert.ToInt64(bm.Height)*
                         Convert.ToInt64(Math.Truncate(Convert.ToSingle(914400)/bm.VerticalResolution));
        }
        else
        {
            extents.Cy = height;
        }

        bm.Dispose();
        t2d.Extents = extents;
        var sp = new ShapeProperties();
        sp.BlackWhiteMode = BlackWhiteModeValues.Auto;
        sp.Transform2D = t2d;
        var prstGeom = new PresetGeometry();
        prstGeom.Preset = ShapeTypeValues.Rectangle;
        prstGeom.AdjustValueList = new AdjustValueList();
        sp.Append(prstGeom);
        sp.Append(new NoFill());

        var picture = new Picture();
        picture.NonVisualPictureProperties = nvpp;
        picture.BlipFill = blipFill;
        picture.ShapeProperties = sp;

        var pos = new Position();
        pos.X = x;
        pos.Y = y;
        var ext = new Extent();
        ext.Cx = extents.Cx;
        ext.Cy = extents.Cy;
        var anchor = new AbsoluteAnchor();
        anchor.Position = pos;
        anchor.Extent = ext;
        anchor.Append(picture);
        anchor.Append(new ClientData());
        wsd.Append(anchor);
        wsd.Save(dp);
    }
    catch (Exception ex)
    {
        // or do something more interesting if you want
        throw ex;
    }
}

public static void InsertImage(MemoryStream stream, long x, long y, string sImagePath)
{
    using (var mySpreadsheet = SpreadsheetDocument.Open(stream, true))
    {
        var workbookPart = mySpreadsheet.WorkbookPart;
        var sheets = mySpreadsheet.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
        var relationshipId = sheets.First().Id.Value;
        var worksheetPart = (WorksheetPart) mySpreadsheet.WorkbookPart.GetPartById(relationshipId);
        var workSheet = worksheetPart.Worksheet;
        //Dim sheets As worksheetpart = mySpreadsheet.WorkbookPart.Workbook.
        // For each sheet, display the sheet information.
        long width = 9525*193;
        long height = 9525*80;
        InsertImage(workSheet, x, y, width, height, sImagePath);
    }
}
4

5 回答 5

2

哈哈哈哈看到的时候吓死我了 我正在做完全相同的事情。

我正在使用封闭的 XML 生成电子表格,然后使用文森特的代码插入图像。

不幸的是我有同样的问题,但它与被声明为 1 的 imageNumber 无关

但是我找到了一种解决方法:

插入图像后,只需使用关闭的 xml 重新打开文档并再次保存即可。

我只是用这个

workbook = new XLWorkbook(filepath);
workbook.save();

之后它应该可以毫无问题地打开。

希望这可以帮助其他有类似问题的人

编辑:

通过更改此段,我能够完全解决此问题:

var imageNumber = dp.ImageParts.Count<ImagePart>();
if (imageNumber == 1)
{
    var drawing = new Drawing();
    drawing.Id = dp.GetIdOfPart(imgp);
    ws.Append(drawing);
}

更改ws.Append(drawing);

ws.InsertBefore(drawing, ws.Last)

然后它会把它放在最后一个对我来说是的孩子之前<x:tableParts count="0"/>

把它放在那之后似乎是导致问题的原因

于 2014-04-22T10:24:09.987 回答
1

问题可能是您将 imageNumber 分配为 1。

var imageNumber = 1;

您可能会发现这会有所帮助:

var imageNumber = dp.ImageParts.Count<ImagePart>();

此外,该代码看起来与我在博客文章中发布的内容非常相似。没关系。此外,您可能会发现SpreadsheetLight很有帮助。它是一个类似于 ClosedXml 的电子表格库,它可以很好地插入图像。免责声明:我写了 SpreadsheetLight。

于 2012-10-09T14:28:32.910 回答
1

我有一个类似的问题。当我对 ClosedXML 创建的文档和我在 Excel 中放置图像的文档进行 OpenXML Productivity Tools 比较时,我发现 ClosedXML 插入了一些元素节点,虽然它们不是无效的,但似乎使带有绘图元素的工作表无效. 我删除了这些元素,带有图像的电子表格可以在 Excel 中正常打开。 这是一个 kluge,但它正在工作。此代码位于 InsertImage 方法的顶部:

    //Remove superfluous worksheet elements. These break the inlcusion of the logo image.
    var element = ws.FirstChild;
    do
    {
        if(element.LocalName == null)
            continue;
        switch (element.LocalName)
        {
            case "pageSetup":
            case "headerFooter":
            case "tableParts":
                var prevElement = element;
                element = element.NextSibling();
                ws.RemoveChild<OpenXmlElement>(prevElement);
                break;
            default:
                element = element.NextSibling();
                break;
        }
    }
    while(element != null);
    // End of element removal

高温高压

安格斯

于 2013-11-01T17:51:13.230 回答
0

OpenXml 并不是最容易理解的东西。

他们没有告诉你的一件事是元素的顺序通常很重要。如前所述,索引号不是原因,您需要重新排序 xml 标记,以便元素位于tableParts元素之前drawing

或者,由于您没有 TableParts,您可以完全删除该元素

所以从你的例子

<?xml version="1.0" encoding="utf-8"?>
<x:worksheet xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
    <x:sheetPr>
        <x:outlinePr summaryBelow="1" summaryRight="1" />
    </x:sheetPr>
    <x:dimension ref="A1:M4" />
    <x:sheetViews>
        <x:sheetView tabSelected="0" workbookViewId="0" />
    </x:sheetViews>
    <x:sheetFormatPr defaultRowHeight="15" />
    <x:cols>
        ...
    </x:cols>
    <x:sheetData>
        ...
    </x:sheetData>
    <x:printOptions horizontalCentered="0" verticalCentered="0" headings="0" gridLines="0" />
    <x:pageMargins left="0.75" right="0.75" top="0.75" bottom="0.5" header="0.5" footer="0.75" />
    <x:pageSetup paperSize="1" scale="100" pageOrder="downThenOver" orientation="default" blackAndWhite="0" draft="0" cellComments="none" errors="displayed" />
    <x:headerFooter />
    <x:drawing r:id="R701e4d0efd7143ee" />
    <x:tableParts count="0" />
</x:worksheet>
于 2015-02-24T09:52:49.440 回答
0

如果您正在写入新的 Excel 文件,则以下代码很好:

if( dp.ImageParts.Count<ImagePart>() == 1 )
{
    var drawing = new Drawing();
    drawing.Id = dp.GetIdOfPart(imgp);
    ws.Append(drawing);
}

如果您正在写入可能已经有绘图部分的现有文件(但没有图像......形状似乎是绘图部分的一部分),那么这将导致您的文档无法通过验证,甚至无法完全打开.

我通过执行以下操作解决了这个问题:

if (dp.ImageParts.Count<ImagePart>() == 1)
{
    Drawing drawing = workSheet.GetFirstChild<Drawing>();
    if (drawing == null)
    {
        drawing = new Drawing();
        drawing.Id = dp.GetIdOfPart(imgp);
        workSheet.InsertAfter(drawing, workSheet.LastChild);
    }
}

似乎每个工作表只有 1 个绘图部分......我花了一段时间才找到问题,因为不幸的是它并不是立即显而易见的。

于 2015-05-12T18:21:35.343 回答