zoukankan      html  css  js  c++  java
  • 关于OpenXml SpreadSheet列宽根据内容的Auto-suitability

          因为之前接到的一个需求,让excel的宽度自动适应。所以最近一直在看Excel相关内容,从结构到.net的两个类库OpenXml和Office.Interop.Excel,再到一些具体的使用。
      这次学习花费了很多东西,主要陷入了两个误区,一个是一开始先入为主的认为Openxml中微软肯定有解决方案(我以为功能很简单,而且Interop.Excel是有解决方法的,但是实际上并没有),另一个在style.xml里面陷入误区了(很想当然的认为了自动列宽应该在stylesheet.xml里面),结果好几天都是毫无头绪。好心塞。。。
    后来终于找到了可爱的Columns类和Column类,然而。。。仔细阅读,逐个属性、方法的检查之后,居然没有解决方案,Width属性要给具体值(然而当初脑补的有个什么AutoFit()这种名字的方法。。。too naive)。
      后来在StackOverflow上找到了半个答案。大概意思就是:BestFit 属性是信息属性 (可能由 Excel 优化)。开发者仍然需要为该列提供宽度。这意味着你必须实际计算列宽度,根据单元格的内容。打开 XML SDK 并不会自动做宽度匹配。
     
    根据官方的文档中BestFit属性给出最后的解决方案如下:
     
      首先得到数据源中每一列的最大值。
    列宽单位:一个列宽单位等于一个常规样式中一个字符的宽度,excel里面用像素和宽度来共同描述宽度。
                    至于常规样式。。。(工具---选项---常规中的标准字体(当然与字大小也有关))
      上面中间的就是
      顺便说一下像素和厘米的换算吧  这里还要引入一个像素精度的概念dpi,如果显示器的像素精度是96dpi,其实就是96像素每英寸。像素精度是由显示设备的分辨率来的。下面放个截图(磅是行高单位) PS:鼠标上的DPI就是这个概念
     
      在常规样式字体下,列宽度的值是根据0,1,2,...,9这是个字符的平均值或最大值(OpenXml官方文档上给的公式是最大值,网上有人说平均值,具体还要再查一下)来计算的。 每个单元格有 4 个像素的边距填充 (每侧两个),再加上 1 像素填充的网格线。
      列宽度 = Truncate([{字符数} * {最大数字宽度像素} + {4+1个像素}]/{最大数字宽度}*256)/256
    [示例: 使用宋体字体为例,11号字体大小的最大单个字符宽度是7个像素在96 dpi时。事实上,每个数字在设置为此字体时是相同的宽度。因此,如果值的宽度是 8 个字符宽,列宽的值也就是 Truncate([8*7+5]/7*256)/256 = 8.7109375个字符宽度]
     
    要转化为列宽度的值在运行时 (以像素为单位表示) 的文件中的宽度值,请使用此公式:
    = Truncate (((256 * {列宽度} + Truncate(128 / {最大数字宽度})) / 256) * {最大数字宽度})
    [示例: 如上使用同一示例,计算将是 Truncate(((256*8.7109375+Truncate(128/7))/256)*7) = 61 像素。]
     
    若要将像素转换为字符数,请使用此公式:
    = Truncate(({像素数}-5) / {最大数字宽度像素} * 100 + 0.5) / 100
     
    最后给出一个Demo
      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using DocumentFormat.OpenXml;
      5 using DocumentFormat.OpenXml.Packaging;
      6 using DocumentFormat.OpenXml.Spreadsheet;
      7 
      8 namespace OpenXmlGenerateExcelTest
      9 {
     10     class Program
     11     {
     12         static void Main()
     13         {
     14             CreateSpreadSheet();
     15         }
     16 
     17         private static void CreateSpreadSheet()
     18         {
     19             string fileName = "X:\01.xlsx";
     20             string sheetName = "测试表格sheet—1";
     21             using (SpreadsheetDocument spreadSheet = SpreadsheetDocument.Create(fileName, SpreadsheetDocumentType.Workbook))
     22             {
     23                 spreadSheet.AddWorkbookPart();
     24                 spreadSheet.WorkbookPart.Workbook = new Workbook();
     25 
     26                 WorksheetPart worksheetPart1 = spreadSheet.WorkbookPart.AddNewPart<WorksheetPart>();
     27                 worksheetPart1.Worksheet = new Worksheet();
     28                 SheetData sheetData = new SheetData();
     29                 ProductData(sheetData);
     30                 worksheetPart1.Worksheet.AppendChild(AutoFit(sheetData));
     31                 worksheetPart1.Worksheet.AppendChild(sheetData);
     32 
     33                 spreadSheet.WorkbookPart.WorksheetParts.ElementAt(0).Worksheet.Save();
     34 
     35                 spreadSheet.WorkbookPart.Workbook.AppendChild(new Sheets());
     36                 spreadSheet.WorkbookPart.Workbook.GetFirstChild<Sheets>().AppendChild(new Sheet()
     37                 {
     38                     Id = spreadSheet.WorkbookPart.GetIdOfPart(spreadSheet.WorkbookPart.WorksheetParts.First()),
     39                     SheetId = 1,
     40                     Name = sheetName
     41                 });
     42 
     43                 spreadSheet.WorkbookPart.Workbook.Save();
     44             }
     45         }
     46 
     47         private static void ProductData(SheetData sheetData)
     48         {
     49             for (uint rowIndex = 1; rowIndex < 5; rowIndex++)
     50             {
     51                 Row row = new Row() { RowIndex = rowIndex };
     52                 for (char cellIndex = 'A'; cellIndex < 'F'; cellIndex++)
     53                 {
     54                     Cell cell = new Cell();
     55                     string innerText = "12234433433";
     56                     cell.CellValue = new CellValue(innerText);
     57                     cell.DataType = new EnumValue<CellValues>(CellValues.String);
     58                     row.Append(cell);
     59                 }
     60                 for (char cellIndex = 'F'; cellIndex < 'K'; cellIndex++)
     61                 {
     62                     Cell cell = new Cell();
     63                     string innerText = "12234";
     64                     cell.CellValue = new CellValue(innerText);
     65                     cell.DataType = new EnumValue<CellValues>(CellValues.String);
     66                     row.Append(cell);
     67                 }
     68                 sheetData.Append(row);
     69             }
     70         }
     71 
     72         private static Columns AutoFit(SheetData sheetData)
     73         {
     74             var maxColWidth = GetMaxCharacterWidth(sheetData);
     75 
     76             Columns columns = new Columns();
     77 
     78             double maxWidth = 7;
     79             foreach (var item in maxColWidth)
     80             {
     81                 /*三种单位宽度公式*/
     82                 double width = Math.Truncate((item.Value * maxWidth + 5) / maxWidth * 256) / 256;
     83                 double pixels = Math.Truncate(((256 * width + Math.Truncate(128 / maxWidth)) / 256) * maxWidth);
     84                 double charWidth = Math.Truncate((pixels - 5) / maxWidth * 100 + 0.5) / 100;
     85 
     86                 Column col = new Column() { BestFit = true, Min = (UInt32)(item.Key + 1), Max = (UInt32)(item.Key + 1), CustomWidth = true, Width = (DoubleValue)width };
     87                 columns.Append(col);
     88             }
     89             return columns;
     90         }
     91 
     92         private static Dictionary<int, int> GetMaxCharacterWidth(SheetData sheetData)
     93         {
     94             Dictionary<int, int> maxColWidth = new Dictionary<int, int>();
     95             var rows = sheetData.Elements<Row>();
     96             foreach (var r in rows)
     97             {
     98                 var cells = r.Elements<Cell>().ToArray();
     99                 for (int i = 0; i < cells.Length; i++)
    100                 {
    101                     var cell = cells[i];
    102                     var cellValue = cell.CellValue == null ? string.Empty : cell.CellValue.InnerText;
    103                     var cellTextLength = cellValue.Length;
    104                     if (maxColWidth.ContainsKey(i))
    105                     {
    106                         var current = maxColWidth[i];
    107                         if (cellTextLength > current)
    108                         {
    109                             maxColWidth[i] = cellTextLength;
    110                         }
    111                     }
    112                     else
    113                     {
    114                         maxColWidth.Add(i, cellTextLength);
    115                     }
    116                 }
    117             }
    118             return maxColWidth;
    119         }
    120     }
    121 }
    View Code
  • 相关阅读:
    Winform 切换语言 实现多语言版本
    PowerDesigner导出表到word
    【SQL】两个带order by查询进行union all报ORA-00933错误的解决方法
    读写txt文件
    c# 进度条的使用(例子)、线程
    设计模式——策略模式
    设计模式——简单工厂模式
    解决JSP路径问题的方法(jsp文件开头path, basePath作用)
    反射
    Struts2中的valuestack
  • 原文地址:https://www.cnblogs.com/sunxingege/p/4905262.html
Copyright © 2011-2022 走看看