| /* ==================================================================== |
| 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.hwpf.usermodel; |
| |
| import java.util.NoSuchElementException; |
| |
| import org.apache.logging.log4j.LogManager; |
| import org.apache.logging.log4j.Logger; |
| import org.apache.poi.common.Duplicatable; |
| import org.apache.poi.hwpf.HWPFDocumentCore; |
| import org.apache.poi.hwpf.model.LFO; |
| import org.apache.poi.hwpf.model.ListLevel; |
| import org.apache.poi.hwpf.model.ListTables; |
| import org.apache.poi.hwpf.model.PAPX; |
| import org.apache.poi.hwpf.model.StyleSheet; |
| import org.apache.poi.hwpf.sprm.ParagraphSprmUncompressor; |
| import org.apache.poi.hwpf.sprm.SprmBuffer; |
| import org.apache.poi.hwpf.sprm.TableSprmCompressor; |
| import org.apache.poi.util.Internal; |
| |
| import static org.apache.logging.log4j.util.Unbox.box; |
| |
| public class Paragraph extends Range implements Duplicatable { |
| private static final Logger LOGGER = LogManager.getLogger(Paragraph.class); |
| |
| public static final short SPRM_JC = 0x2403; |
| public static final short SPRM_FSIDEBYSIDE = 0x2404; |
| public static final short SPRM_FKEEP = 0x2405; |
| public static final short SPRM_FKEEPFOLLOW = 0x2406; |
| public static final short SPRM_FPAGEBREAKBEFORE = 0x2407; |
| public static final short SPRM_BRCL = 0x2408; |
| public static final short SPRM_BRCP = 0x2409; |
| public static final short SPRM_ILVL = 0x260A; |
| public static final short SPRM_ILFO = 0x460B; |
| public static final short SPRM_FNOLINENUMB = 0x240C; |
| public static final short SPRM_CHGTABSPAPX = (short)0xC60D; |
| public static final short SPRM_DXARIGHT = (short)0x840E; |
| public static final short SPRM_DXALEFT = (short)0x840F; |
| public static final short SPRM_DXALEFT1 = (short)0x8411; |
| public static final short SPRM_DYALINE = 0x6412; |
| public static final short SPRM_DYABEFORE = (short)0xA413; |
| public static final short SPRM_DYAAFTER = (short)0xA414; |
| public static final short SPRM_CHGTABS = (short)0xC615; |
| public static final short SPRM_FINTABLE = 0x2416; |
| public static final short SPRM_FTTP = 0x2417; |
| public static final short SPRM_DXAABS = (short)0x8418; |
| public static final short SPRM_DYAABS = (short)0x8419; |
| public static final short SPRM_DXAWIDTH = (short)0x841A; |
| public static final short SPRM_PC = 0x261B; |
| public static final short SPRM_WR = 0x2423; |
| public static final short SPRM_BRCTOP = 0x6424; |
| public static final short SPRM_BRCLEFT = 0x6425; |
| public static final short SPRM_BRCBOTTOM = 0x6426; |
| public static final short SPRM_BRCRIGHT = 0x6427; |
| public static final short SPRM_BRCBAR = 0x6629; |
| public static final short SPRM_FNOAUTOHYPH = 0x242A; |
| public static final short SPRM_WHEIGHTABS = 0x442B; |
| public static final short SPRM_DCS = 0x442C; |
| public static final short SPRM_SHD80 = 0x442D; |
| public static final short SPRM_SHD = (short)0xC64D; |
| public static final short SPRM_DYAFROMTEXT = (short)0x842E; |
| public static final short SPRM_DXAFROMTEXT = (short)0x842F; |
| public static final short SPRM_FLOCKED = 0x2430; |
| public static final short SPRM_FWIDOWCONTROL = 0x2431; |
| public static final short SPRM_RULER = (short)0xC632; |
| public static final short SPRM_FKINSOKU = 0x2433; |
| public static final short SPRM_FWORDWRAP = 0x2434; |
| public static final short SPRM_FOVERFLOWPUNCT = 0x2435; |
| public static final short SPRM_FTOPLINEPUNCT = 0x2436; |
| public static final short SPRM_AUTOSPACEDE = 0x2437; |
| public static final short SPRM_AUTOSPACEDN = 0x2438; |
| public static final short SPRM_WALIGNFONT = 0x4439; |
| public static final short SPRM_FRAMETEXTFLOW = 0x443A; |
| public static final short SPRM_ANLD = (short)0xC63E; |
| public static final short SPRM_PROPRMARK = (short)0xC63F; |
| public static final short SPRM_OUTLVL = 0x2640; |
| public static final short SPRM_FBIDI = 0x2441; |
| public static final short SPRM_FNUMRMLNS = 0x2443; |
| public static final short SPRM_CRLF = 0x2444; |
| public static final short SPRM_NUMRM = (short)0xC645; |
| public static final short SPRM_USEPGSUSETTINGS = 0x2447; |
| public static final short SPRM_FADJUSTRIGHT = 0x2448; |
| |
| @Internal |
| public static Paragraph newParagraph( Range parent, PAPX papx ) |
| { |
| HWPFDocumentCore doc = parent._doc; |
| ListTables listTables = doc.getListTables(); |
| StyleSheet styleSheet = doc.getStyleSheet(); |
| |
| ParagraphProperties properties = new ParagraphProperties(); |
| properties.setIstd( papx.getIstd() ); |
| |
| properties = newParagraph_applyStyleProperties( styleSheet, papx, |
| properties ); |
| properties = ParagraphSprmUncompressor.uncompressPAP( properties, |
| papx.getGrpprl(), 2 ); |
| |
| if ( properties.getIlfo() != 0 && listTables != null ) |
| { |
| LFO lfo = null; |
| try |
| { |
| lfo = listTables.getLfo( properties.getIlfo() ); |
| } |
| catch ( NoSuchElementException exc ) |
| { |
| LOGGER.atWarn().log("Paragraph refers to LFO #{} that does not exists", box(properties.getIlfo())); |
| } |
| if ( lfo != null ) |
| { |
| final ListLevel listLevel = listTables.getLevel( lfo.getLsid(), |
| properties.getIlvl() ); |
| |
| if ( listLevel != null && listLevel.getGrpprlPapx() != null ) |
| { |
| properties = ParagraphSprmUncompressor.uncompressPAP( |
| properties, listLevel.getGrpprlPapx(), 0 ); |
| // reapply style and local PAPX properties |
| properties = newParagraph_applyStyleProperties( styleSheet, |
| papx, properties ); |
| properties = ParagraphSprmUncompressor.uncompressPAP( |
| properties, papx.getGrpprl(), 2 ); |
| } |
| } |
| } |
| |
| if ( properties.getIlfo() > 0 ) |
| return new ListEntry( papx, properties, parent ); |
| |
| return new Paragraph( papx, properties, parent ); |
| } |
| |
| protected static ParagraphProperties newParagraph_applyStyleProperties( |
| StyleSheet styleSheet, PAPX papx, ParagraphProperties properties ) |
| { |
| if ( styleSheet == null ) |
| return properties; |
| |
| int style = papx.getIstd(); |
| byte[] grpprl = styleSheet.getPAPX( style ); |
| return ParagraphSprmUncompressor.uncompressPAP( properties, grpprl, 2 ); |
| } |
| |
| protected short _istd; |
| protected ParagraphProperties _props; |
| protected SprmBuffer _papx; |
| |
| @Internal |
| Paragraph( PAPX papx, ParagraphProperties properties, Range parent ) |
| { |
| super( Math.max( parent._start, papx.getStart() ), Math.min( |
| parent._end, papx.getEnd() ), parent ); |
| _props = properties; |
| _papx = papx.getSprmBuf(); |
| _istd = papx.getIstd(); |
| } |
| |
| Paragraph(Paragraph other) { |
| super(other); |
| _istd = other._istd; |
| _props = (other._props == null) ? null : other._props.copy(); |
| _papx = (other._papx == null) ? null : other._papx.copy(); |
| } |
| |
| /** |
| * Returns the index of the style which applies to this |
| * Paragraph. Details of the style can be looked up |
| * from the {@link StyleSheet}, via |
| * {@link StyleSheet#getStyleDescription(int)} |
| */ |
| public short getStyleIndex() |
| { |
| return _istd; |
| } |
| |
| public boolean isInTable() |
| { |
| return _props.getFInTable(); |
| } |
| |
| /** |
| * @return <tt>true</tt>, if table trailer paragraph (last in table row), |
| * <tt>false</tt> otherwise |
| */ |
| public boolean isTableRowEnd() |
| { |
| return _props.getFTtp() || _props.getFTtpEmbedded(); |
| } |
| |
| public int getTableLevel() |
| { |
| return _props.getItap(); |
| } |
| |
| /** |
| * @return <tt>true</tt>, if the end of paragraph mark is really an end of |
| * cell mark for a nested table cell, <tt>false</tt> otherwise |
| */ |
| public boolean isEmbeddedCellMark() |
| { |
| return _props.getFInnerTableCell(); |
| } |
| |
| public int getJustification() |
| { |
| return _props.getJc(); |
| } |
| |
| public void setJustification(byte jc) |
| { |
| _props.setJc(jc); |
| _papx.updateSprm(SPRM_JC, jc); |
| } |
| |
| public boolean keepOnPage() |
| { |
| return _props.getFKeep(); |
| } |
| |
| public void setKeepOnPage(boolean fKeep) |
| { |
| _props.setFKeep(fKeep); |
| _papx.updateSprm(SPRM_FKEEP, fKeep); |
| } |
| |
| public boolean keepWithNext() |
| { |
| return _props.getFKeepFollow(); |
| } |
| |
| public void setKeepWithNext(boolean fKeepFollow) |
| { |
| _props.setFKeepFollow(fKeepFollow); |
| _papx.updateSprm(SPRM_FKEEPFOLLOW, fKeepFollow); |
| } |
| |
| public boolean pageBreakBefore() |
| { |
| return _props.getFPageBreakBefore(); |
| } |
| |
| public void setPageBreakBefore(boolean fPageBreak) |
| { |
| _props.setFPageBreakBefore(fPageBreak); |
| _papx.updateSprm(SPRM_FPAGEBREAKBEFORE, fPageBreak); |
| } |
| |
| public boolean isLineNotNumbered() |
| { |
| return _props.getFNoLnn(); |
| } |
| |
| public void setLineNotNumbered(boolean fNoLnn) |
| { |
| _props.setFNoLnn(fNoLnn); |
| _papx.updateSprm(SPRM_FNOLINENUMB, fNoLnn); |
| } |
| |
| public boolean isSideBySide() |
| { |
| return _props.getFSideBySide(); |
| } |
| |
| public void setSideBySide(boolean fSideBySide) |
| { |
| _props.setFSideBySide(fSideBySide); |
| _papx.updateSprm(SPRM_FSIDEBYSIDE, fSideBySide); |
| } |
| |
| public boolean isAutoHyphenated() |
| { |
| return !_props.getFNoAutoHyph(); |
| } |
| |
| public void setAutoHyphenated(boolean autoHyph) |
| { |
| _props.setFNoAutoHyph(!autoHyph); |
| _papx.updateSprm(SPRM_FNOAUTOHYPH, !autoHyph); |
| } |
| |
| public boolean isWidowControlled() |
| { |
| return _props.getFWidowControl(); |
| } |
| |
| public void setWidowControl(boolean widowControl) |
| { |
| _props.setFWidowControl(widowControl); |
| _papx.updateSprm(SPRM_FWIDOWCONTROL, widowControl); |
| } |
| |
| public int getIndentFromRight() |
| { |
| return _props.getDxaRight(); |
| } |
| |
| public void setIndentFromRight(int dxaRight) |
| { |
| _props.setDxaRight(dxaRight); |
| _papx.updateSprm(SPRM_DXARIGHT, (short)dxaRight); |
| } |
| |
| public int getIndentFromLeft() |
| { |
| return _props.getDxaLeft(); |
| } |
| |
| public void setIndentFromLeft(int dxaLeft) |
| { |
| _props.setDxaLeft(dxaLeft); |
| _papx.updateSprm(SPRM_DXALEFT, (short)dxaLeft); |
| } |
| |
| public int getFirstLineIndent() |
| { |
| return _props.getDxaLeft1(); |
| } |
| |
| public void setFirstLineIndent(int first) |
| { |
| _props.setDxaLeft1(first); |
| _papx.updateSprm(SPRM_DXALEFT1, (short)first); |
| } |
| |
| public LineSpacingDescriptor getLineSpacing() |
| { |
| return _props.getLspd(); |
| } |
| |
| public void setLineSpacing(LineSpacingDescriptor lspd) |
| { |
| _props.setLspd(lspd); |
| _papx.updateSprm(SPRM_DYALINE, lspd.toInt()); |
| } |
| |
| public int getSpacingBefore() |
| { |
| return _props.getDyaBefore(); |
| } |
| |
| public void setSpacingBefore(int before) |
| { |
| _props.setDyaBefore(before); |
| _papx.updateSprm(SPRM_DYABEFORE, (short)before); |
| } |
| |
| public int getSpacingAfter() |
| { |
| return _props.getDyaAfter(); |
| } |
| |
| public void setSpacingAfter(int after) |
| { |
| _props.setDyaAfter(after); |
| _papx.updateSprm(SPRM_DYAAFTER, (short)after); |
| } |
| |
| public boolean isKinsoku() |
| { |
| return _props.getFKinsoku(); |
| } |
| |
| public void setKinsoku(boolean kinsoku) |
| { |
| _props.setFKinsoku(kinsoku); |
| _papx.updateSprm(SPRM_FKINSOKU, kinsoku); |
| } |
| |
| public boolean isWordWrapped() |
| { |
| return _props.getFWordWrap(); |
| } |
| |
| public void setWordWrapped(boolean wrap) |
| { |
| _props.setFWordWrap(wrap); |
| _papx.updateSprm(SPRM_FWORDWRAP, wrap); |
| } |
| |
| public int getFontAlignment() |
| { |
| return _props.getWAlignFont(); |
| } |
| |
| public void setFontAlignment(int align) |
| { |
| _props.setWAlignFont(align); |
| _papx.updateSprm(SPRM_WALIGNFONT, (short)align); |
| } |
| |
| public boolean isVertical() |
| { |
| return _props.isFVertical(); |
| } |
| |
| public void setVertical(boolean vertical) |
| { |
| _props.setFVertical(vertical); |
| _papx.updateSprm(SPRM_FRAMETEXTFLOW, getFrameTextFlow()); |
| } |
| |
| public boolean isBackward() |
| { |
| return _props.isFBackward(); |
| } |
| |
| public void setBackward(boolean bward) |
| { |
| _props.setFBackward(bward); |
| _papx.updateSprm(SPRM_FRAMETEXTFLOW, getFrameTextFlow()); |
| } |
| |
| public BorderCode getTopBorder() |
| { |
| return _props.getBrcTop(); |
| } |
| |
| public void setTopBorder(BorderCode top) |
| { |
| _props.setBrcTop(top); |
| _papx.updateSprm(SPRM_BRCTOP, top.toInt()); |
| } |
| |
| public BorderCode getLeftBorder() |
| { |
| return _props.getBrcLeft(); |
| } |
| |
| public void setLeftBorder(BorderCode left) |
| { |
| _props.setBrcLeft(left); |
| _papx.updateSprm(SPRM_BRCLEFT, left.toInt()); |
| } |
| |
| public BorderCode getBottomBorder() |
| { |
| return _props.getBrcBottom(); |
| } |
| |
| public void setBottomBorder(BorderCode bottom) |
| { |
| _props.setBrcBottom(bottom); |
| _papx.updateSprm(SPRM_BRCBOTTOM, bottom.toInt()); |
| } |
| |
| public BorderCode getRightBorder() |
| { |
| return _props.getBrcRight(); |
| } |
| |
| public void setRightBorder(BorderCode right) |
| { |
| _props.setBrcRight(right); |
| _papx.updateSprm(SPRM_BRCRIGHT, right.toInt()); |
| } |
| |
| public BorderCode getBarBorder() |
| { |
| return _props.getBrcBar(); |
| } |
| |
| public void setBarBorder(BorderCode bar) |
| { |
| _props.setBrcBar(bar); |
| _papx.updateSprm(SPRM_BRCBAR, bar.toInt()); |
| } |
| |
| public ShadingDescriptor getShading() |
| { |
| return _props.getShd(); |
| } |
| |
| public void setShading(ShadingDescriptor shd) |
| { |
| _props.setShd(shd); |
| //TODO: remove old one |
| _papx.addSprm( SPRM_SHD, shd.serialize() ); |
| } |
| |
| public DropCapSpecifier getDropCap() |
| { |
| return _props.getDcs(); |
| } |
| |
| public void setDropCap(DropCapSpecifier dcs) |
| { |
| _props.setDcs(dcs); |
| _papx.updateSprm(SPRM_DCS, dcs.toShort()); |
| } |
| |
| /** |
| * Returns the ilfo, an index to the document's hpllfo, which |
| * describes the automatic number formatting of the paragraph. |
| * A value of zero means it isn't numbered. |
| */ |
| public int getIlfo() |
| { |
| return _props.getIlfo(); |
| } |
| |
| /** |
| * Returns the multi-level indent for the paragraph. Will be |
| * zero for non-list paragraphs, and the first level of any |
| * list. Subsequent levels in hold values 1-8. |
| */ |
| public int getIlvl() |
| { |
| return _props.getIlvl(); |
| } |
| |
| /** |
| * Returns the heading level (1-8), or 9 if the paragraph |
| * isn't in a heading style. |
| */ |
| public int getLvl() |
| { |
| return _props.getLvl(); |
| } |
| |
| void setTableRowEnd(TableProperties props) |
| { |
| setTableRowEnd(true); |
| byte[] grpprl = TableSprmCompressor.compressTableProperty(props); |
| _papx.append(grpprl); |
| } |
| |
| private void setTableRowEnd(boolean val) |
| { |
| _props.setFTtp(val); |
| _papx.updateSprm(SPRM_FTTP, val); |
| } |
| |
| /** |
| * Returns number of tabs stops defined for paragraph. Must be >= 0 and <= 64. |
| * |
| * @return number of tabs stops defined for paragraph. Must be >= 0 and <= 64 |
| */ |
| public int getTabStopsNumber() |
| { |
| return _props.getItbdMac(); |
| } |
| |
| /** |
| * Returns array of positions of itbdMac tab stops |
| * |
| * @return array of positions of itbdMac tab stops |
| */ |
| public int[] getTabStopsPositions() |
| { |
| return _props.getRgdxaTab(); |
| } |
| |
| public HWPFList getList() |
| { |
| if ( getIlfo() == 0x000 || getIlfo() == 0xF801 ) |
| { |
| throw new IllegalStateException( "Paragraph not in list" ); |
| } |
| return new HWPFList( getDocument().getStyleSheet(), |
| getDocument().getListTables(), getIlfo() ); |
| } |
| |
| public boolean isInList() |
| { |
| return getIlfo() != 0x000 && getIlfo() != 0xF801; |
| } |
| |
| /** |
| * Clone the ParagraphProperties object associated with this |
| * Paragraph, so that you can apply the same properties to |
| * another Paragraph. |
| */ |
| public ParagraphProperties cloneProperties() { |
| return _props.copy(); |
| } |
| |
| @Override |
| public Paragraph copy() { |
| return new Paragraph(this); |
| } |
| |
| private short getFrameTextFlow() |
| { |
| short retVal = 0; |
| if (_props.isFVertical()) |
| { |
| retVal |= 1; |
| } |
| if (_props.isFBackward()) |
| { |
| retVal |= 2; |
| } |
| if (_props.isFRotateFont()) |
| { |
| retVal |= 4; |
| } |
| return retVal; |
| } |
| |
| @Override |
| public String toString() |
| { |
| return "Paragraph [" + getStartOffset() + "; " + getEndOffset() + ")"; |
| } |
| |
| @Internal |
| public ParagraphProperties getProps() { |
| return _props; |
| } |
| } |