多列 docx 文件 C# 的条件新中断

本文关键字:中断 新中断 条件 docx 文件 多列 | 更新日期: 2023-09-27 18:29:05

这是使用 C# 从 ObservableCollection 创建 Word 文件的后续问题。
我有一个.docx文件,其中包含一个Body,其中有 2 列用于其SectionProperties.我有一本外语词典及其翻译。在每一行上,我需要 [Word] = [翻译],每当一个新字母开始时,它都应该在自己的行中,在该字母前后有 2 或 3 个换行符,如下所示:

一个



A字=翻译
A字=翻译





B字=翻译
B字=翻译
...

我在 for 循环中构建了它,以便在每次迭代中我都会创建一个新段落,其中包含字母的可能Run(如果开始一个新段落(、单词的Run和翻译的Run。因此,带有第一个字母的Run与单词和翻译Run处于同一Paragraph,并且在Text前后附加了 2 或 3 个Break对象。
这样做时,第二列有时可以从 1 或 2 个空行开始。或者下一页上的第一列可以以空行开头。
这就是我想避免的。

所以我的问题是,我可以以某种方式检查是否到达页面末尾,或者文本是否位于列的顶部,这样我就不必添加Break?或者,我可以格式化Column本身,使其不以空行开头吗?

我尝试将字母Run放在单独的可选Paragraph中,但同样,我发现自己必须输入换行符,问题仍然存在。

多列 docx 文件 C# 的条件新中断

本着我另一个答案的精神,您可以扩展模板功能。使用快捷会议工具生成单个分页符对象,如下所示:

private readonly Paragraph PageBreakPara = new Paragraph(new Run(new Break() { Type = BreakValues.Page}));

创建一个帮助程序方法来查找文本标记的容器:

public IEnumerable FindElements(OpenXmlCompositeElement searchParent, string tagRegex( 其中 T:OpenXmlElement{ var regex = new Regex(tagRegex(;

return searchParent.Descendants() 
    .Where(e=>(!(e is OpenXmlCompositeElement) 
              && regex.IsMatch(e.InnerText)))
    .SelectMany(e => 
        e.Ancestors()
            .OfType<T>()
            .Union(e is T ? new T[] { (T)e } : new T[] {} ))
    .ToList(); // can skip, prevents reevaluations 

}

另一个从文档中复制范围并删除范围:

public IEnumerable<T> DuplicateRange<T>(OpenXmlCompositeElement root, string tagRegex)
  where T: OpenXmlElement
{ 
// tagRegex must describe exactly two tags, such as [pageStart] and [pageEnd]
// or [page] [/page] - or whatever pattern you choose
  var tagElements = FindElements(root, tagRegex);
  var fromEl = tagElements.First();
  var toEl = tagElements.Skip(1).First(); // throws exception if less than 2 el
// you may want to find a common parent here
// I'll assume you've prepared the template so the elements are siblings.
  var result = new List<OpenXmlElement>();
  var step = fromEl.NextSibling();
  while (step !=null && toEl!=null && step!=toEl){
   // another method called DeleteRange will instead delete elements in that range within this loop
    var copy = step.CloneNode();
    toEl.InsertAfterSelf(copy);
    result.Add(copy);
    step = step.NextSibling();
  }
  return result;
}

public IEnumerable<OpenXmlElement> ReplaceTag(OpenXmlCompositeElement parent, string tagRegex, string replacement){
  var replaceElements = FindElements<OpenXmlElement>(parent, tagRegex);
  var regex = new Regex(tagRegex);
  foreach(var el in  replaceElements){
     el.InnerText = regex.Replace(el.InnerText, replacement);
  }
  return replaceElements;
}

现在,您可以拥有如下所示的文档:

[页][标题信]

[单词

模板][单词]: [翻译] [/单词模板]

[分页][/页]

使用该文档,您可以复制[页面]。[/page] 范围,按字母处理,一旦没有字母 - 删除模板范围:

var 词汇 = 字典>;

foreach (var letter in vocabulary.Keys.OrderByDescending(c=>c)){
  // in reverse order because the copy range comes after the template range
  var pageTemplate = DuplicateRange(wordDocument,"''[/?page'']");
  foreach (var p in pageTemplate.OfType<OpenXmlCompositeElement>()){
    ReplaceTag(p, "[TitleLetter]",""+letter);
    var pageBr = ReplaceTag(p, "[pageBreak]","");
    if (pageBr.Any()){
      foreach(var pbr in pageBr){
       pbr.InsertAfterSelf(PageBreakPara.CloneNode()); 
      }
    }
    var wordTemplateFound = FindElements(p, "''[/?WordTemplate'']");
    if (wordTemplateFound .Any()){
       foreach (var word in vocabulary[letter].Keys){
          var wordTemplate = DuplicateRange(p, "''[/?WordTemplate'']")
              .First(); // since it's a single paragraph template
          ReplaceTag(wordTemplate, "''[/?WordTemplate'']","");
          ReplaceTag(wordTemplate, "''[Word]",word);
          ReplaceTag(wordTemplate, "''[Translation'']",vocabulary[letter][word]);
       }
    }
  }
}

。或类似的东西。

  • 如果事情开始变得过于复杂,请查看 SdtElements
  • 不要使用 AltChunk,尽管这个答案很受欢迎,但它需要 Word 来打开和处理文件,所以你不能使用一些库来制作 PDF
  • Word文档很乱,上面的解决方案应该可以工作(尚未测试(,但模板必须精心制作,经常备份模板
  • 制作一个强大的文档引擎并不容易(因为 Word 很混乱(,做你需要的最低限度,并依赖于你控制的模板(不是用户可编辑的(。
  • 上面的代码远未优化或简化,我试图以可呈现性为代价将其压缩在尽可能小的占用空间中。可能也有错误:)