如何为 CommentsExtended.xml & CommentsExtensible.xml 添加 类 到 POI 3.14
How to add Classes for CommentsExtended.xml & CommentsExtensible.xml to POI 3.14
我知道 POI 仅支持 2007 年的 XSD,但 2012 年引入了 CommentsExtended.xml 和 CommentsExtensible.xml。但我需要在 POI 中添加此功能并最终使用它.我已经下载了这两个文件的 XSD。但不知道从哪里开始。我通常知道 POI 使用 Xmlbeans 为 XSD 模式生成低级别 类。但不知道从哪里开始以及如何进行。任何帮助都将非常宝贵。
第一次尝试
乍一看,我尝试添加创建 CommentsExtended.xml 包并使用 OPCPackage API 添加关系并成功。但问题似乎是创建了一个像 commentsExtended.xml.rels 这样的新关系文件,而不是我想在已经存在的 document.xml.rels 中添加 commentsExtended.xml 的条目。
我做错了什么?如何在 document.xml.rels 本身中为 commentsExtended 文件添加 realtion ? Alex ritcher
public static void main(String[] args) throws Exception {
createCommentsExtendedPackage();
}
private static void createCommentsExtendedPackage() throws Exception {
XWPFDocument document = new XWPFDocument();
PackagePartName partName = PackagingURIHelper.createPartName("/word/commentsExtended.xml");
OPCPackage opcPackage = document.getPackage();
PackagePart part = opcPackage.createPart(partName, ContentTypes.PLAIN_OLD_XML);
OutputStream outputStream = part.getOutputStream();
outputStream.write("<test>A</test>".getBytes());
outputStream.close();
//Creates commentsExtended.xml.rels file which is not needed.
document.getPackagePart().addRelationship(partName, TargetMode.INTERNAL, PackageRelationshipTypes.CUSTOM_XML);
//When trying to add relationShip in document getting NULL Pointer Exception
POIXMLDocumentPart documentPart = new POIXMLDocumentPart(part);
String rIdExtLink = "rId" + (document.getRelationParts().size()+1);
document.addRelation(rIdExtLink, documentPart);
// Save document
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
document.write(outStream);
byte[] fileBytes = outStream.toByteArray();
FileUtil.write("CommentsExtended.docx", fileBytes); //no i18n
}
第二次尝试:
我现在可以使用下面的代码在 document.xml.rels 中添加 commentsExtended.xml 关系,但现在我在 commentsExtended.xml 中的值在保存时被清除。设置值后如何在 commentsExtended.xml 中保留值。 非常感谢任何帮助。
private static void createCommentsExtendedPackage() throws Exception {
XWPFDocument document = new XWPFDocument();
OPCPackage opcPackage = document.getPackage();
PackagePartName partName = PackagingURIHelper.createPartName("/word/commentsExtended.xml");
PackagePart part = opcPackage.createPart(partName, ContentTypes.PLAIN_OLD_XML);
class CommentsExtendedRelation extends POIXMLRelation {
private CommentsExtendedRelation() {
super( "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml",
"http://schemas.microsoft.com/office/2011/relationships/commentsExtended",
"/word/commentsExtended.xml");
}
}
//Adding relation in document.xml.rels
POIXMLDocumentPart documentPart = new POIXMLDocumentPart(part);
String rIdExtLink = "rId" + (document.getRelationParts().size()+1);
document.addRelation(rIdExtLink, new CommentsExtendedRelation() , documentPart);
OutputStream outputStream = part.getOutputStream();
outputStream.write("<test>A</test>".getBytes());
outputStream.close();
// Create main document part
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
document.write(outStream);
byte[] fileBytes = outStream.toByteArray();
FileUtil.write("CommentsExtendedOld.docx", fileBytes); //no i18n
}
为了说明原理,我会尽量给出一个完整的例子,尽量少。
起初:Apache POI 3.14 太旧了。我的回答依赖于当前 apache poi 5.2.2
.
使用 XMLBeans
从 *.xsd
模式文件生成 com.microsoft.schemas.office.word.x2012.wordml.*
classes
word12.xsd
可以从微软的源码中获取:5.2 http://schemas.microsoft.com/office/word/2012/wordml Schema。但它必须稍微改变一下才能编译。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:**w12**="http://schemas.openxmlformats.org/wordprocessingml/2006/main" ...>
为什么 wordprocessingml/2006/
名称 space 有前缀 w12
?这很烦人,最好是 w06
.
<xsd:import id="w12" namespace=**"http://schemas.openxmlformats.org/wordprocessingml/2006/main"** ...>
为什么要将众所周知的wordprocessingml/2006/
名称space额外导入为w12
?这违反了给定的 targetNamespace
。应该是
<xsd:import id="w12" namespace="http://schemas.microsoft.com/office/word/2012/wordml" ...>
并且由于 ECMA-376-Fifth-Edition 类型 ST_OnOff
和 ST_String
不再出现在 http://schemas.openxmlformats.org/wordprocessingml/2006/main
namespace 中,而是出现在 http://schemas.openxmlformats.org/officeDocument/2006/sharedTypes
.所以我们需要一个额外的名字space:
... xmlns:w06st="http://schemas.openxmlformats.org/officeDocument/2006/sharedTypes" ...
完整word12.xsd
:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:w06="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:w06st="http://schemas.openxmlformats.org/officeDocument/2006/sharedTypes"
elementFormDefault="qualified" attributeFormDefault="qualified" blockDefault="#all"
xmlns="http://schemas.microsoft.com/office/word/2012/wordml"
targetNamespace="http://schemas.microsoft.com/office/word/2012/wordml">
<xsd:import id="w12" namespace="http://schemas.microsoft.com/office/word/2012/wordml" schemaLocation="word12.xsd"/>
<xsd:element name="color" type="w06:CT_Color"/>
<xsd:simpleType name="ST_SdtAppearance">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="boundingBox"/>
<xsd:enumeration value="tags"/>
<xsd:enumeration value="hidden"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:element name="dataBinding" type="w06:CT_DataBinding"/>
<xsd:complexType name="CT_SdtAppearance">
<xsd:attribute name="val" type="ST_SdtAppearance"/>
</xsd:complexType>
<xsd:element name="appearance" type="CT_SdtAppearance"/>
<xsd:complexType name="CT_CommentsEx">
<xsd:sequence>
<xsd:element name="commentEx" type="CT_CommentEx" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CT_CommentEx">
<xsd:attribute name="paraId" type="w06:ST_LongHexNumber" use="required"/>
<xsd:attribute name="paraIdParent" type="w06:ST_LongHexNumber" use="optional"/>
<xsd:attribute name="done" type="w06st:ST_OnOff" use="optional"/>
</xsd:complexType>
<xsd:element name="commentsEx" type="CT_CommentsEx"/>
<xsd:complexType name="CT_People">
<xsd:sequence>
<xsd:element name="person" type="CT_Person" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CT_PresenceInfo">
<xsd:attribute name="providerId" type="xsd:string" use="required"/>
<xsd:attribute name="userId" type="xsd:string" use="required"/>
</xsd:complexType>
<xsd:complexType name="CT_Person">
<xsd:sequence>
<xsd:element name="presenceInfo" type="CT_PresenceInfo" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:attribute name="author" type="w06st:ST_String" use="required"/>
</xsd:complexType>
<xsd:element name="people" type="CT_People"/>
<xsd:complexType name="CT_SdtRepeatedSection">
<xsd:sequence>
<xsd:element name="sectionTitle" type="w06:CT_String" minOccurs="0"/>
<xsd:element name="doNotAllowInsertDeleteSection" type="w06:CT_OnOff" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:simpleType name="ST_Guid">
<xsd:restriction base="xsd:token">
<xsd:pattern value="\{[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\}"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="CT_Guid">
<xsd:attribute name="val" type="ST_Guid"/>
</xsd:complexType>
<xsd:element name="repeatingSection" type="CT_SdtRepeatedSection"/>
<xsd:element name="repeatingSectionItem" type="w06:CT_Empty"/>
<xsd:element name="chartTrackingRefBased" type="w06:CT_OnOff"/>
<xsd:element name="collapsed" type="w06:CT_OnOff"/>
<xsd:element name="docId" type="CT_Guid"/>
<xsd:element name="footnoteColumns" type="w06:CT_DecimalNumber"/>
<xsd:element name="webExtensionLinked" type="w06:CT_OnOff"/>
<xsd:element name="webExtensionCreated" type="w06:CT_OnOff"/>
<xsd:attribute name="restartNumberingAfterBreak" type="w06st:ST_OnOff"/>
</xsd:schema>
中的word12.xsd
指的是其他类型。因此,在编译时需要访问其他方案。这些可以从 ECMA-376 获得,下载第 4 部分并解压 OfficeOpenXML-XMLSchema-Transitional.zip
ECMA-376-Fifth-Edition-Part-4-Transitional-Migration-Features.zip
到文件系统的目录。我这样做是为了 ./xsd/ooxml
(将 .
替换为您的特殊位置)。也将 word12.xsd
复制到此目录。
现在我们可以使用scomp
进行编译了。
scomp -src ./xmlbeans/ooxml/src -out ./xmlbeans/ooxml/ooxml-schemas-word12-5.2.2.jar ./xsd/ooxml/word12.xsd ./xsd/ooxml/shared-commonSimpleTypes.xsd ./xsd/ooxml/shared-math.xsd
这会编译三个模式文件 ./xsd/ooxml/word12.xsd ./xsd/ooxml/shared-commonSimpleTypes.xsd ./xsd/ooxml/shared-math.xsd
并创建 JAR ./xmlbeans/ooxml/ooxml-schemas-word12-5.2.2.jar
并将 *.java
源文件放入 ./xmlbeans/ooxml/src
.
编译后,您的 ooxml-schemas-word12-5.2.2.jar
包含新的 com.microsoft.schemas.office.word.x2012.wordml.*
classes。如果使用 XmlBeans 编译有问题,这里是结果 ooxml-schemas-word12-5.2.2.jar。这必须在编译时位于 class 路径中,并且在代码后 运行 中。
使用 ooxml-schemas-word12-5.2.2.jar
中的 com.microsoft.schemas.office.word.x2012.wordml.*
classes 创建一个 XWPFDocument
具有注释并使用扩展注释属性
我们知道 *.docx
ZIP 压缩包中需要 /word/comments.xml
和 /word/commentsExtended.xml
。在 apache poi
ooxml
中,此 XML 文件称为文档部件。必须有扩展 POIXMLDocumentPart
的 XWPF
classes 来处理这些文档部分。此 classes 需要覆盖 protected void commit()
才能在 XWPFDocument.write
时保存内容。此外,我们需要一种方法来创建(添加)此文档部分到 XWPFDocument
的 OPCPackage
(ZipPackage
)。文档部分和 OPCPackage
之间的关系使用 POIXMLRelation
保存。所以我们需要 XWPFRelation.COMMENT
,它已经在 apache poi
中提供,另外 XWPFCommentsExRelation
扩展了 POIXMLRelation
.
要在 CTCommentsEx
中引用,CTComment
中的段落需要名称 space http://schemas.microsoft.com/office/word/2010/wordml
中的属性 paraId
。我们应该避免导入和编译 5.1 http://schemas.microsoft.com/office/word/2010/wordml Schema 只是为了能够设置这个属性。所以我们使用 XmlCursor.insertAttributeWithValue
将该属性添加到 CTComment
中的 CTP
。 (注意:上面链接的 XSD 似乎甚至不是名称 space http://schemas.microsoft.com/office/word/2010/wordml
的完整模式。至少 [=75= 中没有属性 paraId
的定义].)
将所有这些放在一起我们可以得到以下代码:
import java.io.*;
import org.apache.poi.openxml4j.opc.*;
import org.apache.xmlbeans.*;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.ooxml.*;
import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import com.microsoft.schemas.office.word.x2012.wordml.*;
import javax.xml.namespace.QName;
import java.math.BigInteger;
import java.util.GregorianCalendar;
import java.util.Locale;
public class CreateWordWithCommentsAndCommentsEx {
//a method for creating the CommentsDocument /word/comments.xml in the *.docx ZIP archive
private static MyXWPFCommentsDocument createCommentsDocument(XWPFDocument document) throws Exception {
OPCPackage oPCPackage = document.getPackage();
PackagePartName partName = PackagingURIHelper.createPartName("/word/comments.xml");
PackagePart part = oPCPackage.createPart(partName, "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml");
MyXWPFCommentsDocument myXWPFCommentsDocument = new MyXWPFCommentsDocument(part);
String rId = document.addRelation(null, XWPFRelation.COMMENT, myXWPFCommentsDocument).getRelationship().getId();
return myXWPFCommentsDocument;
}
//a method for creating the CommentsExtendedDocument /word/commentsExtended.xml in the *.docx ZIP archive
private static MyXWPFCommentsExtendedDocument createCommentsExtendedDocument(XWPFDocument document) throws Exception {
OPCPackage oPCPackage = document.getPackage();
PackagePartName partName = PackagingURIHelper.createPartName("/word/commentsExtended.xml");
PackagePart part = oPCPackage.createPart(partName, "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml");
MyXWPFCommentsExtendedDocument myXWPFCommentsExtendedDocument = new MyXWPFCommentsExtendedDocument(part);
String rId = document.addRelation(null, new XWPFCommentsExRelation(), myXWPFCommentsExtendedDocument).getRelationship().getId();
return myXWPFCommentsExtendedDocument;
}
public static void main(String[] args) throws Exception {
XWPFDocument document = new XWPFDocument();
MyXWPFCommentsDocument myXWPFCommentsDocument = createCommentsDocument(document);
MyXWPFCommentsExtendedDocument myXWPFCommentsExtendedDocument = createCommentsExtendedDocument(document);
CTComments comments = myXWPFCommentsDocument.getComments();
CTCommentsEx commentsEx = myXWPFCommentsExtendedDocument.getCommentsEx();
CTComment ctComment;
CTCommentEx ctCommentEx;
CTP ctP;
XWPFParagraph paragraph;
XWPFRun run;
BigInteger cId;
XmlCursor cursor;
//first comment
cId = BigInteger.ZERO;
ctComment = comments.addNewComment();
ctComment.setAuthor("Axel Richter");
ctComment.setInitials("AR");
ctComment.setDate(new GregorianCalendar(Locale.US));
ctP = ctComment.addNewP();
cursor = ctP.newCursor();
cursor.toNextToken();
cursor.insertAttributeWithValue(new QName("http://schemas.microsoft.com/office/word/2010/wordml", "paraId"), "01020304");
cursor.dispose();
ctP.addNewR().addNewT().setStringValue("The first comment.");
ctComment.setId(cId);
ctCommentEx = commentsEx.addNewCommentEx();
ctCommentEx.setParaId(new byte[]{1,2,3,4});
paragraph = document.createParagraph();
paragraph.getCTP().addNewCommentRangeStart().setId(cId);
run = paragraph.createRun();
run.setText("Paragraph with the first comment.");
paragraph.getCTP().addNewCommentRangeEnd().setId(cId);
paragraph.getCTP().addNewR().addNewCommentReference().setId(cId);
//sub comment to first comment
cId = cId.add(BigInteger.ONE);
ctComment = comments.addNewComment();
ctComment.setAuthor("Axel Richter");
ctComment.setInitials("AR");
ctComment.setDate(new GregorianCalendar(Locale.US));
ctP = ctComment.addNewP();
cursor = ctP.newCursor();
cursor.toNextToken();
cursor.insertAttributeWithValue(new QName("http://schemas.microsoft.com/office/word/2010/wordml", "paraId"), "01020305");
cursor.dispose();
ctP.addNewR().addNewT().setStringValue("Sub comment to the first comment.");
ctComment.setId(cId);
ctCommentEx = commentsEx.addNewCommentEx();
ctCommentEx.setParaId(new byte[]{1,2,3,5});
ctCommentEx.setParaIdParent(new byte[]{1,2,3,4});
paragraph.getCTP().addNewCommentRangeStart().setId(cId);
paragraph.getCTP().addNewCommentRangeEnd().setId(cId);
paragraph.getCTP().addNewR().addNewCommentReference().setId(cId);
//paragraph without comment
paragraph = document.createParagraph();
run = paragraph.createRun();
run.setText("Paragraph without comment.");
//second comment
cId = cId.add(BigInteger.ONE);
ctComment = comments.addNewComment();
ctComment.setAuthor("Axel Richter");
ctComment.setInitials("AR");
ctComment.setDate(new GregorianCalendar(Locale.US));
ctComment.addNewP().addNewR().addNewT().setStringValue("The second comment.");
ctComment.setId(cId);
// ctCommentEx = commentsEx.addNewCommentEx();
paragraph = document.createParagraph();
paragraph.getCTP().addNewCommentRangeStart().setId(cId);
run = paragraph.createRun();
run.setText("Paragraph with the second comment.");
paragraph.getCTP().addNewCommentRangeEnd().setId(cId);
paragraph.getCTP().addNewR().addNewCommentReference().setId(cId);
//write document
FileOutputStream out = new FileOutputStream("CreateWordWithComments.docx");
document.write(out);
out.close();
document.close();
}
//a wrapper class for the CommentsDocument /word/comments.xml in the *.docx ZIP archive
private static class MyXWPFCommentsDocument extends POIXMLDocumentPart {
private CTComments comments;
private MyXWPFCommentsDocument(PackagePart part) throws Exception {
super(part);
comments = CommentsDocument.Factory.newInstance().addNewComments();
}
private CTComments getComments() {
return comments;
}
@Override
protected void commit() throws IOException {
XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
xmlOptions.setSaveSyntheticDocumentElement(new QName(CTComments.type.getName().getNamespaceURI(), "comments"));
PackagePart part = getPackagePart();
OutputStream out = part.getOutputStream();
comments.save(out, xmlOptions);
out.close();
}
}
//a wrapper class for the CommentsExDocument /word/commentsExtended.xml in the *.docx ZIP archive
private static class MyXWPFCommentsExtendedDocument extends POIXMLDocumentPart {
private CTCommentsEx commentsEx;
private MyXWPFCommentsExtendedDocument(PackagePart part) throws Exception {
super(part);
commentsEx = CommentsExDocument.Factory.newInstance().addNewCommentsEx();
}
private CTCommentsEx getCommentsEx() {
return commentsEx;
}
@Override
protected void commit() throws IOException {
XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
xmlOptions.setSaveSyntheticDocumentElement(new QName(CTCommentsEx.type.getName().getNamespaceURI(), "commentsExtended"));
PackagePart part = getPackagePart();
OutputStream out = part.getOutputStream();
commentsEx.save(out, xmlOptions);
out.close();
}
}
//the XWPFRelation for /word/commentsExtended.xml
private final static class XWPFCommentsExRelation extends POIXMLRelation {
private XWPFCommentsExRelation() {
super(
"application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml",
"http://schemas.microsoft.com/office/2011/relationships/commentsExtended",
"/word/commentsExtended.xml");
}
}
}
这会生成:
我知道 POI 仅支持 2007 年的 XSD,但 2012 年引入了 CommentsExtended.xml 和 CommentsExtensible.xml。但我需要在 POI 中添加此功能并最终使用它.我已经下载了这两个文件的 XSD。但不知道从哪里开始。我通常知道 POI 使用 Xmlbeans 为 XSD 模式生成低级别 类。但不知道从哪里开始以及如何进行。任何帮助都将非常宝贵。
第一次尝试
乍一看,我尝试添加创建 CommentsExtended.xml 包并使用 OPCPackage API 添加关系并成功。但问题似乎是创建了一个像 commentsExtended.xml.rels 这样的新关系文件,而不是我想在已经存在的 document.xml.rels 中添加 commentsExtended.xml 的条目。 我做错了什么?如何在 document.xml.rels 本身中为 commentsExtended 文件添加 realtion ? Alex ritcher
public static void main(String[] args) throws Exception {
createCommentsExtendedPackage();
}
private static void createCommentsExtendedPackage() throws Exception {
XWPFDocument document = new XWPFDocument();
PackagePartName partName = PackagingURIHelper.createPartName("/word/commentsExtended.xml");
OPCPackage opcPackage = document.getPackage();
PackagePart part = opcPackage.createPart(partName, ContentTypes.PLAIN_OLD_XML);
OutputStream outputStream = part.getOutputStream();
outputStream.write("<test>A</test>".getBytes());
outputStream.close();
//Creates commentsExtended.xml.rels file which is not needed.
document.getPackagePart().addRelationship(partName, TargetMode.INTERNAL, PackageRelationshipTypes.CUSTOM_XML);
//When trying to add relationShip in document getting NULL Pointer Exception
POIXMLDocumentPart documentPart = new POIXMLDocumentPart(part);
String rIdExtLink = "rId" + (document.getRelationParts().size()+1);
document.addRelation(rIdExtLink, documentPart);
// Save document
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
document.write(outStream);
byte[] fileBytes = outStream.toByteArray();
FileUtil.write("CommentsExtended.docx", fileBytes); //no i18n
}
第二次尝试: 我现在可以使用下面的代码在 document.xml.rels 中添加 commentsExtended.xml 关系,但现在我在 commentsExtended.xml 中的值在保存时被清除。设置值后如何在 commentsExtended.xml 中保留值。 非常感谢任何帮助。
private static void createCommentsExtendedPackage() throws Exception {
XWPFDocument document = new XWPFDocument();
OPCPackage opcPackage = document.getPackage();
PackagePartName partName = PackagingURIHelper.createPartName("/word/commentsExtended.xml");
PackagePart part = opcPackage.createPart(partName, ContentTypes.PLAIN_OLD_XML);
class CommentsExtendedRelation extends POIXMLRelation {
private CommentsExtendedRelation() {
super( "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml",
"http://schemas.microsoft.com/office/2011/relationships/commentsExtended",
"/word/commentsExtended.xml");
}
}
//Adding relation in document.xml.rels
POIXMLDocumentPart documentPart = new POIXMLDocumentPart(part);
String rIdExtLink = "rId" + (document.getRelationParts().size()+1);
document.addRelation(rIdExtLink, new CommentsExtendedRelation() , documentPart);
OutputStream outputStream = part.getOutputStream();
outputStream.write("<test>A</test>".getBytes());
outputStream.close();
// Create main document part
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
document.write(outStream);
byte[] fileBytes = outStream.toByteArray();
FileUtil.write("CommentsExtendedOld.docx", fileBytes); //no i18n
}
为了说明原理,我会尽量给出一个完整的例子,尽量少。
起初:Apache POI 3.14 太旧了。我的回答依赖于当前 apache poi 5.2.2
.
使用 XMLBeans
从*.xsd
模式文件生成 com.microsoft.schemas.office.word.x2012.wordml.*
classes
word12.xsd
可以从微软的源码中获取:5.2 http://schemas.microsoft.com/office/word/2012/wordml Schema。但它必须稍微改变一下才能编译。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:**w12**="http://schemas.openxmlformats.org/wordprocessingml/2006/main" ...>
为什么 wordprocessingml/2006/
名称 space 有前缀 w12
?这很烦人,最好是 w06
.
<xsd:import id="w12" namespace=**"http://schemas.openxmlformats.org/wordprocessingml/2006/main"** ...>
为什么要将众所周知的wordprocessingml/2006/
名称space额外导入为w12
?这违反了给定的 targetNamespace
。应该是
<xsd:import id="w12" namespace="http://schemas.microsoft.com/office/word/2012/wordml" ...>
并且由于 ECMA-376-Fifth-Edition 类型 ST_OnOff
和 ST_String
不再出现在 http://schemas.openxmlformats.org/wordprocessingml/2006/main
namespace 中,而是出现在 http://schemas.openxmlformats.org/officeDocument/2006/sharedTypes
.所以我们需要一个额外的名字space:
... xmlns:w06st="http://schemas.openxmlformats.org/officeDocument/2006/sharedTypes" ...
完整word12.xsd
:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:w06="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:w06st="http://schemas.openxmlformats.org/officeDocument/2006/sharedTypes"
elementFormDefault="qualified" attributeFormDefault="qualified" blockDefault="#all"
xmlns="http://schemas.microsoft.com/office/word/2012/wordml"
targetNamespace="http://schemas.microsoft.com/office/word/2012/wordml">
<xsd:import id="w12" namespace="http://schemas.microsoft.com/office/word/2012/wordml" schemaLocation="word12.xsd"/>
<xsd:element name="color" type="w06:CT_Color"/>
<xsd:simpleType name="ST_SdtAppearance">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="boundingBox"/>
<xsd:enumeration value="tags"/>
<xsd:enumeration value="hidden"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:element name="dataBinding" type="w06:CT_DataBinding"/>
<xsd:complexType name="CT_SdtAppearance">
<xsd:attribute name="val" type="ST_SdtAppearance"/>
</xsd:complexType>
<xsd:element name="appearance" type="CT_SdtAppearance"/>
<xsd:complexType name="CT_CommentsEx">
<xsd:sequence>
<xsd:element name="commentEx" type="CT_CommentEx" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CT_CommentEx">
<xsd:attribute name="paraId" type="w06:ST_LongHexNumber" use="required"/>
<xsd:attribute name="paraIdParent" type="w06:ST_LongHexNumber" use="optional"/>
<xsd:attribute name="done" type="w06st:ST_OnOff" use="optional"/>
</xsd:complexType>
<xsd:element name="commentsEx" type="CT_CommentsEx"/>
<xsd:complexType name="CT_People">
<xsd:sequence>
<xsd:element name="person" type="CT_Person" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CT_PresenceInfo">
<xsd:attribute name="providerId" type="xsd:string" use="required"/>
<xsd:attribute name="userId" type="xsd:string" use="required"/>
</xsd:complexType>
<xsd:complexType name="CT_Person">
<xsd:sequence>
<xsd:element name="presenceInfo" type="CT_PresenceInfo" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:attribute name="author" type="w06st:ST_String" use="required"/>
</xsd:complexType>
<xsd:element name="people" type="CT_People"/>
<xsd:complexType name="CT_SdtRepeatedSection">
<xsd:sequence>
<xsd:element name="sectionTitle" type="w06:CT_String" minOccurs="0"/>
<xsd:element name="doNotAllowInsertDeleteSection" type="w06:CT_OnOff" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:simpleType name="ST_Guid">
<xsd:restriction base="xsd:token">
<xsd:pattern value="\{[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\}"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="CT_Guid">
<xsd:attribute name="val" type="ST_Guid"/>
</xsd:complexType>
<xsd:element name="repeatingSection" type="CT_SdtRepeatedSection"/>
<xsd:element name="repeatingSectionItem" type="w06:CT_Empty"/>
<xsd:element name="chartTrackingRefBased" type="w06:CT_OnOff"/>
<xsd:element name="collapsed" type="w06:CT_OnOff"/>
<xsd:element name="docId" type="CT_Guid"/>
<xsd:element name="footnoteColumns" type="w06:CT_DecimalNumber"/>
<xsd:element name="webExtensionLinked" type="w06:CT_OnOff"/>
<xsd:element name="webExtensionCreated" type="w06:CT_OnOff"/>
<xsd:attribute name="restartNumberingAfterBreak" type="w06st:ST_OnOff"/>
</xsd:schema>
中的word12.xsd
指的是其他类型。因此,在编译时需要访问其他方案。这些可以从 ECMA-376 获得,下载第 4 部分并解压 OfficeOpenXML-XMLSchema-Transitional.zip
ECMA-376-Fifth-Edition-Part-4-Transitional-Migration-Features.zip
到文件系统的目录。我这样做是为了 ./xsd/ooxml
(将 .
替换为您的特殊位置)。也将 word12.xsd
复制到此目录。
现在我们可以使用scomp
进行编译了。
scomp -src ./xmlbeans/ooxml/src -out ./xmlbeans/ooxml/ooxml-schemas-word12-5.2.2.jar ./xsd/ooxml/word12.xsd ./xsd/ooxml/shared-commonSimpleTypes.xsd ./xsd/ooxml/shared-math.xsd
这会编译三个模式文件 ./xsd/ooxml/word12.xsd ./xsd/ooxml/shared-commonSimpleTypes.xsd ./xsd/ooxml/shared-math.xsd
并创建 JAR ./xmlbeans/ooxml/ooxml-schemas-word12-5.2.2.jar
并将 *.java
源文件放入 ./xmlbeans/ooxml/src
.
编译后,您的 ooxml-schemas-word12-5.2.2.jar
包含新的 com.microsoft.schemas.office.word.x2012.wordml.*
classes。如果使用 XmlBeans 编译有问题,这里是结果 ooxml-schemas-word12-5.2.2.jar。这必须在编译时位于 class 路径中,并且在代码后 运行 中。
使用 ooxml-schemas-word12-5.2.2.jar
中的 com.microsoft.schemas.office.word.x2012.wordml.*
classes 创建一个 XWPFDocument
具有注释并使用扩展注释属性
我们知道 *.docx
ZIP 压缩包中需要 /word/comments.xml
和 /word/commentsExtended.xml
。在 apache poi
ooxml
中,此 XML 文件称为文档部件。必须有扩展 POIXMLDocumentPart
的 XWPF
classes 来处理这些文档部分。此 classes 需要覆盖 protected void commit()
才能在 XWPFDocument.write
时保存内容。此外,我们需要一种方法来创建(添加)此文档部分到 XWPFDocument
的 OPCPackage
(ZipPackage
)。文档部分和 OPCPackage
之间的关系使用 POIXMLRelation
保存。所以我们需要 XWPFRelation.COMMENT
,它已经在 apache poi
中提供,另外 XWPFCommentsExRelation
扩展了 POIXMLRelation
.
要在 CTCommentsEx
中引用,CTComment
中的段落需要名称 space http://schemas.microsoft.com/office/word/2010/wordml
中的属性 paraId
。我们应该避免导入和编译 5.1 http://schemas.microsoft.com/office/word/2010/wordml Schema 只是为了能够设置这个属性。所以我们使用 XmlCursor.insertAttributeWithValue
将该属性添加到 CTComment
中的 CTP
。 (注意:上面链接的 XSD 似乎甚至不是名称 space http://schemas.microsoft.com/office/word/2010/wordml
的完整模式。至少 [=75= 中没有属性 paraId
的定义].)
将所有这些放在一起我们可以得到以下代码:
import java.io.*;
import org.apache.poi.openxml4j.opc.*;
import org.apache.xmlbeans.*;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.ooxml.*;
import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import com.microsoft.schemas.office.word.x2012.wordml.*;
import javax.xml.namespace.QName;
import java.math.BigInteger;
import java.util.GregorianCalendar;
import java.util.Locale;
public class CreateWordWithCommentsAndCommentsEx {
//a method for creating the CommentsDocument /word/comments.xml in the *.docx ZIP archive
private static MyXWPFCommentsDocument createCommentsDocument(XWPFDocument document) throws Exception {
OPCPackage oPCPackage = document.getPackage();
PackagePartName partName = PackagingURIHelper.createPartName("/word/comments.xml");
PackagePart part = oPCPackage.createPart(partName, "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml");
MyXWPFCommentsDocument myXWPFCommentsDocument = new MyXWPFCommentsDocument(part);
String rId = document.addRelation(null, XWPFRelation.COMMENT, myXWPFCommentsDocument).getRelationship().getId();
return myXWPFCommentsDocument;
}
//a method for creating the CommentsExtendedDocument /word/commentsExtended.xml in the *.docx ZIP archive
private static MyXWPFCommentsExtendedDocument createCommentsExtendedDocument(XWPFDocument document) throws Exception {
OPCPackage oPCPackage = document.getPackage();
PackagePartName partName = PackagingURIHelper.createPartName("/word/commentsExtended.xml");
PackagePart part = oPCPackage.createPart(partName, "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml");
MyXWPFCommentsExtendedDocument myXWPFCommentsExtendedDocument = new MyXWPFCommentsExtendedDocument(part);
String rId = document.addRelation(null, new XWPFCommentsExRelation(), myXWPFCommentsExtendedDocument).getRelationship().getId();
return myXWPFCommentsExtendedDocument;
}
public static void main(String[] args) throws Exception {
XWPFDocument document = new XWPFDocument();
MyXWPFCommentsDocument myXWPFCommentsDocument = createCommentsDocument(document);
MyXWPFCommentsExtendedDocument myXWPFCommentsExtendedDocument = createCommentsExtendedDocument(document);
CTComments comments = myXWPFCommentsDocument.getComments();
CTCommentsEx commentsEx = myXWPFCommentsExtendedDocument.getCommentsEx();
CTComment ctComment;
CTCommentEx ctCommentEx;
CTP ctP;
XWPFParagraph paragraph;
XWPFRun run;
BigInteger cId;
XmlCursor cursor;
//first comment
cId = BigInteger.ZERO;
ctComment = comments.addNewComment();
ctComment.setAuthor("Axel Richter");
ctComment.setInitials("AR");
ctComment.setDate(new GregorianCalendar(Locale.US));
ctP = ctComment.addNewP();
cursor = ctP.newCursor();
cursor.toNextToken();
cursor.insertAttributeWithValue(new QName("http://schemas.microsoft.com/office/word/2010/wordml", "paraId"), "01020304");
cursor.dispose();
ctP.addNewR().addNewT().setStringValue("The first comment.");
ctComment.setId(cId);
ctCommentEx = commentsEx.addNewCommentEx();
ctCommentEx.setParaId(new byte[]{1,2,3,4});
paragraph = document.createParagraph();
paragraph.getCTP().addNewCommentRangeStart().setId(cId);
run = paragraph.createRun();
run.setText("Paragraph with the first comment.");
paragraph.getCTP().addNewCommentRangeEnd().setId(cId);
paragraph.getCTP().addNewR().addNewCommentReference().setId(cId);
//sub comment to first comment
cId = cId.add(BigInteger.ONE);
ctComment = comments.addNewComment();
ctComment.setAuthor("Axel Richter");
ctComment.setInitials("AR");
ctComment.setDate(new GregorianCalendar(Locale.US));
ctP = ctComment.addNewP();
cursor = ctP.newCursor();
cursor.toNextToken();
cursor.insertAttributeWithValue(new QName("http://schemas.microsoft.com/office/word/2010/wordml", "paraId"), "01020305");
cursor.dispose();
ctP.addNewR().addNewT().setStringValue("Sub comment to the first comment.");
ctComment.setId(cId);
ctCommentEx = commentsEx.addNewCommentEx();
ctCommentEx.setParaId(new byte[]{1,2,3,5});
ctCommentEx.setParaIdParent(new byte[]{1,2,3,4});
paragraph.getCTP().addNewCommentRangeStart().setId(cId);
paragraph.getCTP().addNewCommentRangeEnd().setId(cId);
paragraph.getCTP().addNewR().addNewCommentReference().setId(cId);
//paragraph without comment
paragraph = document.createParagraph();
run = paragraph.createRun();
run.setText("Paragraph without comment.");
//second comment
cId = cId.add(BigInteger.ONE);
ctComment = comments.addNewComment();
ctComment.setAuthor("Axel Richter");
ctComment.setInitials("AR");
ctComment.setDate(new GregorianCalendar(Locale.US));
ctComment.addNewP().addNewR().addNewT().setStringValue("The second comment.");
ctComment.setId(cId);
// ctCommentEx = commentsEx.addNewCommentEx();
paragraph = document.createParagraph();
paragraph.getCTP().addNewCommentRangeStart().setId(cId);
run = paragraph.createRun();
run.setText("Paragraph with the second comment.");
paragraph.getCTP().addNewCommentRangeEnd().setId(cId);
paragraph.getCTP().addNewR().addNewCommentReference().setId(cId);
//write document
FileOutputStream out = new FileOutputStream("CreateWordWithComments.docx");
document.write(out);
out.close();
document.close();
}
//a wrapper class for the CommentsDocument /word/comments.xml in the *.docx ZIP archive
private static class MyXWPFCommentsDocument extends POIXMLDocumentPart {
private CTComments comments;
private MyXWPFCommentsDocument(PackagePart part) throws Exception {
super(part);
comments = CommentsDocument.Factory.newInstance().addNewComments();
}
private CTComments getComments() {
return comments;
}
@Override
protected void commit() throws IOException {
XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
xmlOptions.setSaveSyntheticDocumentElement(new QName(CTComments.type.getName().getNamespaceURI(), "comments"));
PackagePart part = getPackagePart();
OutputStream out = part.getOutputStream();
comments.save(out, xmlOptions);
out.close();
}
}
//a wrapper class for the CommentsExDocument /word/commentsExtended.xml in the *.docx ZIP archive
private static class MyXWPFCommentsExtendedDocument extends POIXMLDocumentPart {
private CTCommentsEx commentsEx;
private MyXWPFCommentsExtendedDocument(PackagePart part) throws Exception {
super(part);
commentsEx = CommentsExDocument.Factory.newInstance().addNewCommentsEx();
}
private CTCommentsEx getCommentsEx() {
return commentsEx;
}
@Override
protected void commit() throws IOException {
XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
xmlOptions.setSaveSyntheticDocumentElement(new QName(CTCommentsEx.type.getName().getNamespaceURI(), "commentsExtended"));
PackagePart part = getPackagePart();
OutputStream out = part.getOutputStream();
commentsEx.save(out, xmlOptions);
out.close();
}
}
//the XWPFRelation for /word/commentsExtended.xml
private final static class XWPFCommentsExRelation extends POIXMLRelation {
private XWPFCommentsExRelation() {
super(
"application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml",
"http://schemas.microsoft.com/office/2011/relationships/commentsExtended",
"/word/commentsExtended.xml");
}
}
}
这会生成: