将两个 ODT 文档合并在一起时如何保持样式格式
How to maintain style formatting when merging two ODT documents together
我正在使用 C# 的 AODL 库。到目前为止,我已经能够将第二个文档的文本批量导入到第一个文档中。问题是我不太清楚我需要抓住什么来确保样式也被移到合并的文档中。下面是我用来测试的简单代码。我能找到的最接近的答案是 Merging two .odt files from code,它在某种程度上回答了我的问题,但它仍然没有告诉我需要在哪里放置样式/从哪里获取样式。它至少让我知道我需要检查第二个文档中的样式,并确保第一个文档中没有匹配的名称,否则会发生冲突。我不确定到底该怎么做,而且文档非常少。在您提出任何建议之前,我想让您知道,是的,odt 是我需要使用的文件类型,并且像 Microsoft 对 Word 所做的那样进行任何类型的互操作并不是我所追求的。如果还有另一个图书馆与 AODL 的工作方式类似,我会洗耳恭听。
TextDocument mergeTemplateDoc = ReadContentsOfFile(mergeTemplateFileName);
TextDocument vehicleTemplateDoc = ReadContentsOfFile(vehicleTemplateFileName);
foreach (IContent piece in vehicleTemplateDoc.Content)
{
XmlNode newNode = mergeTemplateDoc.XmlDoc.ImportNode(piece.Node,true);
Paragraph p = ParagraphBuilder.CreateParagraphWithExistingNode(mergeTemplateDoc, newNode);
mergeTemplateDoc.Content.Add(p);
}
mergeTemplateDoc.SaveTo("MergComplete.odt");
我不知道应该如何精确编码,但使用 7zip 我已经能够将整个 styles.xml 从一个文件复制到另一个文件。以编程方式,它应该同样简单。
我总是用样式格式化我的文件,从不直接格式化。所以只是替换任何文件很容易消除本地样式。
我找到了这个答案(针对问题 "Cleaning a stylesheet of unused styles")https://www.mobileread.com/forums/showpost.php?s=cbbee08a1204df71ec5cd88bcf222253&p=2100914&postcount=13
它遍历一个文档中的所有样式。它没有显示如何将一个合并到另一个,但是 backbone 很清楚。
'---------------------------------------------------------- 03/02/2012
' Supprimer les styles personnalisés inutilisés
' d'un document texte ou d'un classeur
'---------------------------------------------------------------------
sub stylesPersoInutiles()
dim coStylesFamilles as object, oStyleFamille as object
dim oStyle as object, nomFamille as string
dim f as long, x as long
dim ts(), buf as string, iRet as integer
const SEP = ", "
coStylesFamilles = thisComponent.StyleFamilies
for f = 0 to coStylesFamilles.count -1
' Pour chaque famille
nomFamille = coStylesFamilles.elementNames(f)
oStyleFamille = coStylesFamilles.getByName(nomFamille)
buf = ""
for x = 0 to oStyleFamille.Count -1
' Pour chaque style
oStyle = oStyleFamille(x)
'xray oStyle
if (oStyle.isUserDefined) and (not oStyle.isInUse) then
buf = buf & oStyle.name & SEP
end if
next x
if len(buf) > len(SEP) then
buf = left(buf, len(buf) - len(SEP))
iRet = msgBox("Styles personnalisés non utilisés : " _
& chr(13) & buf & chr(13) & chr(13) _
& "Faut-il les détruire ?", 4+32+256, nomFamille)
if iRet = 6 then
ts = split(buf, SEP)
for x = 0 to uBound(ts)
oStyleFamille.removeByName(ts(x))
next x
end if
end if
next f
end sub
这是我最终解决问题的方法。请记住,自从提出这个问题以来,我已经迁移到使用 Java,因为该库似乎在该语言中工作得更好一些。
基本上下面的方法所做的是获取每个文档中生成的自动样式。它遍历第二个文档并找到每个样式节点,检查名称属性。然后用该文档唯一的额外标识符标记该名称,因此当它们合并在一起时,它们不会在名称方面发生冲突。
mergeFontTypesToPrimaryDoc 仅抓取主文档中尚不存在的字体,因为所有字体在文档中都以相同的方式引用,因此无需进行任何编辑。
updateNodeChildrenStyleNames 只是一种递归方法,我用它来确保更新所有行内样式节点以删除两个文档之间的任何冲突名称。
这个类似的想法在 C# 中应该也适用。
private static void mergeStylesToPrimaryDoc(OdfTextDocument primaryDoc, OdfTextDocument secondaryDoc) throws Exception {
OdfFileDom primaryContentDom = primaryDoc.getContentDom();
OdfOfficeAutomaticStyles primaryDocAutomaticStyles = primaryDoc.getContentDom().getAutomaticStyles();
OdfOfficeAutomaticStyles secondaryDocAutomaticStyles = secondaryDoc.getContentDom().getAutomaticStyles();
//Adopt style nodes from secondary doc
for(int i =0; i<secondaryDocAutomaticStyles.getLength();i++){
Node style = secondaryDocAutomaticStyles.item(i).cloneNode(true);
if(style.hasAttributes()){
NamedNodeMap attributes = style.getAttributes();
for(int j=0; j< attributes.getLength();j++){
Node a = attributes.item(j);
if(a.getLocalName().equals("name")){
a.setNodeValue(a.getNodeValue()+_stringToAddToStyle);
}
}
}
if(style.hasChildNodes()){
updateNodeChildrenStyleNames(style, _stringToAddToStyle, "name");
}
primaryDocAutomaticStyles.appendChild(primaryContentDom.adoptNode(style));
}
}
private static void mergeFontTypesToPrimaryDoc(OdfTextDocument primaryDoc, OdfTextDocument secondaryDoc) throws Exception {
//Insert referenced font types that are not in the primary document you are merging into
NodeList sdDomNodes = secondaryDoc.getContentDom().getChildNodes().item(0).getChildNodes();
NodeList pdDomNodes = primaryDoc.getContentDom().getChildNodes().item(0).getChildNodes();
OdfFileDom primaryContentDom = primaryDoc.getContentDom();
Node sdFontNode=null;
Node pdFontNode=null;
for(int i =0; i<sdDomNodes.getLength();i++){
if(sdDomNodes.item(i).getNodeName().equals("office:font-face-decls")){
sdFontNode = sdDomNodes.item(i);
break;
}
}
for(int i =0; i<pdDomNodes.getLength();i++){
Node n =pdDomNodes.item(i);
if(n.getNodeName().equals("office:font-face-decls")){
pdFontNode = pdDomNodes.item(i);
break;
}
}
if(sdFontNode !=null && pdFontNode != null){
NodeList sdFontNodeChildList = sdFontNode.getChildNodes();
NodeList pdFontNodeChildList = pdFontNode.getChildNodes();
List<String> fontNames = new ArrayList<String>();
//Get list of existing fonts in primary doc
for(int i=0; i<pdFontNodeChildList.getLength();i++){
NamedNodeMap attributes = pdFontNodeChildList.item(i).getAttributes();
for(int j=0; j<attributes.getLength();j++){
if(attributes.item(j).getLocalName().equals("name")){
fontNames.add(attributes.item(j).getNodeValue());
}
}
}
//Check each font in the secondary doc to make sure it gets added if the primary doesn't have it
for(int i=0; i<sdFontNodeChildList.getLength();i++){
Node fontNode = sdFontNodeChildList.item(i).cloneNode(true);
NamedNodeMap attributes = fontNode.getAttributes();
String fontName="";
for(int j=0; j< attributes.getLength();j++){
if(attributes.item(j).getLocalName().equals("name")){
fontName = attributes.item(j).getNodeValue();
break;
}
}
if(!fontName.equals("") && !fontNames.contains(fontName)){
pdFontNode.appendChild(primaryContentDom.adoptNode(fontNode));
}
}
}
}
private static void updateNodeChildrenStyleNames(Node n, String stringToAddToStyle, String nodeLocalName){
NodeList childNodes = n.getChildNodes();
for (int i=0; i< childNodes.getLength(); i++){
Node currentChild = childNodes.item(i);
if(currentChild.hasAttributes()){
NamedNodeMap attributes = currentChild.getAttributes();
for(int j =0; j < attributes.getLength(); j++){
Node a = attributes.item(j);
if(a.getLocalName().equals(nodeLocalName)){
a.setNodeValue(a.getNodeValue() + stringToAddToStyle);
}
}
}
if(currentChild.hasChildNodes()){
updateNodeChildrenStyleNames(currentChild, stringToAddToStyle, nodeLocalName);
}
}
}
}
我正在使用 C# 的 AODL 库。到目前为止,我已经能够将第二个文档的文本批量导入到第一个文档中。问题是我不太清楚我需要抓住什么来确保样式也被移到合并的文档中。下面是我用来测试的简单代码。我能找到的最接近的答案是 Merging two .odt files from code,它在某种程度上回答了我的问题,但它仍然没有告诉我需要在哪里放置样式/从哪里获取样式。它至少让我知道我需要检查第二个文档中的样式,并确保第一个文档中没有匹配的名称,否则会发生冲突。我不确定到底该怎么做,而且文档非常少。在您提出任何建议之前,我想让您知道,是的,odt 是我需要使用的文件类型,并且像 Microsoft 对 Word 所做的那样进行任何类型的互操作并不是我所追求的。如果还有另一个图书馆与 AODL 的工作方式类似,我会洗耳恭听。
TextDocument mergeTemplateDoc = ReadContentsOfFile(mergeTemplateFileName);
TextDocument vehicleTemplateDoc = ReadContentsOfFile(vehicleTemplateFileName);
foreach (IContent piece in vehicleTemplateDoc.Content)
{
XmlNode newNode = mergeTemplateDoc.XmlDoc.ImportNode(piece.Node,true);
Paragraph p = ParagraphBuilder.CreateParagraphWithExistingNode(mergeTemplateDoc, newNode);
mergeTemplateDoc.Content.Add(p);
}
mergeTemplateDoc.SaveTo("MergComplete.odt");
我不知道应该如何精确编码,但使用 7zip 我已经能够将整个 styles.xml 从一个文件复制到另一个文件。以编程方式,它应该同样简单。 我总是用样式格式化我的文件,从不直接格式化。所以只是替换任何文件很容易消除本地样式。
我找到了这个答案(针对问题 "Cleaning a stylesheet of unused styles")https://www.mobileread.com/forums/showpost.php?s=cbbee08a1204df71ec5cd88bcf222253&p=2100914&postcount=13 它遍历一个文档中的所有样式。它没有显示如何将一个合并到另一个,但是 backbone 很清楚。
'---------------------------------------------------------- 03/02/2012
' Supprimer les styles personnalisés inutilisés
' d'un document texte ou d'un classeur
'---------------------------------------------------------------------
sub stylesPersoInutiles()
dim coStylesFamilles as object, oStyleFamille as object
dim oStyle as object, nomFamille as string
dim f as long, x as long
dim ts(), buf as string, iRet as integer
const SEP = ", "
coStylesFamilles = thisComponent.StyleFamilies
for f = 0 to coStylesFamilles.count -1
' Pour chaque famille
nomFamille = coStylesFamilles.elementNames(f)
oStyleFamille = coStylesFamilles.getByName(nomFamille)
buf = ""
for x = 0 to oStyleFamille.Count -1
' Pour chaque style
oStyle = oStyleFamille(x)
'xray oStyle
if (oStyle.isUserDefined) and (not oStyle.isInUse) then
buf = buf & oStyle.name & SEP
end if
next x
if len(buf) > len(SEP) then
buf = left(buf, len(buf) - len(SEP))
iRet = msgBox("Styles personnalisés non utilisés : " _
& chr(13) & buf & chr(13) & chr(13) _
& "Faut-il les détruire ?", 4+32+256, nomFamille)
if iRet = 6 then
ts = split(buf, SEP)
for x = 0 to uBound(ts)
oStyleFamille.removeByName(ts(x))
next x
end if
end if
next f
end sub
这是我最终解决问题的方法。请记住,自从提出这个问题以来,我已经迁移到使用 Java,因为该库似乎在该语言中工作得更好一些。
基本上下面的方法所做的是获取每个文档中生成的自动样式。它遍历第二个文档并找到每个样式节点,检查名称属性。然后用该文档唯一的额外标识符标记该名称,因此当它们合并在一起时,它们不会在名称方面发生冲突。
mergeFontTypesToPrimaryDoc 仅抓取主文档中尚不存在的字体,因为所有字体在文档中都以相同的方式引用,因此无需进行任何编辑。
updateNodeChildrenStyleNames 只是一种递归方法,我用它来确保更新所有行内样式节点以删除两个文档之间的任何冲突名称。
这个类似的想法在 C# 中应该也适用。
private static void mergeStylesToPrimaryDoc(OdfTextDocument primaryDoc, OdfTextDocument secondaryDoc) throws Exception {
OdfFileDom primaryContentDom = primaryDoc.getContentDom();
OdfOfficeAutomaticStyles primaryDocAutomaticStyles = primaryDoc.getContentDom().getAutomaticStyles();
OdfOfficeAutomaticStyles secondaryDocAutomaticStyles = secondaryDoc.getContentDom().getAutomaticStyles();
//Adopt style nodes from secondary doc
for(int i =0; i<secondaryDocAutomaticStyles.getLength();i++){
Node style = secondaryDocAutomaticStyles.item(i).cloneNode(true);
if(style.hasAttributes()){
NamedNodeMap attributes = style.getAttributes();
for(int j=0; j< attributes.getLength();j++){
Node a = attributes.item(j);
if(a.getLocalName().equals("name")){
a.setNodeValue(a.getNodeValue()+_stringToAddToStyle);
}
}
}
if(style.hasChildNodes()){
updateNodeChildrenStyleNames(style, _stringToAddToStyle, "name");
}
primaryDocAutomaticStyles.appendChild(primaryContentDom.adoptNode(style));
}
}
private static void mergeFontTypesToPrimaryDoc(OdfTextDocument primaryDoc, OdfTextDocument secondaryDoc) throws Exception {
//Insert referenced font types that are not in the primary document you are merging into
NodeList sdDomNodes = secondaryDoc.getContentDom().getChildNodes().item(0).getChildNodes();
NodeList pdDomNodes = primaryDoc.getContentDom().getChildNodes().item(0).getChildNodes();
OdfFileDom primaryContentDom = primaryDoc.getContentDom();
Node sdFontNode=null;
Node pdFontNode=null;
for(int i =0; i<sdDomNodes.getLength();i++){
if(sdDomNodes.item(i).getNodeName().equals("office:font-face-decls")){
sdFontNode = sdDomNodes.item(i);
break;
}
}
for(int i =0; i<pdDomNodes.getLength();i++){
Node n =pdDomNodes.item(i);
if(n.getNodeName().equals("office:font-face-decls")){
pdFontNode = pdDomNodes.item(i);
break;
}
}
if(sdFontNode !=null && pdFontNode != null){
NodeList sdFontNodeChildList = sdFontNode.getChildNodes();
NodeList pdFontNodeChildList = pdFontNode.getChildNodes();
List<String> fontNames = new ArrayList<String>();
//Get list of existing fonts in primary doc
for(int i=0; i<pdFontNodeChildList.getLength();i++){
NamedNodeMap attributes = pdFontNodeChildList.item(i).getAttributes();
for(int j=0; j<attributes.getLength();j++){
if(attributes.item(j).getLocalName().equals("name")){
fontNames.add(attributes.item(j).getNodeValue());
}
}
}
//Check each font in the secondary doc to make sure it gets added if the primary doesn't have it
for(int i=0; i<sdFontNodeChildList.getLength();i++){
Node fontNode = sdFontNodeChildList.item(i).cloneNode(true);
NamedNodeMap attributes = fontNode.getAttributes();
String fontName="";
for(int j=0; j< attributes.getLength();j++){
if(attributes.item(j).getLocalName().equals("name")){
fontName = attributes.item(j).getNodeValue();
break;
}
}
if(!fontName.equals("") && !fontNames.contains(fontName)){
pdFontNode.appendChild(primaryContentDom.adoptNode(fontNode));
}
}
}
}
private static void updateNodeChildrenStyleNames(Node n, String stringToAddToStyle, String nodeLocalName){
NodeList childNodes = n.getChildNodes();
for (int i=0; i< childNodes.getLength(); i++){
Node currentChild = childNodes.item(i);
if(currentChild.hasAttributes()){
NamedNodeMap attributes = currentChild.getAttributes();
for(int j =0; j < attributes.getLength(); j++){
Node a = attributes.item(j);
if(a.getLocalName().equals(nodeLocalName)){
a.setNodeValue(a.getNodeValue() + stringToAddToStyle);
}
}
}
if(currentChild.hasChildNodes()){
updateNodeChildrenStyleNames(currentChild, stringToAddToStyle, nodeLocalName);
}
}
}
}