向Docx4j生成的word文档中添加布局--第二部分

原文标题:Adding layout to your Docx4j-generated word documents, part 2

原文链接:http://blog.iprofs.nl/2012/11/19/adding-layout-to-your-docx4j-generated-word-documents-part-2/

原文作者:lvdpal

发表日期:2012年11月19日

注:我没有再试着翻译代码中的注释,因为我觉得我水平实在有限,翻译过来的注释还不如看英文来地明白...



在前面发表的两篇博客中,我写了一些关于在docx文档中创建表格添加图像和布局的内容。本篇博客中我将继续介绍一些文档布局相关的示例:


更改默认样式


几乎所有的客户都想要他们自己的风格。Word提供了一些默认的样式但它们不够好(注意一下我没有更改两个图像之间的文本,因此图像中的文本并不反映实际的风格):

大多数客户会想改变这些风格,在应用了我们在本例中创建的风格之后,上面的word文档看起来会像这样:

public class ChangingTheStyleSheet {
    private static WordprocessingMLPackage  wordMLPackage;
 
    /**
     *  First we create the package, then we alter the style sheet and add some
     *  styled paragraphs. Finally we save the package.
     */
    public static void main (String[] args) throws Docx4JException {
        wordMLPackage = WordprocessingMLPackage.createPackage();
        alterStyleSheet();
 
        wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Title",
            "Hello World! This title is now in Arial.");
        wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Subtitle",
            "Subtitle, this subtitle is now Arial too");
        wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Heading1",
            "As is Heading1");
        wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Heading2",
            "Heading2 is now Arial, no longer bold and has an underline " +
            "and fontsize 12");
        wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Heading3",
            "Heading3 is now Arial");
        wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Normal",
            "And normal text has changed to Arial and fontsize 10");
 
        wordMLPackage.save(new java.io.File("src/main/files/HelloWord12.docx") );
    }
 
    /**
     *  This method alters the default style sheet that is part of each document.
     *
     *  To do this, we first retrieve the style sheet from the package and then
     *  get the Styles object from it. From this object, we get the list of actual
     *  styles and iterate over them.
     *  We check against all styles we want to alter and apply the alterations if
     *  applicable.
     *
     *  @param wordMLPackage
     */
    public static void alterStyleSheet() {
        StyleDefinitionsPart styleDefinitionsPart =
            wordMLPackage.getMainDocumentPart().getStyleDefinitionsPart();
        Styles styles = styleDefinitionsPart.getJaxbElement();
 
        List<Style>  stylesList = styles.getStyle();
        for (Style style : stylesList) {
            if (style.getStyleId().equals("Normal")) {
                alterNormalStyle(style);
            } else if (style.getStyleId().equals("Heading2")) {
                alterHeading2Style(style);
            } else if (style.getStyleId().equals("Heading1") ||
                    style.getStyleId().equals("Heading3") ||
                    style.getStyleId().equals("Title") ||
                    style.getStyleId().equals("Subtitle")) {
                getRunPropertiesAndRemoveThemeInfo(style);
            }
        }
    }
 
    /**
     *  First we create a run properties object as we want to remove nearly all of
     *  the existing styling. Then we change the font and font size and set the
     *  run properties on the given style. As in previous examples, the font size
     *  is defined to be in half-point size.
     */
    private static void alterNormalStyle(Style style) {
        // we want to change (or remove) almost all the run properties of the
        // normal style, so we create a new one.
        RPr rpr = new RPr();
        changeFontToArial(rpr);
        changeFontSize(rpr, 20);
        style.setRPr(rpr);
    }
 
    /**
     *  For this style, we get the existing run properties from the style and
     *  remove the theme font information from them. Then we also remove the bold
     *  styling, change the font size (half-points) and add an underline.
     */
    private static void alterHeading2Style(Style style) {
        RPr rpr = getRunPropertiesAndRemoveThemeInfo(style);
        removeBoldStyle(rpr);
        changeFontSize(rpr, 24);
        addUnderline(rpr);
    }
 
    private static RPr getRunPropertiesAndRemoveThemeInfo(Style style) {
        // We only want to change some settings, so we get the existing run
        // properties from the style.
        RPr rpr = style.getRPr();
        removeThemeFontInformation(rpr);
        return rpr;
    }
 
    /**
     *  Change the font of the given run properties to Arial.
     *
     *  A run font specifies the fonts which shall be used to display the contents
     *  of the run. Of the four possible types of content, we change the styling of
     *  two of them: ASCII and High ANSI.
     *  Finally we add the run font to the run properties.
     *
     *  @param runProperties
     */
    private static void changeFontToArial(RPr runProperties) {
        RFonts runFont = new RFonts();
        runFont.setAscii("Arial");
        runFont.setHAnsi("Arial");
        runProperties.setRFonts(runFont);
    }
 
    /**
     * Change the font size of the given run properties to the given value.
     *
     * @param runProperties
     * @param fontSize  Twice the size needed, as it is specified as half-point value
     */
    private static void changeFontSize(RPr runProperties, int fontSize) {
        HpsMeasure size = new HpsMeasure();
        size.setVal(BigInteger.valueOf(fontSize));
        runProperties.setSz(size);
    }
 
    /**
     * Removes the theme font information from the run properties.
     * If this is not removed then the styles based on the normal style won't
     * inherit the Arial font from the normal style.
     *
     * @param runProperties
     */
    private static void removeThemeFontInformation(RPr runProperties) {
        runProperties.getRFonts().setAsciiTheme(null);
        runProperties.getRFonts().setHAnsiTheme(null);
    }
 
    /**
     * Removes the Bold styling from the run properties.
     *
     * @param runProperties
     */
    private static void removeBoldStyle(RPr runProperties) {
        runProperties.getB().setVal(false);
    }
 
    /**
     * Adds a single underline to the run properties.
     *
     * @param runProperties
     */
    private static void addUnderline(RPr runProperties) {
        U underline = new U();
        underline.setVal(UnderlineEnumeration.SINGLE);
        runProperties.setU(underline );
    }
}

添加页脚或页眉


添加页脚或页眉(或二者兼具)是一个很多客户都想要的特性。幸运的是,做这个并不是很难。这个例子是docx4j示例的一部分,但是我删掉了添加图像的部分只展示最基本的添加页脚部分。添加页眉,你需要做的仅仅是用header来替换footer(或者hdr替换ftr)。

public class AddingAFooter {
    private static WordprocessingMLPackage wordMLPackage;
    private static ObjectFactory factory;
 
    /**
     *  First we create the package and the factory. Then we create the footer part,
     *  which returns a relationship. This relationship is then used to create
     *  a reference. Finally we add some text to the document and save it.
     */
    public static void main (String[] args) throws Docx4JException {
        wordMLPackage = WordprocessingMLPackage.createPackage();
        factory = Context.getWmlObjectFactory();
 
        Relationship relationship = createFooterPart();
        createFooterReference(relationship);
 
        wordMLPackage.getMainDocumentPart().addParagraphOfText("Hello Word!");
 
        wordMLPackage.save(new File("src/main/files/HelloWord14.docx") );
    }
 
    /**
     *  This method creates a footer part and set the package on it. Then we add some
     *  text and add the footer part to the package. Finally we return the
     *  corresponding relationship.
     *
     *  @return
     *  @throws InvalidFormatException
     */
    private static Relationship createFooterPart() throws InvalidFormatException {
        FooterPart footerPart = new FooterPart();
        footerPart.setPackage(wordMLPackage);
 
        footerPart.setJaxbElement(createFooter("Text"));
 
        return wordMLPackage.getMainDocumentPart().addTargetPart(footerPart);
    }
 
    /**
     *  First we create a footer, a paragraph, a run and a text. We add the given
     *  given content to the text and add that to the run. The run is then added to
     *  the paragraph, which is in turn added to the footer. Finally we return the
     *  footer.
     *
     *  @param content
     *  @return
     */
    private static Ftr createFooter(String content) {
        Ftr footer = factory.createFtr();
        P paragraph = factory.createP();
        R run = factory.createR();
        Text text = new Text();
        text.setValue(content);
        run.getContent().add(text);
        paragraph.getContent().add(run);
        footer.getContent().add(paragraph);
        return footer;
    }
 
    /**
     *  First we retrieve the document sections from the package. As we want to add
     *  a footer, we get the last section and take the section properties from it.
     *  The section is always present, but it might not have properties, so we check
     *  if they exist to see if we should create them. If they need to be created,
     *  we do and add them to the main document part and the section.
     *  Then we create a reference to the footer, give it the id of the relationship,
     *  set the type to header/footer reference and add it to the collection of
     *  references to headers and footers in the section properties.
     *
     * @param relationship
     */
    private static void createFooterReference(Relationship relationship) {
        List<SectionWrapper> sections =
            wordMLPackage.getDocumentModel().getSections();
 
        SectPr sectionProperties = sections.get(sections.size() - 1).getSectPr();
        // There is always a section wrapper, but it might not contain a sectPr
        if (sectionProperties==null ) {
            sectionProperties = factory.createSectPr();
            wordMLPackage.getMainDocumentPart().addObject(sectionProperties);
            sections.get(sections.size() - 1).setSectPr(sectionProperties);
        }
 
        FooterReference footerReference = factory.createFooterReference();
        footerReference.setId(relationship.getId());
        footerReference.setType(HdrFtrRef.DEFAULT);
        sectionProperties.getEGHdrFtrReferences().add(footerReference);
    }
}

在页脚添加页码


我在本博客要讨论的最后一个特性是怎样在我们刚创建的页脚处添加页码。你可能已经知道,在Word中页码是一个域,因此本例或多或少是前面的两个例子的结合:目录表和页脚。

public class AddingPageNrToFooter {
    private static WordprocessingMLPackage wordMLPackage;
    private static ObjectFactory factory;
 
    /**
     *  First we create the package and the factory. Then we create the footer.
     *  Finally we add two pages with text to the document and save it.
     */
    public static void main (String[] args) throws Exception {
        wordMLPackage = WordprocessingMLPackage.createPackage();
        factory = Context.getWmlObjectFactory();
 
        Relationship relationship = createFooterPart();
        createFooterReference(relationship);
 
        MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
 
        documentPart.addParagraphOfText("Hello World!");
 
        addPageBreak(documentPart);
 
        documentPart.addParagraphOfText("This is page 2!");
        wordMLPackage.save(new File("src/main/files/HelloWord15.docx") );
    }
 
    /**
     *  As in the previous example, this method creates a footer part and adds it to
     *  the main document and then returns the corresponding relationship.
     *
     *  @return
     *  @throws InvalidFormatException
     */
    private static Relationship createFooterPart() throws InvalidFormatException {
        FooterPart footerPart = new FooterPart();
        footerPart.setPackage(wordMLPackage);
 
        footerPart.setJaxbElement(createFooterWithPageNr());
 
        return wordMLPackage.getMainDocumentPart().addTargetPart(footerPart);
    }
 
    /**
     *  As in the previous example, we create a footer and a paragraph object. But
     *  this time, instead of adding text to a run, we add a field. And just as with
     *  the table of content, we have to add a begin and end character around the
     *  actual field with the page number. Finally we add the paragraph to the
     *  content of the footer and then return it.
     *
     * @return
     */
    public static Ftr createFooterWithPageNr() {
        Ftr ftr = factory.createFtr();
        P paragraph = factory.createP();
 
        addFieldBegin(paragraph);
        addPageNumberField(paragraph);
        addFieldEnd(paragraph);
 
        ftr.getContent().add(paragraph);
        return ftr;
    }
 
    /**
     *  Creating the page number field is nearly the same as creating the field in
     *  the TOC example. The only difference is in the value. We use the PAGE
     *  command, which prints the number of the current page, together with the
     *  MERGEFORMAT switch, which indicates that the current formatting should be
     *  preserved when the field is updated.
     *
     * @param paragraph
     */
    private static void addPageNumberField(P paragraph) {
        R run = factory.createR();
        Text txt = new Text();
        txt.setSpace("preserve");
        txt.setValue(" PAGE   \\* MERGEFORMAT ");
        run.getContent().add(factory.createRInstrText(txt));
        paragraph.getContent().add(run);
    }
 
    /**
     * Every fields needs to be delimited by complex field characters. This method
     * adds the delimiter that precedes the actual field to the given paragraph.
     * @param paragraph
     */
    private static void addFieldBegin(P paragraph) {
        R run = factory.createR();
        FldChar fldchar = factory.createFldChar();
        fldchar.setFldCharType(STFldCharType.BEGIN);
        run.getContent().add(fldchar);
        paragraph.getContent().add(run);
    }
 
    /**
     * Every fields needs to be delimited by complex field characters. This method
     * adds the delimiter that follows the actual field to the given paragraph.
     * @param paragraph
     */
    private static void addFieldEnd(P paragraph) {
        FldChar fldcharend = factory.createFldChar();
        fldcharend.setFldCharType(STFldCharType.END);
        R run3 = factory.createR();
        run3.getContent().add(fldcharend);
        paragraph.getContent().add(run3);
    }
 
    /**
     * This method fetches the document final section properties, and adds a newly
     * created footer reference to them.
     *
     * @param relationship
     */
    public static void createFooterReference(Relationship relationship){
 
        List<SectionWrapper> sections =
            wordMLPackage.getDocumentModel().getSections();
 
        SectPr sectPr = sections.get(sections.size() - 1).getSectPr();
        // There is always a section wrapper, but it might not contain a sectPr
        if (sectPr==null ) {
            sectPr = factory.createSectPr();
            wordMLPackage.getMainDocumentPart().addObject(sectPr);
            sections.get(sections.size() - 1).setSectPr(sectPr);
        }
 
        FooterReference footerReference = factory.createFooterReference();
        footerReference.setId(relationship.getId());
        footerReference.setType(HdrFtrRef.DEFAULT);
        sectPr.getEGHdrFtrReferences().add(footerReference);
    }
 
    /**
     * Adds a page break to the document.
     *
     * @param documentPart
     */
    private static void addPageBreak(MainDocumentPart documentPart) {
        Br breakObj = new Br();
        breakObj.setType(STBrType.PAGE);
 
        P paragraph = factory.createP();
        paragraph.getContent().add(breakObj);
        documentPart.getJaxbElement().getBody().getContent().add(paragraph);
    }
}

总结


在三篇博客中,我展示了怎样创建一个word文档以及一些很常用的特性。这些示例全都是我的客户要求我需要创建的word文档。而且我觉得它们足以让你知道如何去做更多其它的事情,如果不是,用你所需要的特性创建一个文档并去查看内部的XML,如果一切仍然不能奏效,你可以到Docx4j论坛进行提问,docx4j的作者会定期尝试回答所有的问题。

在以后的博客中我会展示一些关于xlsx文档的示例。


  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: 要在前端展示Word文档的内容,可以通过以下几个步骤来实现。 首先,需要将Word文档转换成可被前端读取和展示的格式。可以使用一些库或工具来将Word文档转换成HTML格式或纯文本格式。例如,使用python-docx库可以将Word文档转换成HTML格式,然后将生成的HTML内容保存在后端的数据库生成一个HTML文件。 接下来,在前端使用JavaScript来读取和展示转换后的内容。可以使用JavaScript的XMLHttpRequest对象或Fetch API来从后端获取转换后的HTML内容。然后,可以将获取到的HTML内容插入到HTML页面的合适元素,例如使用innerHTML属性或createElement方法来动态创建HTML元素并添加内容。这样,Word文档的内容就可以在前端进行展示了。 同时,为了提供更好的用户体验,可以使用一些JavaScript库或框架来定制展示样式和功能。例如,可以使用jQuery库来操作和修改动态添加的HTML内容,或使用Bootstrap框架来进行响应式设计和布局。 总结起来,要在前端展示Word文档的内容,需要将文档转换成可被前端读取和展示的格式,然后使用JavaScript进行内容的读取和展示,并可根据需求使用额外的库或框架来定制展示样式和功能。 ### 回答2: 要实现前端展示word文档内容,可以使用JavaScript来读取word文档,并将其内容展示在前端页面上。 一种常用的方法是使用`FileReader`对象来读取word文档。首先,通过HTML的`<input type="file">`元素,用户能够选择本地存储的word文档文件。当用户选择文件后,可以通过JavaScript监听到该事件。接着,使用`FileReader`对象的`readAsArrayBuffer()`方法将选择的文件转换为二进制数组(ArrayBuffer)。 读取完毕后,可以使用`mammoth.js`等第三方库来解析二进制数据。mammoth.js是一个开源的JavaScript库,可以将docx格式的word文档转换为HTML格式。通过引入mammoth.js库,可以将二进制数据发送到其提供的处理函数,得到转换后的HTML内容。 最后,在前端页面上展示word文档内容,可以将转换后的HTML内容插入到页面的DOM元素,比如一个`<div>`元素。可以使用JavaScript的API,如`document.getElementById()`方法,获取DOM元素,将解析完成的HTML内容设置到该元素的`innerHTML`属性上。 需要注意的是,这只是一个简单的示例,并不提供完整的word文档解析和展示的功能。如果要实现更复杂的功能,可能需要使用更高级的库或者调用后端接口。 ### 回答3: 要在前端展示Word文档的内容,可以使用JS来读取文档内容并进行展示。以下是一种可能的实现方式: 首先,需要使用JS读取Word文档的内容。可以借助第三方库,如`mammoth.js`或`docxtemplater`来进行解析,并将文档转换为HTML格式。这些库提供了API可以将Word文档的内容转换为HTML格式的字符串。 接下来,可以通过使用`innerHTML`属性将解析得到的HTML内容插入到HTML页面的指定元素,从而展示Word文档的内容。假设有一个具有`id`属性为`wordContent`的元素,可以通过`document.getElementById('wordContent').innerHTML = convertedHtml`将解析得到的HTML内容插入到该元素。 需要注意的是,由于Word文档可能包含复杂的样式和格式,转换得到的HTML可能无法完美展示所有内容。可以根据实际需求,对HTML进行进一步处理和样式调整,以获得更好的展示效果。 总而言之,通过使用JS读取Word文档的内容,并将其转换成HTML格式来展示,可以在前端实现展示Word文档的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值