多列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]);
}
}
}
}