Epplus SetPosition图片问题

JP *_*ons 7 excel c#-4.0 epplus

我正在使用Epplus库在Asp.Net C#中生成Excel 2010及以上的兼容文件.我使用的是版本3.1.2,这是目前最新的版本.

我首先设置行高,然后添加这样的图片:

ExcelPackage pck = new ExcelPackage();
var ws = pck.Workbook.Worksheets.Add("sheet 1");
while (i < dt.Rows.Count + offset)
{
    ws.Row(i).Height = 84;
    i++;
}
Run Code Online (Sandbox Code Playgroud)

dt是DataRows的DataTable.设置高度后,我再次循环遍历行以添加图片

while (i < dt.Rows.Count + offset)
{
    var prodImg = ws.Drawings.AddPicture(dr["code"].ToString(), new FileInfo(path));
    prodImg.SetPosition(i - 1, 0, 14, 0);
    prodImg.SetSize(75);
}
Run Code Online (Sandbox Code Playgroud)

这有效,但这不是:

var prodImg = ws.Drawings.AddPicture(dr["code"].ToString(), new FileInfo(path));
int w = prodImg.Image.Width;
int h = prodImg.Image.Height;

if (h > 140) // because height of 84 is 140 pixels in excel
{
    double scale = h / 140.0;
    w = (int)Math.Floor(w / scale);
    h = 140;
}

int xOff = (150 - w) / 2;
int yOff = (140 - h) / 2;

prodImg.SetPosition(i - 1, xOff, 11, yOff);
prodImg.SetSize(w, h);
Run Code Online (Sandbox Code Playgroud)

这导致偏离中心的图像和未图像化的图像.然后这个代码在同一个循环中:

var prodImgDm = ws.Drawings.AddPicture("bcdm" + dr["code"].ToString(), new FileInfo(pathDm));
prodImgDm.SetPosition(i - 1, 25, 15, 40);
prodImgDm.SetSize(100);
Run Code Online (Sandbox Code Playgroud)

这确实有效.图片prodImgDm是具有静态宽度和高度的数据矩阵图像,因为它们总是很小/很小,所以不需要调整大小.因此,如果没有SetSize在某些行中,它可以工作,而在其他一些行中,它不起作用.真奇怪,因为代码是一样的.它可能是库和/或Excel中的某些内容.也许我使用它错了?任何epplus图片专家?

提前致谢!!

编辑有时一张图片胜过千言万语,所以这里是截图.如您所见,产品图像在单元格中不是水平和垂直对齐的.最右边的数据矩阵有时会缩放120%左右,即使我设置的SetSize(100)时候也是如此.因此,最后一个数据矩阵具有正确的大小...我已经找到了这个SO线程但是这对我没有帮助,我想.

epplus图像

编辑 2013/04/09 Essenpillai给了我一个提示

pck.DoAdjustDrawings = false;
Run Code Online (Sandbox Code Playgroud)

但这给了我更奇怪的图像:

doadjustdrawings

datamatrix仍在按行更改.在行上是好的,另一个不是.并且ean13代码太宽了.

Ati*_*ris 2

我对Epplus 库也有同样的问题。
在我在代码中找不到如何解决此问题的解决方案后,我检查了该库的源代码。Epplus创建Excel图片总是像twoCellAnchor绘图一样。在文件中,您可以使用以下代码xlsx找到:drawingXYZ.xml

<xdr:twoCellAnchor editAs="oneCell">
  <xdr:from> ... </xdr:from>
  <xdr:to> ... </xdr:to>
  <xdr:pic>
  ...
</xdr:twoCellAnchor>
Run Code Online (Sandbox Code Playgroud)

因此,图片总是连接到两个单元格,这不是 Epplus 库的可变部分。您可以在ExcelDrawing.cs文件中找到这部分代码。

  XmlElement drawNode = _drawingsXml.CreateElement(
    "xdr", "twoCellAnchor", ExcelPackage.schemaSheetDrawings);
  colNode.AppendChild(drawNode);
Run Code Online (Sandbox Code Playgroud)

您可以轻松创建您自己的该 dll 副本。好消息是您只需修改两个文件即可解决此问题。所以..

从该站点下载 Epplus 库的源代码副本并在 Visual Studio 中打开。

我们需要生成 oneCellAnchor 的代码drawing因此我们必须删除<xdr:to>图片元素并创建<xdr:ext />以图片尺寸为参数的元素。
新的 xml 结构将如下所示:

<xdr:oneCellAnchor editAs="oneCell">
  <xdr:from> ... </xdr:from>
  <xdr:ext cx="1234567" cy="7654321" />
  <xdr:pic>
  ...
</xdr:oneCellAnchor>
Run Code Online (Sandbox Code Playgroud)

好的,那么,该怎么做呢?

Epplus代码的变化

ExcelDrawings.cs此处链接到文件

  1. 首先我们修改CreateDrawingXml()里面的方法ExcelDrawings.cs。为了保留原始功能,我们添加一个oneCellAnchor具有默认值的可选参数(如果是 create )。在方法中,基于此参数,我们创建一个或两个单元锚点并创建或不创建<xdr:to>元素。

该方法代码的重要部分:

private XmlElement CreateDrawingXml(bool twoCell = true) { 
  if (DrawingXml.OuterXml == "") 
  { ... } // not changed
  XmlNode colNode= _drawingsXml.SelectSingleNode("//xdr:wsDr", NameSpaceManager);
  //First change in method code
  XmlElement drawNode;
  if (twoCell)
    drawNode = _drawingsXml.CreateElement(
      "xdr", "twoCellAnchor", ExcelPackage.schemaSheetDrawings);
  else
    drawNode = _drawingsXml.CreateElement(
      "xdr", "oneCellAnchor", ExcelPackage.schemaSheetDrawings);
  colNode.AppendChild(drawNode);

  //Add from position Element; // Not changed
  XmlElement fromNode = _drawingsXml.CreateElement(
    "xdr", "from", ExcelPackage.schemaSheetDrawings);
  drawNode.AppendChild(fromNode);
  fromNode.InnerXml = "<xdr:col>0</xdr:col><xdr:colOff>0</xdr:colOff>"
    + "<xdr:row>0</xdr:row><xdr:rowOff>0</xdr:rowOff>";

  //Add to position Element;
  //Second change in method
  if (twoCell)
  {
    XmlElement toNode = _drawingsXml.CreateElement(
      "xdr", "to", ExcelPackage.schemaSheetDrawings);
    drawNode.AppendChild(toNode);
    toNode.InnerXml = "<xdr:col>10</xdr:col><xdr:colOff>0</xdr:colOff>"
      + "<xdr:row>10</xdr:row><xdr:rowOff>0</xdr:rowOff>";
  }
  return drawNode;
}
Run Code Online (Sandbox Code Playgroud)

AddPicture然后我们在同一个文件中修改两个方法:

public ExcelPicture AddPicture(string Name, Image image, Uri Hyperlink)
{
  if (image != null) {
    if (_drawingNames.ContainsKey(Name.ToLower())) {
      throw new Exception("Name already exists in the drawings collection");
    }
    XmlElement drawNode = CreateDrawingXml(false);
    // Change: we need create element with dimensions
    // like: <xdr:ext cx="3857625" cy="1047750" />
    XmlElement xdrext = _drawingsXml.CreateElement(
      "xdr", "ext", ExcelPackage.schemaSheetDrawings);
    xdrext.SetAttribute("cx", 
      (image.Width * ExcelDrawing.EMU_PER_PIXEL).ToString());
    xdrext.SetAttribute("cy", 
      (image.Height * ExcelDrawing.EMU_PER_PIXEL).ToString());
    drawNode.AppendChild(xdrext);
    // End of change, next part of method is the same:
    drawNode.SetAttribute("editAs", "oneCell");
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

这个方法作为FileInfo输入参数:

public ExcelPicture AddPicture(string Name, FileInfo ImageFile, Uri Hyperlink)
{
  if (ImageFile != null) {
    if (_drawingNames.ContainsKey(Name.ToLower())) {
      throw new Exception("Name already exists in the drawings collection");
    }
    XmlElement drawNode = CreateDrawingXml(false);
    // Change: First create ExcelPicture object and calculate EMU dimensions
    ExcelPicture pic = new ExcelPicture(this, drawNode, ImageFile, Hyperlink);
    XmlElement xdrext = _drawingsXml.CreateElement(
      "xdr", "ext", ExcelPackage.schemaSheetDrawings);
    xdrext.SetAttribute("cx", 
      (pic.Image.Width * ExcelDrawing.EMU_PER_PIXEL).ToString());
    xdrext.SetAttribute("cy", 
      (pic.Image.Height * ExcelDrawing.EMU_PER_PIXEL).ToString());
    drawNode.AppendChild(xdrext);
    // End of change, next part of method is the same (without create pic object)
    drawNode.SetAttribute("editAs", "oneCell");
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

所以,这些都是重要的代码。现在我们必须更改搜索节点的代码并保留元素的顺序。

private void AddDrawings()我们改变xpath

XmlNodeList list = _drawingsXml.SelectNodes(
  "//xdr:twoCellAnchor", NameSpaceManager);
Run Code Online (Sandbox Code Playgroud)

对此

XmlNodeList list = _drawingsXml.SelectNodes(
  "//(xdr:twoCellAnchor or xdr:oneCellAnchor)", NameSpaceManager);
Run Code Online (Sandbox Code Playgroud)

全部都在这个文件中,现在我们更改
ExcelPicture.cs链接到此处的文件

原始代码查找节点以在构造函数中附加下一个代码,如下所示:

node.SelectSingleNode("xdr:to",NameSpaceManager);
Run Code Online (Sandbox Code Playgroud)

因为我们并不总是创建<xdr:to>元素,所以我们更改此代码:

internal ExcelPicture(ExcelDrawings drawings, XmlNode node
  , Image image, Uri hyperlink) 
  : base(drawings, node, "xdr:pic/xdr:nvPicPr/xdr:cNvPr/@name")
{
  XmlElement picNode = node.OwnerDocument.CreateElement(
    "xdr", "pic", ExcelPackage.schemaSheetDrawings);
  // Edited: find xdr:to, or xdr:ext if xdr:to not exists
  XmlNode befor = node.SelectSingleNode("xdr:to",NameSpaceManager);
  if (befor != null && befor.Name == "xdr:to")
    node.InsertAfter(picNode, befor);
  else {
    befor = node.SelectSingleNode("xdr:ext", NameSpaceManager);
    node.InsertAfter(picNode, befor);
  }
  // End of change, next part of constructor is unchanged
  _hyperlink = hyperlink;
  ...
}
Run Code Online (Sandbox Code Playgroud)

对于以 FileInfo 作为输入参数的第二个构造函数也是如此:

internal ExcelPicture(ExcelDrawings drawings, XmlNode node
  , FileInfo imageFile, Uri hyperlink) 
  : base(drawings, node, "xdr:pic/xdr:nvPicPr/xdr:cNvPr/@name")
{
  XmlElement picNode = node.OwnerDocument.CreateElement(
    "xdr", "pic", ExcelPackage.schemaSheetDrawings);
  // Edited: find xdr:to, or xdr:ext if xdr:to not exists
  XmlNode befor = node.SelectSingleNode("xdr:to", NameSpaceManager);
  if (befor != null && befor.Name == "xdr:to")
    node.InsertAfter(picNode, befor);
  else {
    befor = node.SelectSingleNode("xdr:ext", NameSpaceManager);
    node.InsertAfter(picNode, befor);
  }
  // End of change, next part of constructor is unchanged
  _hyperlink = hyperlink;
  ...
Run Code Online (Sandbox Code Playgroud)

现在,图片被创建为oneCellAnchor. 如果需要,您可以AddPicture为展位变体创建多种方法。最后一步是构建此项目并创建您自己的自定义EPPlus.dll. 然后关闭使用此 dll 的项目EPPlus.dllEPPlus.pdbEPPlus.XML在项目内的同一位置复制新文件(备份并替换原始 dll 文件)(因此您不需要对项目引用或设置进行任何更改)。
然后打开并重建您的项目,并尝试这是否可以解决您的问题。