需求:
按数据层级生成WORD文件,要有目录,目录里要有真实的页码,附件内容用表格显示,大标题 做为封面当独显示一页,
PDF内容
大标题,
目录(里有对应的页码)
正文 里有 表格
重点:NPOI 在生成word文件时不会自动生成总页数,所以本人使用了一个固定行索引来计算页码的功能。 使用 12 号字体,一页是44行,按这个方法计算页码。有更好的方法可以在评论区告诉我。
表格使用方法也要注意:
1.注意 CreateTable()方法里会默认添加一行,这时如果使用 table.CreateRow() 方法创建新行这时就会有两行。第一行为只有一个默认的单元格。所有正确的方法是使用table.GetRow(0)方法 2.注意 table.CreateRow()方法也会默认添一列,由于第一行已确定了表格的列数所要直接使用GetCell(0)方法获取列对象,0表示列索引 AddNewTableCell()方法会在第一行的列数上再加列,所以正确的用法是GetCell(0)方法
代码
/// <summary>
/// 生成报告详情文件 pdf
/// </summary>
/// <param name="model"></param>
/// <param name="caseDetailsViewModels"></param>
/// <param name="TargetPath">目录路径</param>
/// <param name="NewFileName">文件名</param>
/// <returns></returns>
private static string generateWordFile(ViewModel model, List<detailsViewModel> detailsViewModels, string TargetPath, string NewFileName)
{
string outMergeFile = TargetPath + NewFileName;
int rowIndex = 0;//行索引
//TODO:使用FileStream文件流来写入数据(传入参数为:文件所在路径,对文件的操作方式,对文件内数据的操作)
//通过使用文件流,创建文件流对象,向文件流中写入内容,并保存为Word文档格式
using (var stream = new FileStream(outMergeFile, FileMode.Create, FileAccess.Write))
{
//创建document文档对象对象实例
XWPFDocument document = new XWPFDocument();
/**
*这里我通过设置公共的Word文档中SetParagraph(段落)实例创建和段落样式格式设置,大大减少了代码的冗余,
* 避免每使用一个段落而去创建一次段落实例和设置段落的基本样式
*(如下,ParagraphInstanceSetting为段落实例创建和样式设置,后面索引表示为当前是第几行段落,索引从0开始)
*/
//12号字 44行一页
//for (int i = 1; i <= 50; i++)
//{
// document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, i.ToString(), true, 12, "黑体", ParagraphAlignment.CENTER), rowIndex);
// rowIndex++;
//}
int iPageNum = 1;//页索引
int pageRowCount = 44;//每页44行
//第一页
for (int i = 1; i <= 21; i++)
{
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, String.Empty, true, 12, "黑体", ParagraphAlignment.CENTER), rowIndex);
rowIndex++;
}
//文本标题 要显示一页
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, model.TitleModel.F_ContentStr ?? string.Empty, true, 20, "黑体", ParagraphAlignment.CENTER), rowIndex);
rowIndex++;
for (int i = 1; i <= 22; i++)
{
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, String.Empty, true, 12, "黑体", ParagraphAlignment.CENTER), rowIndex);
rowIndex++;
}
//空行
//document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, string.Empty, true, 12, "宋体", ParagraphAlignment.LEFT), rowIndex);
//rowIndex++;
document.GetProperties().ExtendedProperties.GetUnderlyingProperties().Pages = iPageNum;
var fontSizeTwoTitle = 16;
var fontSizeTwoBody = 12;
var fontSize = 12;
iPageNum++;
//目录最大行索引
var catalogueRowCount = iPageNum * pageRowCount;
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, "目录", true, 16, "黑体", ParagraphAlignment.CENTER), rowIndex);
setWordPages(document, ref rowIndex, pageRowCount, ref iPageNum);
//一、检索目的...............................................4~4
var catalogueTest = "一、检索目的...............................................4~4";
//目录 要显示一页
//TODO:这里一行需要显示两个文本
foreach (var item in model.TwoTitleList)
{
if (!string.IsNullOrEmpty(item.F_ContentStr))
{
StringBuilder catalogueText = new StringBuilder(item.F_ContentStr);//string.Format(" {0}.{1}", item.F_ContentStr ?? string.Empty, item.F_Page ?? "1~1");
string pageNameTxt = !string.IsNullOrEmpty(item.F_Page) ? item.F_Page : "1~1";
int beginI = catalogueText.ToString().Length + pageNameTxt.Length;
for (int i = beginI; i < catalogueTest.Length; i++)
{
catalogueText.Append(".");
}
catalogueText.Append(pageNameTxt);
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, catalogueText.ToString(), false, fontSize, "宋体", ParagraphAlignment.LEFT), rowIndex);
setWordPages(document, ref rowIndex, pageRowCount, ref iPageNum);
}
}
for (int i = rowIndex; i < catalogueRowCount; i++)
{
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, String.Empty, true, 12, "黑体", ParagraphAlignment.CENTER), rowIndex);
setWordPages(document, ref rowIndex, pageRowCount, ref iPageNum);
}
#region 正文 从第三页开始
iPageNum = 3;
int beginPageNum = 3;
foreach (var item in model.TwoTitleList)
{
beginPageNum = document.GetProperties().ExtendedProperties.GetUnderlyingProperties().Pages;
//标题
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, string.Format("{0}", item.F_ContentStr ?? string.Empty), false, fontSizeTwoTitle, "宋体", ParagraphAlignment.LEFT, false, String.Empty, "165DFF"), rowIndex);
setWordPages(document, ref rowIndex, pageRowCount, ref iPageNum);
foreach (var itemThree in item.ThreeContentList)
{
itemThree.F_ContentStr = itemThree.F_ContentStr.Replace("\r\n", string.Empty);
//内容
if (itemThree.F_ContentStr.Contains("</p>"))
{
var contentStr = itemThree.F_ContentStr.Replace("<p>", string.Empty);
var contentList = contentStr.Split("</p>");
foreach (var itemCon in contentList)
{
var itemConTxt = itemCon.Replace("\n", string.Empty).Trim();
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, string.Format(" {0}", itemConTxt), false, fontSizeTwoBody, "宋体", ParagraphAlignment.LEFT), rowIndex);
setWordPages(document, ref rowIndex, pageRowCount, ref iPageNum);
}
}
else
{
var F_ContentStrTxt = itemThree.F_ContentStr.Replace("\n", string.Empty).Trim();
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, string.Format(" {0}", F_ContentStrTxt), false, fontSizeTwoBody, "宋体", ParagraphAlignment.LEFT), rowIndex);
setWordPages(document, ref rowIndex, pageRowCount, ref iPageNum);
}
}
int pages = document.GetProperties().ExtendedProperties.GetUnderlyingProperties().Pages;
item.F_Page = string.Format("{0}~{1}", beginPageNum, pages);
iPageNum = pages;
}
#endregion
#region 附件
if (model.FileList != null && model.FileList.Count > 0)
{
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, " (一)检索结果分析列表", false, fontSizeTwoTitle, "宋体", ParagraphAlignment.LEFT), rowIndex);
setWordPages(document, ref rowIndex, pageRowCount, ref iPageNum);
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, " ", false, fontSize, "宋体", ParagraphAlignment.LEFT), rowIndex);
setWordPages(document, ref rowIndex, pageRowCount, ref iPageNum);
int groupNum = 1;
foreach (var item in model.FileList)
{
var KeywordTxt = !string.IsNullOrEmpty(item.Keyword) ? item.Keyword : string.Empty;
KeywordTxt = KeywordTxt.Replace("\n", string.Empty).Trim();
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, string.Format(" {0}", KeywordTxt), false, fontSizeTwoBody, "宋体", ParagraphAlignment.LEFT), rowIndex);
setWordPages(document, ref rowIndex, pageRowCount, ref iPageNum);
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, " ", false, fontSize, "宋体", ParagraphAlignment.LEFT), rowIndex);
setWordPages(document, ref rowIndex, pageRowCount, ref iPageNum);
//注意CreateTable()方法里会默认添加一行,这时如果使用 table.CreateRow() 方法创建新行这时就会有两行。第一行为只有一个默认的单元格
//所有正确的方法是使用table.GetRow(0);方法
var table = document.CreateTable();
XWPFTableRow tableHeadRow = table.GetRow(0);//新增行
tableHeadRow.GetCell(0).SetText("标题");
tableHeadRow.GetCell(0).SetVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
var tableHeadRowCell_CaseCode = tableHeadRow.AddNewTableCell();
tableHeadRowCell_CaseCode.SetText("案号");
tableHeadRowCell_CaseCode.SetVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
var tableHeadRowCell_IsAdd = tableHeadRow.AddNewTableCell();
tableHeadRowCell_IsAdd.SetText("是否加入检索报告");
tableHeadRowCell_IsAdd.SetVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
var tableHeadRowCell_NoAddCause = tableHeadRow.AddNewTableCell();
tableHeadRowCell_NoAddCause.SetText("未加入检索报告原因");
tableHeadRowCell_NoAddCause.SetVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
foreach (var itemKey in item.KeywordFileList)
{
//注意 table.CreateRow()方法也会默认添一列,由于第一行已确定了表格的列数所要直接使用GetCell(0)方法获取列对象,0表示列索引
//AddNewTableCell()方法会在第一行的列数上再加列,所以正确的用法是GetCell(0)方法
XWPFTableRow tableBodyRow = table.CreateRow();//新增行
//var tableBodyRowCell_Title = tableBodyRow.AddNewTableCell();
//tableBodyRowCell_Title.SetText(itemKey.F_CaseTitle ?? String.Empty);
//tableBodyRowCell_Title.SetVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
tableBodyRow.GetCell(0).SetText(itemKey.F_CaseTitle ?? string.Empty);
//var tableBodyRowCell_CaseCode = tableBodyRow.AddNewTableCell();
//tableBodyRowCell_CaseCode.SetText(itemKey.F_CaseCode ?? String.Empty);
//tableBodyRowCell_CaseCode.SetVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
tableBodyRow.GetCell(1).SetText(itemKey.F_CaseCode ?? string.Empty);
//var tableBodyRowCell_IsAdd = tableBodyRow.AddNewTableCell();
//tableBodyRowCell_IsAdd.SetText(itemKey.F_IsAdd ? "是" : "否");
//tableBodyRowCell_IsAdd.SetVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
tableBodyRow.GetCell(2).SetText(itemKey.F_IsAdd ? "是" : "否");
tableBodyRow.GetCell(2).SetVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
//var tableBodyRowCell_NoAddCause = tableBodyRow.AddNewTableCell();
//tableBodyRowCell_NoAddCause.SetText(itemKey.F_NoAddCause ?? String.Empty);
//tableBodyRowCell_NoAddCause.SetVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
tableBodyRow.GetCell(3).SetText(itemKey.F_NoAddCause ?? string.Empty);
groupNum++;
}
}
}
#endregion
#region 生成案例
if (caseDetailsViewModels != null && caseDetailsViewModels.Count > 0)
{
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, string.Format(" (二)检索案例全文"), false, fontSize, "宋体", ParagraphAlignment.LEFT), rowIndex);
setWordPages(document, ref rowIndex, pageRowCount, ref iPageNum);
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, " ", false, fontSize, "宋体", ParagraphAlignment.LEFT), rowIndex);
setWordPages(document, ref rowIndex, pageRowCount, ref iPageNum);
foreach (var item in caseDetailsViewModels)
{
foreach (var itemCase in item.CaseItemList)
{
//标题
var F_TitleTxt = itemCase.F_Title.Replace("\r\n", string.Empty);
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, string.Format(" {0}", F_TitleTxt), false, fontSize, "宋体", ParagraphAlignment.LEFT), rowIndex);
setWordPages(document, ref rowIndex, pageRowCount, ref iPageNum);
//内容
itemCase.F_Content = itemCase.F_Content.Replace("\r\n", string.Empty);
if (itemCase.F_Content.Contains("</p>"))
{
var contentStr = itemCase.F_Content.Replace("<p>", string.Empty);
var contentList = contentStr.Split("</p>");
foreach (var itemCon in contentList)
{
var itemConTxt = itemCon.Replace("\n", string.Empty).Trim();
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, string.Format(" {0}", itemConTxt), false, fontSize, "宋体", ParagraphAlignment.LEFT), rowIndex);
setWordPages(document, ref rowIndex, pageRowCount, ref iPageNum);
//if (writer.PageNumber > iPageNum)
//{
// writePageNumber(document, writer, BF_Light);
//}
}
}
else
{
var F_ContentTxt = itemCase.F_Content.Replace("\n", string.Empty).Trim();
document.SetParagraph(NpoiWordParagraphTextStyleHelper._.ParagraphInstanceSetting(document, string.Format(" {0}", F_ContentTxt), false, fontSize, "宋体", ParagraphAlignment.LEFT), rowIndex);
setWordPages(document, ref rowIndex, pageRowCount, ref iPageNum);
}
}
}
}
#endregion
//向文档流中写入内容,生成word
document.Write(stream);
}
return outMergeFile;
}
/// <summary>
/// 设置word的Pages 总页数
/// </summary>
/// <param name="document"></param>
/// <param name="rowIndex">行索引</param>
/// <param name="pageRowCount">每页的总行数</param>
/// <param name="iPageNum">当前页索引</param>
private static void setWordPages(XWPFDocument document, ref int rowIndex, int pageRowCount, ref int iPageNum)
{
rowIndex++;
if (document != null)
{
if ((rowIndex % pageRowCount) == 0)
{
iPageNum++;
document.GetProperties().ExtendedProperties.GetUnderlyingProperties().Pages = iPageNum;
}
}
}
调用
注意 要调用两次,第一次生成时不知道目录里的内容在第几页,第一次在生成时收集页码。这时会生成一个临时文件。第二次生成的目录里的页码才是真实有效的数据。所以要生成两次。
就好比你在写WORD时也是不知道目录里的内容会写在第几次,都是一、二级标题和内容都写完后,再去生成目录的。这里的道理也是一样的。
//word
var fileNameTempPath = generateWordFile(model, caseDetailsViewModels, uploadPath, fileNameTemp);
savePath = generateWordFile(model, caseDetailsViewModels, uploadPath, fileName);
//删除临时文件
if (File.Exists(fileNameTempPath))
{
File.Delete(fileNameTempPath);
}
END