【问题标题】:How to get a shape/picture coordinates in pixels in an excel file (xlsx) using EPPlus如何使用 EPPlus 在 excel 文件 (xlsx) 中以像素为单位获取形状/图片坐标
【发布时间】:2016-04-06 22:14:57
【问题描述】:

有没有人成功地使用 epplus 获得了 excel 2010 文件中表格中形状或图片的 x 和 y 坐标(以像素为单位)?

【问题讨论】:

    标签: c# epplus epplus-4


    【解决方案1】:

    我终于找到了使用内部 xml 数据的解决方案,似乎 excel 使用了一个名为 EMU 的度量单位,即 equal to 1/9525 pixel。解决方法如下

    public static Rectangle GetDimensions(string shapeName, ExcelWorksheet sheet)
    {
      const int EMU = 9525;
      var shapeBaseNodexPath = string.Format("//*[@name='{0}']", shapeName);
    
      //get node that contains name of the shape
      var shapeNameNode = sheet.Drawings.DrawingXml.SelectSingleNode(shapeBaseNode);
    
      if (shapeNameNode == null)
        throw new ArgumentException("Invalid shape name");
      //go 2 levels up and select shape properties node <a:xfrm> node
      var propertiesNode = shapeNameNode
        .SelectSingleNode("../../*[local-name() = 'spPr']/*[local-name() = 'xfrm']");
      if (propertiesNode == null)
        throw new InvalidOperationException("Could not parse Excel file xml data");
    
      //get coodinates and size nodes
      var locationNode = propertiesNode.SelectSingleNode("*[local-name() = 'off']");
      var sizeNode = propertiesNode.SelectSingleNode("*[local-name() = 'ext']");
    
      //create Rectangle
      int x, y, w, h = 0;
      x = int.Parse(locationNode.Attributes["x"].Value) / EMU;
      y = int.Parse(locationNode.Attributes["y"].Value) / EMU;
      w = int.Parse(sizeNode.Attributes["cx"].Value) / EMU;
      h = int.Parse(sizeNode.Attributes["cy"].Value) / EMU;
    
      return new Rectangle(x, y, w, h);
    }
    

    【讨论】:

    • 你从哪里得到这个实际数字 (1045,451)?当我对我用一张图片创建的 excel 文件运行你的函数时,你的实际上有点短。在我的测试中,我的 func 给出了 140 的宽度,而你的给出了 131。我把图片放在了 inisde Col C 中,如果我在 Excel 本身中计算 Cols A 和 B 的宽度,我得到 137。3 像素的差异看起来很适合Col C 的开头和图像之间的间隙。我更新了我的帖子以说明我的意思。请注意,如果我将两个列宽设置为相同(例如 65 像素),那么 BOTH funcs 将得到 131。
    • 我使用了两种方法,第一种是使用屏幕坐标工具,第二种是通过检查 xlsx 文件中的图纸 xml 文件,我的方法基于这两种方法给了我相同的结果。根据此来源officeopenxml.com/drwSp-size.phpxfrm 标签中的值指定 EMU 中形状的坐标和大小。
    【解决方案2】:

    不幸的是,它并不漂亮。您真正需要的函数是ExcelDrawingBase.cs 文件中的GetPixelTopGetPixelLeft,但它们被标记为internal:http://epplus.codeplex.com/SourceControl/latest#EPPlus/Drawing/ExcelDrawingBase.cs。所以你不能真正调用它们而不修改源代码。

    最简单的方法就是重新创建您自己的版本。这是一个快速剪切/粘贴(确保在投入生产之前做好 QA):

    public int DrawingPixelX(ExcelWorksheet worksheet, ExcelDrawing drawing)
    {
        const int emuPerPixel = ExcelDrawing.EMU_PER_PIXEL;
        decimal mdw = worksheet.Workbook.MaxFontWidth;
    
        var pix = 0;
        for (var i = 1; i <= drawing.From.Column; i++)
            pix += (int)decimal.Truncate(((256 * (decimal)worksheet.Column(i).Width + decimal.Truncate(128 / mdw)) / 256) * mdw);
    
        pix += drawing.From.ColumnOff / emuPerPixel;
        return pix;
    }
    
    public int DrawingPixelY(ExcelWorksheet worksheet, ExcelDrawing drawing)
    {
        const int emuPerPixel = ExcelDrawing.EMU_PER_PIXEL;
    
        var piy = 0;
        for (var i = 1; i <= drawing.From.Row; i++)
            piy += (int)(worksheet.Row(i).Height / 0.75);
    
        piy += drawing.From.RowOff / emuPerPixel;
        return piy;
    }
    

    你可以这样使用它:

    var workbook = package.Workbook;
    var ws = workbook.Worksheets.First();
    var pic = ws.Drawings.First();
    
    Console.WriteLine(DrawingPixelX(ws, pic));
    Console.WriteLine(DrawingPixelY(ws, pic));
    

    附加图像的左上角分别为 141 和 43。

    【讨论】:

    • TY 寻求帮助,我已经尝试过这种方法,但不幸的是没有给出“X”的准确坐标,我有一个位于 (1045,451) 像素中的形状,上面我得到不同的值x=1067.
    • 当 x > 1000 时差距变得更加明显,我做了另一个测试,结果是 1895 与实际 1853。
    • 你是如何获得“真实”的?我尝试了相同的不同列宽,总计超过 1200,并且两个函数根据 GUI 中的列宽计数返回相同的(正确的数字)。
    • 请在下面查看我的评论。话题越来越火,不知道谁是对的,你还是我:-)
    【解决方案3】:

    您可以从内部ExcelDrawing 内部字段_widht_height 中读取它。感谢@Ernie 指向ExcelDrawingBase.cs

    shape = (ExcelShape) xls.Workbook.Worksheets[1].Drawings["MyShape"];
    var width = (int) shape.GetType()
            .GetProperty("_width", BindingFlags.NonPublic | BindingFlags.Instance)
            .GetValue(shape);
    var height = (int) shape.GetType()
            .GetProperty("_height", BindingFlags.NonPublic | BindingFlags.Instance)
            .GetValue(shape);   
    

    编辑:此方法仅在 EPP 4 上测试,当前版本 EPP 5 内部实现可能已更改。

    【讨论】:

    • 嗨,感谢这个例子。我试过这个,但我在 GetProperty("_width") 步骤返回 Null 值,然后 GetValue(shape) 失败。有没有人成功做到这一点?
    • @DK2014 如果_width_height 是字段而不是属性,那么您可能只需要使用.GetField() 而不是.GetProperty
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多