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

多列docx文件的条件新中断,C#,c#,multiple-columns,docx,C#,Multiple Columns,Docx,这是对的后续问题。 我有一个.docx文件,它有一个正文,它的部分属性有两列。我有一本外国单词词典及其翻译。每行我都需要[Word]=[Translation],每当一个新字母开始时,它都应该在自己的行中,在该字母前后有2到3个换行符,如下所示: A A-word=翻译 A-word=翻译 B B-word=翻译 B-word=翻译 ... 我在for循环中构建了这个段落,因此在每次迭代中,我都会创建一个新段落,其中可能包含字母的Run(如果新段落开始),单词的Run,翻译的Ru

这是对的后续问题。
我有一个.docx文件,它有一个
正文
,它的
部分属性有两列
。我有一本外国单词词典及其翻译。每行我都需要[Word]=[Translation],每当一个新字母开始时,它都应该在自己的行中,在该字母前后有2到3个换行符,如下所示:

A



A-word=翻译
A-word=翻译



B



B-word=翻译
B-word=翻译
...

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

所以我的问题是,我能不能检查一下是否到达了页面的末尾,或者文本是否在列的顶部,这样我就不必添加一个
分隔符
?或者,我可以格式化
列本身,使其不以空行开头吗

我曾尝试将字母
Run
放在一个单独的、可选的
段落
,但我发现自己不得不输入换行符,问题仍然存在。

本着我的精神,您可以扩展模板功能。 使用生产力工具生成单个分页符对象,如:

private readonly Paragraph PageBreakPara = new Paragraph(new Run(new Break() { Type = BreakValues.Page}));
创建查找文本标记容器的帮助器方法:

公共IEnumerable FindElements(OpenXmlCompositeElement searchParent,字符串tagRegex) 其中T:openxmlement { var regex=新regex(tagRegex)

返回searchParent.substands()
.其中(e=>(!(e是OpenXmlCompositeElement)
&®ex.IsMatch(e.InnerText)))
.SelectMany(e=>
e、 祖先()
第()类
.Union(e是T?新的T[]{(T)e}:新的T[]{}))
.ToList();//可以跳过,防止重新评估
}

以及另一个从文档复制范围并删除范围的文件:

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;
}
公共IEnumerable DuplicateRange(OpenXmlCompositeElement根,字符串tagRegex) 其中T:openxmlement { //tagRegex必须准确描述两个标记,例如[pageStart]和[pageEnd] //或者[page][/page]-或者您选择的任何模式 var tagElements=FindElements(根,tagRegex); var fromEl=tagElements.First(); var toEl=tagElements.Skip(1).First();//如果小于2,则引发异常 //你可能想在这里找到一个共同的父母 //我假设您已经准备好了模板,因此元素是同级的。 var result=新列表(); var step=fromEl.NextSibling(); while(step!=null&&toEl!=null&&step!=toEl){ //另一个名为DeleteRange的方法将在该循环中删除该范围内的元素 var copy=step.CloneNode(); toEl.InsertAfterSelf(副本); 结果。添加(副本); step=step.NextSibling(); } 返回结果; } public IEnumerable ReplaceTag(OpenXmlCompositeElement父级、字符串tagRegex、字符串替换){ var replaceElements=findelelements(父项,tagRegex); var regex=新regex(tagRegex); foreach(替换元素中的变量el){ el.InnerText=regex.Replace(el.InnerText,replacement); } 返回替换元素; }
现在,您可以拥有一个如下所示的文档:

[第页] [标题栏]

[WordTemplate][Word]:[Translation][/WordTemplate]

[分页符] [/页]

使用该文档,您可以复制[page]..[/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]);
       }
    }
  }
}
foreach(词汇表中的var字母.Keys.OrderByDescending(c=>c)){
//相反的顺序,因为复制范围在模板范围之后
var pageTemplate=DuplicateRange(wordDocument,“\\\[/?page\\]”);
foreach(pageTemplate.OfType()中的var p){
替换标签(p,“[标题栏]”,“+字母);
var pageBr=ReplaceTag(p,“[pageBreak]”,“”);
如果(pageBr.Any()){
foreach(第br页中的var pbr){
pbr.InsertAfterSelf(PageBreakPara.CloneNode());
}
}
var wordTemplateFound=findelelements(p,“\\[/?WordTemplate\\]”;
if(wordTemplateFound.Any()){
foreach(词汇表中的var单词[字母].键){
var wordTemplate=DuplicateRange(p,“\\[/?wordTemplate\\]”)
.First();//因为它是单个段落模板
替换标签(wordTemplate,\\[/?wordTemplate\\],“”);
替换标签(wordTemplate,“\\[Word]”,Word);
替换标签(单词模板,\\[Translation\\],词汇[字母][word]);
}
}
}
}
…或者类似的东西

  • 如果事情开始变得太复杂,请调查SDTelement
  • 不要使用AltChunk尽管这个答案很流行,但它需要Word来打开和处理文件,所以你不能使用一些库来制作PDF文件
  • Word文档很混乱,上面的解决方案应该可以工作(尚未测试),但模板必须精心制作,经常备份模板
  • 制作一个健壮的文档引擎并不容易(因为Word很凌乱),请尽可能少地使用您需要的模板,并依赖您控制的模板(用户不可编辑)
  • 上面的代码远远没有经过优化或简化,我已经尝试以牺牲外观为代价将其压缩到尽可能小的占用空间中。可能有虫子
    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]);
           }
        }
      }
    }