| /* ==================================================================== |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ==================================================================== */ |
| |
| package org.apache.poi.xwpf.usermodel; |
| |
| import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import javax.xml.namespace.QName; |
| |
| import org.apache.poi.ooxml.POIXMLDocumentPart; |
| import org.apache.poi.ooxml.POIXMLException; |
| import org.apache.poi.openxml4j.opc.PackagePart; |
| import org.apache.xmlbeans.XmlException; |
| import org.apache.xmlbeans.XmlOptions; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDocDefaults; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFonts; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTLanguage; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPrDefault; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPr; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPrDefault; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTStyle; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTStyles; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.StylesDocument; |
| |
| /** |
| * Holds details of built-in, default and user styles, which |
| * apply to tables / paragraphs / lists etc. |
| * Text within one of those with custom stylings has the style |
| * information stored in the {@link XWPFRun} |
| */ |
| public class XWPFStyles extends POIXMLDocumentPart { |
| private CTStyles ctStyles; |
| private final List<XWPFStyle> listStyle = new ArrayList<>(); |
| |
| private XWPFLatentStyles latentStyles; |
| private XWPFDefaultRunStyle defaultRunStyle; |
| private XWPFDefaultParagraphStyle defaultParaStyle; |
| |
| /** |
| * Construct XWPFStyles from a package part |
| * |
| * @param part the package part holding the data of the styles, |
| * |
| * @since POI 3.14-Beta1 |
| */ |
| public XWPFStyles(PackagePart part) { |
| super(part); |
| } |
| |
| /** |
| * Construct XWPFStyles from scratch for a new document. |
| */ |
| public XWPFStyles() { |
| } |
| |
| /** |
| * Read document |
| */ |
| @Override |
| protected void onDocumentRead() throws IOException { |
| StylesDocument stylesDoc; |
| try (InputStream is = getPackagePart().getInputStream()) { |
| stylesDoc = StylesDocument.Factory.parse(is, DEFAULT_XML_OPTIONS); |
| setStyles(stylesDoc.getStyles()); |
| latentStyles = new XWPFLatentStyles(ctStyles.getLatentStyles(), this); |
| } catch (XmlException e) { |
| throw new POIXMLException("Unable to read styles", e); |
| } |
| } |
| |
| @Override |
| protected void commit() throws IOException { |
| if (ctStyles == null) { |
| throw new IllegalStateException("Unable to write out styles that were never read in!"); |
| } |
| |
| XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS); |
| xmlOptions.setSaveSyntheticDocumentElement(new QName(CTStyles.type.getName().getNamespaceURI(), "styles")); |
| PackagePart part = getPackagePart(); |
| OutputStream out = part.getOutputStream(); |
| ctStyles.save(out, xmlOptions); |
| out.close(); |
| } |
| |
| protected void ensureDocDefaults() { |
| if (!ctStyles.isSetDocDefaults()) { |
| ctStyles.addNewDocDefaults(); |
| } |
| |
| CTDocDefaults docDefaults = ctStyles.getDocDefaults(); |
| if (!docDefaults.isSetPPrDefault()) |
| docDefaults.addNewPPrDefault(); |
| if (!docDefaults.isSetRPrDefault()) |
| docDefaults.addNewRPrDefault(); |
| |
| CTPPrDefault pprd = docDefaults.getPPrDefault(); |
| CTRPrDefault rprd = docDefaults.getRPrDefault(); |
| if (!pprd.isSetPPr()) pprd.addNewPPr(); |
| if (!rprd.isSetRPr()) rprd.addNewRPr(); |
| |
| defaultRunStyle = new XWPFDefaultRunStyle(rprd.getRPr()); |
| defaultParaStyle = new XWPFDefaultParagraphStyle(pprd.getPPr()); |
| } |
| |
| /** |
| * Sets the ctStyles |
| * |
| * @param styles The CTStyles object to set |
| */ |
| public void setStyles(CTStyles styles) { |
| ctStyles = styles; |
| |
| // Build up all the style objects |
| for (CTStyle style : ctStyles.getStyleArray()) { |
| listStyle.add(new XWPFStyle(style, this)); |
| } |
| if (ctStyles.isSetDocDefaults()) { |
| CTDocDefaults docDefaults = ctStyles.getDocDefaults(); |
| if (docDefaults.isSetRPrDefault() && docDefaults.getRPrDefault().isSetRPr()) { |
| defaultRunStyle = new XWPFDefaultRunStyle( |
| docDefaults.getRPrDefault().getRPr()); |
| } |
| if (docDefaults.isSetPPrDefault() && docDefaults.getPPrDefault().isSetPPr()) { |
| defaultParaStyle = new XWPFDefaultParagraphStyle( |
| docDefaults.getPPrDefault().getPPr()); |
| } |
| } |
| } |
| |
| /** |
| * checks whether style with styleID exist |
| * |
| * @param styleID styleID of the Style in the style-Document |
| * @return true if style exist, false if style not exist |
| */ |
| public boolean styleExist(String styleID) { |
| return null != getStyle(styleID); |
| } |
| |
| /** |
| * add a style to the document |
| * |
| * @param style The style to add |
| */ |
| public void addStyle(XWPFStyle style) { |
| listStyle.add(style); |
| ctStyles.addNewStyle(); |
| int pos = ctStyles.sizeOfStyleArray() - 1; |
| ctStyles.setStyleArray(pos, style.getCTStyle()); |
| } |
| |
| /** |
| * Get style by a styleID |
| * |
| * @param styleID styleID of the searched style |
| * @return style |
| */ |
| public XWPFStyle getStyle(String styleID) { |
| for (XWPFStyle style : listStyle) { |
| if (null != style.getStyleId() && style.getStyleId().equals(styleID)) |
| return style; |
| } |
| return null; |
| } |
| |
| public int getNumberOfStyles() { |
| return listStyle.size(); |
| } |
| |
| /** |
| * get the styles which are related to the parameter style and their relatives |
| * |
| * this method can be used to copy all styles from one document to another document |
| * |
| * @param style The style to look for |
| * @return a list of all styles which were used by this method |
| */ |
| public List<XWPFStyle> getUsedStyleList(XWPFStyle style) { |
| List<XWPFStyle> usedStyleList = new ArrayList<>(); |
| usedStyleList.add(style); |
| return getUsedStyleList(style, usedStyleList); |
| } |
| |
| /** |
| * get the styles which are related to parameter style |
| * |
| * @param style The style to look for |
| * @param usedStyleList The list of current style, found |
| * related styles are added to this list. |
| * |
| * @return all Styles of the parameterList, returns the same object as |
| * the input-parameter usedStyleList |
| */ |
| private List<XWPFStyle> getUsedStyleList(XWPFStyle style, List<XWPFStyle> usedStyleList) { |
| String basisStyleID = style.getBasisStyleID(); |
| XWPFStyle basisStyle = getStyle(basisStyleID); |
| if ((basisStyle != null) && (!usedStyleList.contains(basisStyle))) { |
| usedStyleList.add(basisStyle); |
| getUsedStyleList(basisStyle, usedStyleList); |
| } |
| String linkStyleID = style.getLinkStyleID(); |
| XWPFStyle linkStyle = getStyle(linkStyleID); |
| if ((linkStyle != null) && (!usedStyleList.contains(linkStyle))) { |
| usedStyleList.add(linkStyle); |
| getUsedStyleList(linkStyle, usedStyleList); |
| } |
| |
| String nextStyleID = style.getNextStyleID(); |
| XWPFStyle nextStyle = getStyle(nextStyleID); |
| if ((nextStyle != null) && (!usedStyleList.contains(nextStyle))) { |
| usedStyleList.add(nextStyle); |
| getUsedStyleList(nextStyle, usedStyleList); |
| } |
| return usedStyleList; |
| } |
| |
| protected CTLanguage getCTLanguage() { |
| ensureDocDefaults(); |
| |
| if (defaultRunStyle.getRPr().sizeOfLangArray() > 0) { |
| return defaultRunStyle.getRPr().getLangArray(0); |
| } else { |
| return defaultRunStyle.getRPr().addNewLang(); |
| } |
| } |
| |
| /** |
| * Sets the default spelling language on ctStyles DocDefaults parameter |
| * |
| * @param strSpellingLanguage the default spelling language to use |
| */ |
| public void setSpellingLanguage(String strSpellingLanguage) { |
| CTLanguage lang = getCTLanguage(); |
| lang.setVal(strSpellingLanguage); |
| lang.setBidi(strSpellingLanguage); |
| } |
| |
| /** |
| * Sets the default East Asia spelling language on ctStyles DocDefaults parameter |
| * |
| * @param strEastAsia The default East Asia spelling language to use |
| */ |
| public void setEastAsia(String strEastAsia) { |
| CTLanguage lang = getCTLanguage(); |
| lang.setEastAsia(strEastAsia); |
| } |
| |
| /** |
| * Sets the default font on ctStyles DocDefaults parameter |
| * TODO Replace this with specific setters for each type, possibly |
| * on XWPFDefaultRunStyle |
| */ |
| public void setDefaultFonts(CTFonts fonts) { |
| ensureDocDefaults(); |
| |
| CTRPr runProps = defaultRunStyle.getRPr(); |
| if (runProps.sizeOfRFontsArray() == 0) { |
| runProps.addNewRFonts(); |
| } |
| runProps.setRFontsArray(0, fonts); |
| } |
| |
| /** |
| * get the style with the same name |
| * if this style is not existing, return null |
| */ |
| public XWPFStyle getStyleWithSameName(XWPFStyle style) { |
| for (XWPFStyle ownStyle : listStyle) { |
| if (ownStyle.hasSameName(style)) { |
| return ownStyle; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Get the default style which applies text runs in the document |
| */ |
| public XWPFDefaultRunStyle getDefaultRunStyle() { |
| ensureDocDefaults(); |
| return defaultRunStyle; |
| } |
| |
| /** |
| * Get the default paragraph style which applies to the document |
| */ |
| public XWPFDefaultParagraphStyle getDefaultParagraphStyle() { |
| ensureDocDefaults(); |
| return defaultParaStyle; |
| } |
| |
| /** |
| * Get the definition of all the Latent Styles |
| */ |
| public XWPFLatentStyles getLatentStyles() { |
| return latentStyles; |
| } |
| |
| /** |
| * Get the style with the specified name, if any. |
| * |
| * @param styleName The name of the style to get, e.g., "Heading 1" |
| * @return {@link XWPFStyle} with the specified name, or null if not found. |
| */ |
| public XWPFStyle getStyleWithName(String styleName) { |
| XWPFStyle style = null; |
| for (XWPFStyle cand : listStyle) { |
| if (styleName.equals(cand.getName())) { |
| style = cand; |
| break; |
| } |
| } |
| return style; |
| } |
| } |