博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
OpenXml操作Word的一些操作总结.无word组件生成word.
阅读量:7035 次
发布时间:2019-06-28

本文共 24041 字,大约阅读时间需要 80 分钟。

OpenXml相对于用MS提供的COM组件来生成WORD,有如下优势:

1.相对于MS 的COM组件,因为版本带来的不兼容问题,及各种会生成WORD半途会崩溃的问题.

2.对比填满一张30多页的WORD来说(包含图,表等),用COM组件来生成会占用20秒,Openxml1秒.

3.MS Word软件太贵了,你的客户装的是开源WORD,如LibreOffice,OpenOffice.这样你就只能用Openxml生成的WORD文档,各种支持MS Word都能打开,避免客户机器上安装MS Word.

简单来说OpenXml的各个操作.

首先用OpenXml打开一张报表.

public void CreateOpenXMLFile(string filePath)        {            using (WordprocessingDocument objWordDocument = WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document))            {                MainDocumentPart objMainDocumentPart = objWordDocument.AddMainDocumentPart();                objMainDocumentPart.Document = new Document(new Body());                Body objBody = objMainDocumentPart.Document.Body;                //创建一些需要用到的样式,如标题3,标题4,在OpenXml里面,这些样式都要自己来创建的                 //ReportExport.CreateParagraphStyle(objWordDocument);                SectionProperties sectionProperties = new SectionProperties();                PageSize pageSize = new PageSize();                PageMargin pageMargin = new PageMargin();                Columns columns = new Columns() { Space = "220" };//720                DocGrid docGrid = new DocGrid() { LinePitch = 100 };//360                //创建页面的大小,页距,页面方向一些基本的设置,如A4,B4,Letter,                 //GetPageSetting(PageSize,PageMargin);                //在这里填充各个Paragraph,与Table,页面上第一级元素就是段落,表格.                objBody.Append(new Paragraph());                objBody.Append(new Table());                objBody.Append(new Paragraph());                //我会告诉你这里的顺序很重要吗?下面才是把上面那些设置放到Word里去.(大家可以试试把这下面的代码放上面,会不会出现打开openxml文件有误,因为内容有误)                sectionProperties.Append(pageSize, pageMargin, columns, docGrid);                objBody.Append(sectionProperties);                                //如果有页眉,在这里添加页眉.                if (IsAddHead)                {                    //添加页面,如果有图片,这个图片和上面添加在objBody方式有点不一样,这里搞了好久.                    //ReportExport.AddHeader(objMainDocumentPart, image);                }                objMainDocumentPart.Document.Save();            }        }

发现上面有点注解说错,那个顺序不影响Word,但是影响如LibreOffice软件打开后看到的格式不正确.改里面太麻烦,直接在这说了.

从这个总纲里,把上面的各个步骤再来仔细说下.

首先是在Openxml创建标题3,标题4.

// 为文档创建段落样式        public static void CreateParagraphStyle(WordprocessingDocument doc)        {            // 进入文档控制样式部分            StyleDefinitionsPart styleDefinitionsPart;            styleDefinitionsPart = doc.MainDocumentPart.AddNewPart
(); Styles root = new Styles(); root.Save(styleDefinitionsPart); Styles styles = styleDefinitionsPart.Styles; if (styles == null) { styleDefinitionsPart.Styles = new Styles(); styleDefinitionsPart.Styles.Save(); } Style style3 = CreateTitleStyle(3); Style style4 = CreateTitleStyle(4); // 把样式添加入文档中 styles.Append(style3); styles.Append(style4); } private static Style CreateTitleStyle(int titleIndex) { string titleID = titleIndex.ToString(); string rsid = string.Empty; string before = string.Empty; string after = string.Empty; string line = string.Empty; string val = string.Empty; int outline = titleIndex - 1; if (titleIndex == 3) { rsid = "00BA1E98"; before = "130";//"260" after = "0"; line = "286";//"416" val = "32"; } else if (titleIndex == 4) { rsid = "00BA1E98"; before = "88"; after = "0"; line = "288";//"376" val = "28"; } Style style2 = new Style() { Type = StyleValues.Paragraph, StyleId = titleID }; StyleName styleName2 = new StyleName() { Val = "heading " + titleID }; BasedOn basedOn1 = new BasedOn() { Val = "a" }; NextParagraphStyle nextParagraphStyle1 = new NextParagraphStyle() { Val = "a" }; LinkedStyle linkedStyle1 = new LinkedStyle() { Val = titleID + "Char" }; UIPriority uIPriority1 = new UIPriority() { Val = 9 }; PrimaryStyle primaryStyle2 = new PrimaryStyle(); Rsid rsid2 = new Rsid() { Val = rsid }; style2.Append(styleName2); style2.Append(basedOn1); style2.Append(nextParagraphStyle1); style2.Append(linkedStyle1); style2.Append(uIPriority1); style2.Append(primaryStyle2); style2.Append(rsid2); StyleParagraphProperties styleParagraphProperties2 = new StyleParagraphProperties(); KeepNext keepNext1 = new KeepNext(); KeepLines keepLines1 = new KeepLines(); SpacingBetweenLines spacingBetweenLines1 = new SpacingBetweenLines() { Before = before, After = after, Line = line, LineRule = LineSpacingRuleValues.Auto }; OutlineLevel outlineLevel1 = new OutlineLevel() { Val = outline }; styleParagraphProperties2.Append(keepNext1); styleParagraphProperties2.Append(keepLines1); styleParagraphProperties2.Append(spacingBetweenLines1); styleParagraphProperties2.Append(outlineLevel1); style2.Append(styleParagraphProperties2); StyleRunProperties styleRunProperties1 = new StyleRunProperties(); Bold bold1 = new Bold(); BoldComplexScript boldComplexScript1 = new BoldComplexScript(); // Kern kern2 = new Kern() { Val = (UInt32)44U }; FontSize fontSize2 = new FontSize() { Val = val }; FontSizeComplexScript fontSizeComplexScript2 = new FontSizeComplexScript() { Val = val }; styleRunProperties1.Append(bold1); styleRunProperties1.Append(boldComplexScript1); //styleRunProperties1.Append(kern2); styleRunProperties1.Append(fontSize2); styleRunProperties1.Append(fontSizeComplexScript2); style2.Append(styleRunProperties1); return style2; }

然后是对页面大小,页面方向,页距设置.(懒的和报表里的一个个对应,里面的页距有很多大家可以精确设置.)

public static void GetPageSetting(ref PageSize pageSize, ref PageMargin pageMargin)        {            bool val = IsPaperOrientation;            string str_paperSize = "Letter";//A4,B4            UInt32Value width = 15840U;            UInt32Value height = 12240U;            int top = 1440;            UInt32Value left = 1440U;            if (str_paperSize == "A4")            {                width = 16840U;                height = 11905U;            }            else if (str_paperSize == "B4")            {                width = 20636U;                height = 14570U;            }            if (!val)            {                UInt32Value sweep = width;                width = height;                height = sweep;                int top_sweep = top;                top = (int)left.Value;                left = (uint)top_sweep;            }            pageSize.Width = width;            pageSize.Height = height;            pageSize.Orient = new EnumValue
(val ? PageOrientationValues.Landscape : PageOrientationValues.Portrait); pageMargin.Top = top; pageMargin.Bottom = top; pageMargin.Left = left; pageMargin.Right = left; pageMargin.Header = (UInt32Value)720U; pageMargin.Footer = (UInt32Value)720U; pageMargin.Gutter = (UInt32Value)0U; }

然后重点来了,大家对各元素如何在OpenXml添加的.

我先说下,在Openxml里相关元素的关系,在Word里,按一下Enter,转一行,对应的一行元素就是Paragraph,那如果一行文字在里面如何存放.Paragraph->Run->Text,图表Paragraph->Run->Drawing,表格Table->TableRow->TableCell->Paragraph->Run->Text与Drawing.在关系上说,还是很简洁的.来看一下具体的操作,我们先定义一个类.对应OpenXML里的基本样式设置.

public class ReportCommon    {        public const float defaultSize = 12f;        public const float H1 = 20f;        public const float H3 = 16f;        public const float H4 = 14f;        private string text = string.Empty;        public ReportCommon()        {            Alignment = -1;            Size = defaultSize;        }        //规定-1左对齐,0中间,1右对齐        public int Alignment { get; set; }        public virtual float Size { get; set; }        public string Text { get; set; }        public virtual bool IsBold { get; set; }    }

其中我定义这个类的三个子类,分别是ReportValue:主要是这种A: B,ReportImage:包含一个图片的路径.ReportText:只有一个文本.

private static List
GetRuns(ReportCommon common) { List
runs = new List
(); if (common is ReportValue) { ReportValue reportvalue = common as ReportValue; Run r = new Run(); RunProperties rP = GetRunProperties(reportvalue, true); r.Append(rP); string text = reportvalue.Text; if (text.EndsWith(":")) text = text + " "; if (!text.EndsWith(": ")) text = text + ": "; Text t = CreateText(text); r.Append(t); runs.Add(r); r = new Run(); rP = GetRunProperties(reportvalue, false); r.Append(rP); r.Append(CreateText(reportvalue.Value)); runs.Add(r); } else if (common is ReportImage) { ReportImage reportImage = common as ReportImage; Run r = new Run(); RunProperties rP = GetRunProperties(reportImage); Drawing image = GetImageToBody(reportImage.RId, reportImage.Width * 600, reportImage.Height * 800); //Drawing image = new Drawing(); //image.Append(new A.Blip() { Embed = new StringValue(reportImage.RId) }); r.Append(rP); r.Append(image); runs.Add(r); } else if (common is ReportText) { Run r = new Run(); RunProperties rP = GetRunProperties(common); r.Append(rP); r.Append(CreateText(common.Text)); runs.Add(r); } return runs; }

看了这里,问题是不是越来越多.图片具体操作先不说.上面的RunProperties,与CreateText分别是指什么.

RunProperties是指包含这段Text,你要设置的一些字体大小,颜色,文本对齐设置.

而CreateText是因为我们在Openxml你在后面加个空格,而会给你过滤掉,空格要对应到XML的具体设置,看如下代码.

public static RunProperties GetRunProperties(ReportCommon common, bool bBold = false)        {            RunProperties rPr = new RunProperties();            //Color color = new Color() { Val = "FF0000" }; // the color is red            RunFonts rFont = new RunFonts();            rFont.Ascii = "Arial"; // the font is Arial            //rPr.Append(color);            //rPr.Append(rFont);            if (common.IsBold || bBold)                rPr.Append(new Bold()); // it is Bold            //TextAlignment             rPr.Append(new FontSize() { Val = new StringValue((common.Size * 2).ToString()) }); //font size (in 1/72 of an inch)            return rPr;        }  private static Text CreateText(string text)        {            if (text == null)                text = string.Empty;            Text t = new Text(text);            if (text.EndsWith(" "))            {                t.Space = new EnumValue
(SpaceProcessingModeValues.Preserve); } if (text.StartsWith(" ")) { t.Space = new EnumValue
(SpaceProcessingModeValues.Default); } return t; }

不知这么多代码大家看烦没,因为我这人喜欢不是业务与大纲的事,都喜欢直接看代码来说,比人讲的清楚.所以讲的时候也喜欢,直接上代码.废话不说了,说下图片的问题,Openxml插入图片比较麻烦,先贴一段代码.

private static Drawing GetImageToBody(string relationshipId, int x = 914400, int y = 360000)        {            // Define the reference of the image.            var element =                 new Drawing(                     new DW.Inline(                         new DW.Extent() { Cx = x, Cy = y },                         new DW.EffectExtent()                         {                             LeftEdge = 0L,                             TopEdge = 0L,                             RightEdge = 0L,                             BottomEdge = 0L                         },                         new DW.DocProperties()                         {                             Id = (UInt32Value)1U,                             Name = "Picture 1"                         },                         new DW.NonVisualGraphicFrameDrawingProperties(                             new A.GraphicFrameLocks() { NoChangeAspect = true }),                         new A.Graphic(                             new A.GraphicData(                                 new PIC.Picture(                                     new PIC.NonVisualPictureProperties(                                         new PIC.NonVisualDrawingProperties()                                         {                                             Id = (UInt32Value)0U,                                             Name = "New Bitmap Image.jpg"                                         },                                         new PIC.NonVisualPictureDrawingProperties()),                                     new PIC.BlipFill(                                         new A.Blip(                                             new A.BlipExtensionList(                                                 new A.BlipExtension()                                                 {                                                     Uri =                                                       "{28A0092B-C50C-407E-A947-70E740481C1C}"                                                 })                                         )                                         {                                             Embed = relationshipId,                                             CompressionState =                                             A.BlipCompressionValues.Print                                         },                                         new A.Stretch(                                             new A.FillRectangle())),                                     new PIC.ShapeProperties(                                         new A.Transform2D(                                             new A.Offset() { X = 0L, Y = 0L },                                             new A.Extents() { Cx = x, Cy = y }),                                         new A.PresetGeometry(                                             new A.AdjustValueList()                                         ) { Preset = A.ShapeTypeValues.Rectangle }))                             ) { Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" })                     )                     {                         DistanceFromTop = (UInt32Value)0U,                         DistanceFromBottom = (UInt32Value)0U,                         DistanceFromLeft = (UInt32Value)0U,                         DistanceFromRight = (UInt32Value)0U,                         EditId = "50D07946"                     });            // Append the reference to body, the element should be in a Run.            var blip = element.Descendants
() .FirstOrDefault
(); return element; }

这段代码里,东东比较多,大家主要看这一节, Embed = relationshipId,也是参数里要求传入的,我们可以这么理解,在OpenXML插入一张电脑的图片,插入数据到word后,word然后把保存这个图片的一个标识量给我们,让我们来用,就是relationshipId.说到这,我们好像还没看如何把一张路径下的图片插入word.如下

public static void CreateImageRid(ReportImage reportImage, MainDocumentPart objMainDocumentPart)        {            ImagePartType imagetype = ImagePartType.Jpeg;            FileInfo newImg = new FileInfo(reportImage.Value);            ImagePart newImgPart = objMainDocumentPart.AddImagePart(imagetype);            //插入图片数据到Word里去.            using (FileStream stream = newImg.OpenRead())            {                newImgPart.FeedData(stream);            }            //Word返回给我们插入数据的标识符.            reportImage.RId = objMainDocumentPart.GetIdOfPart(newImgPart);        }

这里图片插入有先后关系,要先调用上面这段,插入数据到word,然后才能调用上上段的那段代码来生成Drawing元素.

大家如果要把图片的宽度,设为当前Word的可用宽度.Int32Value width = (int)(pageSize.Width - pageMargin.Right - pageMargin.Left);

好吧,大家会发现上上段那里的长度特大,这里发现这里的值要很大才能显现比较好看的图片,一般我在原来的基础宽度*600,基础长度*800.原因吗,我也不清楚,有些猜测,没有验证就不说了,这个要求OpenXML生成报表比较急,我把这几天所有操作先总结一下.后面再来修改,如果有知道的道友,不妨说一下.

在这里,文字与图片如何生成Paragraph就很简单了.

public static Paragraph GetParagraph(ReportCommon common)        {            Paragraph p = new Paragraph();            ParagraphProperties pPr = GetParagraphProperties(common);            p.Append(pPr);            List
run = GetRuns(common); foreach (var r in run) { p.Append(r); } return p; }

好吧,最后说到如何在OpenXML生成一张表.生成图表我用的基础数据是List<List<string>>,上面的ReportTable数据就放在这个里面.当然还有一些基本的定义属性就不说.具体如下看代码.

public static Table GetParagraph(ReportTable reportTable, Int32Value width)        {            Table table = new Table();            TableProperties tblProp = new TableProperties(                new TableBorders(                    new TopBorder() { Val = new EnumValue
(BorderValues.Single), Size = 4 }, new BottomBorder() { Val = new EnumValue
(BorderValues.Single), Size = 4 }, new LeftBorder() { Val = new EnumValue
(BorderValues.Single), Size = 4 }, new RightBorder() { Val = new EnumValue
(BorderValues.Single), Size = 4 }, new InsideHorizontalBorder() { Val = new EnumValue
(BorderValues.Single), Size = 4 }, new InsideVerticalBorder() { Val = new EnumValue
(BorderValues.Single), Size = 4 } ) ); tblProp.TableWidth = new TableWidth() { Width = width.ToString(), Type = TableWidthUnitValues.Dxa }; table.Append(tblProp); int count = reportTable.Value.Count; int cols = reportTable.Column; int j = 0; foreach (List
strs in reportTable.Value) { TableRow row = new TableRow(); for (int i = 0; i < cols; i++) { TableCell cell = new TableCell(); TableCellProperties tableCellProperties = new TableCellProperties(); TableCellMargin margin = new TableCellMargin(); margin.LeftMargin = new LeftMargin() { Width = "100", Type = TableWidthUnitValues.Dxa }; margin.RightMargin = new RightMargin() { Width = "100", Type = TableWidthUnitValues.Dxa }; tableCellProperties.Append(margin); Paragraph par = new Paragraph(); Run run = new Run(); if (j == 0 && reportTable.IsHaveColumn) { RunProperties rPr = new RunProperties(); rPr.Append(new Bold()); run.Append(rPr); } if (strs.Count != cols && i >= strs.Count - 1) { HorizontalMerge verticalMerge = new HorizontalMerge(); if (i == strs.Count - 1) { RunProperties rPr = new RunProperties(); rPr.Append(new Bold()); run.Append(rPr); verticalMerge.Val = MergedCellValues.Restart; run.Append(CreateText(strs[i])); } else { verticalMerge.Val = MergedCellValues.Continue; } tableCellProperties.Append(verticalMerge); } else { run.Append(CreateText(strs[i])); } par.Append(run); cell.Append(tableCellProperties); cell.Append(par); row.Append(cell); } j++; table.Append(row); } return table; }

代码可以简单的多,只是因为有一些要求,比如你表每行是5个数据,但是有一行,数据只有四个,有二个表格合并了,这里会默认把最后二格合并,具体意思大家可以改改代码看.

好吧,到这里,就差不多,元素添加完后,然后是把相关页面大小的设置加到objBody,最后是添加页眉. 

public static void AddHeader(MainDocumentPart mainDocPart, ReportImage reportImge)        {            // Delete the existing header parts.            mainDocPart.DeleteParts(mainDocPart.HeaderParts);            // Create a new header part and get its relationship id.            HeaderPart newHeaderPart = mainDocPart.AddNewPart
(); string rId = mainDocPart.GetIdOfPart(newHeaderPart); ImagePart imagepart = newHeaderPart.AddImagePart(ImagePartType.Jpeg); FileInfo newImg = new FileInfo(reportImge.Value); using (FileStream stream = newImg.OpenRead()) { imagepart.FeedData(stream); } string imageRID = newHeaderPart.GetIdOfPart(imagepart); reportImge.RId = imageRID; Header header = GeneratePageHeaderPart(reportImge); header.Save(newHeaderPart); foreach (SectionProperties sectProperties in mainDocPart.Document.Descendants
()) { // Delete any existing references to headers. foreach (HeaderReference headerReference in sectProperties.Descendants
()) sectProperties.RemoveChild(headerReference); HeaderReference newHeaderReference = new HeaderReference() { Id = rId, Type = HeaderFooterValues.Default }; sectProperties.Append(newHeaderReference); } header.Save(); } // Creates an header instance and adds its children. private static Header GeneratePageHeaderPart(ReportImage reportImge) { var runs = GetRuns(reportImge); Paragraph paragraph = new Paragraph(); paragraph.Append(GetParagraphProperties(reportImge)); foreach (var run in runs) { paragraph.Append(run); } paragraph.Append(new Run(new Text() { Text = "" })); Header header = new Header(); header.Append(paragraph); return header; }

上面的代码主要注意,Image所指的路径存放的和在前面的Document里不一样,这里存放在Header里,从Word文件解压来说,二都是不同的XML文档,你把图片数据写在Document里,得到的标识符在Header是空的.

好了.OpenXML的主要操作都在这了,但是大家如果想生成这种样式,应该如何处理?

 上面每一个元素如Payload Mass (LBS): 0.22046,就是前面的ReportValue,如何以这种对齐方式来插入了?引入一个新的结构,ReportValueList,主要就是ReportValue的键表信息.

public static List
GetParagraph(ReportValueList valueList, Int32Value width, int column = 2) { if (column < 1) column = 1; List
list = new List
(); int currentcolumn = 0; Paragraph currentParagraph = null; foreach (var reportvalue in valueList.Values) { reportvalue.Size = valueList.Size; if (currentcolumn == 0) { currentParagraph = new Paragraph(); ParagraphProperties pPr = new ParagraphProperties(); //添加标签类 Tabs tabs = new Tabs(); Int32Value eachWidth = width / (new Int32Value(column)); for (int i = 1; i < column; i++) { TabStop stop = new TabStop(); stop.Val = new EnumValue
(TabStopValues.Left); stop.Position = eachWidth * i; tabs.Append(stop); } pPr.Append(tabs); currentParagraph.Append(pPr); list.Add(currentParagraph); } List
runs = GetRuns(reportvalue); foreach (var run in runs) { currentParagraph.Append(run); } currentcolumn++; if (currentcolumn < column) { Run run = new Run(); run.Append(new TabChar()); currentParagraph.Append(run); } if (currentcolumn >= column) { currentcolumn = 0; } } return list; }

主要是对元素TabStop的运用,仔细的大家可以去查查文档

写到这里,忘记写要用到的命名空间.

using A = DocumentFormat.OpenXml.Drawing;using PIC = DocumentFormat.OpenXml.Drawing.Pictures;using DW = DocumentFormat.OpenXml.Drawing.Wordprocessing;using DocumentFormat.OpenXml;using DocumentFormat.OpenXml.Packaging;using DocumentFormat.OpenXml.Wordprocessing;

这个就到这里了.下面如果有时间,我会讲一下,如果对整张报表来进行模版化,意思是在word里的每一个表,一个图片,一个文字,在软件上用生成的结果来替换相应的元素.

转载地址:http://ggjal.baihongyu.com/

你可能感兴趣的文章
右侧客服 运动案例
查看>>
T4 Editor地址
查看>>
小程序文档
查看>>
QQ分享-定制分享卡片
查看>>
DataTable的用法
查看>>
17_服务器提权
查看>>
Python文件指针与Python函数
查看>>
免费16WiFi被吐槽
查看>>
移动UI自动化-Page Objects Pattern
查看>>
-------分割线------\n FriskyPuppy的图论学习之路!
查看>>
JZ2440 裸机驱动 第13章 LCD控制器(2)
查看>>
连邦IT服务IT用户、厂商和服务商
查看>>
浪潮信息10亿投向云计算
查看>>
[C] getopt使用说明
查看>>
【转载】在模态对话框中实现用户和AutoCAD 的交互操作
查看>>
关于华三实验室在win10上不能启动设备的解决办法
查看>>
SVN相关命令
查看>>
Android 如何更改一个 imageview的颜色
查看>>
如何将Html内容渲染成Image
查看>>
(清理缓存)
查看>>