| /* ==================================================================== |
| 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.sprm; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.poi.hwpf.model.TabDescriptor; |
| import org.apache.poi.hwpf.usermodel.BorderCode; |
| import org.apache.poi.hwpf.usermodel.DateAndTime; |
| import org.apache.poi.hwpf.usermodel.DropCapSpecifier; |
| import org.apache.poi.hwpf.usermodel.LineSpacingDescriptor; |
| import org.apache.poi.hwpf.usermodel.ParagraphProperties; |
| import org.apache.poi.hwpf.usermodel.ShadingDescriptor; |
| import org.apache.poi.hwpf.usermodel.ShadingDescriptor80; |
| import org.apache.poi.util.Internal; |
| import org.apache.poi.util.LittleEndian; |
| import org.apache.poi.util.POILogFactory; |
| import org.apache.poi.util.POILogger; |
| |
| @Internal |
| public final class ParagraphSprmUncompressor |
| extends SprmUncompressor |
| { |
| private static final POILogger logger = POILogFactory |
| .getLogger( ParagraphSprmUncompressor.class ); |
| |
| public ParagraphSprmUncompressor() |
| { |
| } |
| |
| public static ParagraphProperties uncompressPAP(ParagraphProperties parent, |
| byte[] grpprl, |
| int offset) |
| { |
| ParagraphProperties newProperties = null; |
| try |
| { |
| newProperties = (ParagraphProperties) parent.clone(); |
| } |
| catch (CloneNotSupportedException cnse) |
| { |
| throw new RuntimeException("There is no way this exception should happen!!"); |
| } |
| SprmIterator sprmIt = new SprmIterator(grpprl, offset); |
| |
| while (sprmIt.hasNext()) |
| { |
| SprmOperation sprm = sprmIt.next(); |
| |
| // PAPXs can contain table sprms if the paragraph marks the end of a |
| // table row |
| if (sprm.getType() == SprmOperation.TYPE_PAP) |
| { |
| try |
| { |
| unCompressPAPOperation( newProperties, sprm ); |
| } |
| catch ( Exception exc ) |
| { |
| logger.log( |
| POILogger.ERROR, |
| "Unable to apply SPRM operation '" |
| + sprm.getOperation() + "': ", exc ); |
| } |
| } |
| } |
| |
| return newProperties; |
| } |
| |
| /** |
| * Performs an operation on a ParagraphProperties object. Used to uncompress |
| * from a papx. |
| * |
| * @param newPAP The ParagraphProperties object to perform the operation on. |
| * @param operand The operand that defines the operation. |
| * @param param The operation's parameter. |
| * @param varParam The operation's variable length parameter. |
| * @param grpprl The original papx. |
| * @param offset The current offset in the papx. |
| * @param spra A part of the sprm that defined this operation. |
| */ |
| static void unCompressPAPOperation (ParagraphProperties newPAP, SprmOperation sprm) |
| { |
| switch (sprm.getOperation()) |
| { |
| case 0: |
| newPAP.setIstd (sprm.getOperand()); |
| break; |
| case 0x1: |
| |
| // Used only for piece table grpprl's not for PAPX |
| // int istdFirst = LittleEndian.getShort (varParam, 2); |
| // int istdLast = LittleEndian.getShort (varParam, 4); |
| // if ((newPAP.getIstd () > istdFirst) || (newPAP.getIstd () <= istdLast)) |
| // { |
| // permuteIstd (newPAP, varParam, opSize); |
| // } |
| break; |
| case 0x2: |
| if (newPAP.getIstd () <= 9 || newPAP.getIstd () >= 1) |
| { |
| byte paramTmp = (byte) sprm.getOperand(); |
| newPAP.setIstd (newPAP.getIstd () + paramTmp); |
| newPAP.setLvl ((byte) (newPAP.getLvl () + paramTmp)); |
| |
| if (((paramTmp >> 7) & 0x01) == 1) |
| { |
| newPAP.setIstd (Math.max (newPAP.getIstd (), 1)); |
| } |
| else |
| { |
| newPAP.setIstd (Math.min (newPAP.getIstd (), 9)); |
| } |
| |
| } |
| break; |
| case 0x3: |
| // Physical justification of the paragraph |
| newPAP.setJc ((byte) sprm.getOperand()); |
| break; |
| case 0x4: |
| newPAP.setFSideBySide (sprm.getOperand() != 0); |
| break; |
| case 0x5: |
| newPAP.setFKeep (sprm.getOperand() != 0); |
| break; |
| case 0x6: |
| newPAP.setFKeepFollow (sprm.getOperand() != 0); |
| break; |
| case 0x7: |
| newPAP.setFPageBreakBefore (sprm.getOperand() != 0); |
| break; |
| case 0x8: |
| newPAP.setBrcl ((byte) sprm.getOperand()); |
| break; |
| case 0x9: |
| newPAP.setBrcp ((byte) sprm.getOperand()); |
| break; |
| case 0xa: |
| newPAP.setIlvl ((byte) sprm.getOperand()); |
| break; |
| case 0xb: |
| /* sprmPIlfo -- 0x460B */ |
| newPAP.setIlfo( sprm.getOperandShortSigned() ); |
| break; |
| case 0xc: |
| newPAP.setFNoLnn (sprm.getOperand() != 0); |
| break; |
| case 0xd: |
| /**handle tabs . variable parameter. seperate processing needed*/ |
| handleTabs(newPAP, sprm); |
| break; |
| case 0xe: |
| newPAP.setDxaRight (sprm.getOperand()); |
| break; |
| case 0xf: |
| newPAP.setDxaLeft (sprm.getOperand()); |
| break; |
| case 0x10: |
| |
| // sprmPNest is only stored in grpprls linked to a piece table. |
| newPAP.setDxaLeft (newPAP.getDxaLeft () + sprm.getOperand()); |
| newPAP.setDxaLeft (Math.max (0, newPAP.getDxaLeft ())); |
| break; |
| case 0x11: |
| newPAP.setDxaLeft1 (sprm.getOperand()); |
| break; |
| case 0x12: |
| newPAP.setLspd(new LineSpacingDescriptor(sprm.getGrpprl(), sprm.getGrpprlOffset())); |
| break; |
| case 0x13: |
| newPAP.setDyaBefore (sprm.getOperand()); |
| break; |
| case 0x14: |
| newPAP.setDyaAfter (sprm.getOperand()); |
| break; |
| case 0x15: |
| // fast saved only |
| //applySprmPChgTabs (newPAP, varParam, opSize); |
| break; |
| case 0x16: |
| // sprmPFInTable -- 0x2416 |
| newPAP.setFInTable( sprm.getOperand() != 0); |
| break; |
| case 0x17: |
| newPAP.setFTtp ( sprm.getOperand() != 0); |
| break; |
| case 0x18: |
| newPAP.setDxaAbs (sprm.getOperand()); |
| break; |
| case 0x19: |
| newPAP.setDyaAbs (sprm.getOperand()); |
| break; |
| case 0x1a: |
| newPAP.setDxaWidth (sprm.getOperand()); |
| break; |
| case 0x1b: |
| byte param = (byte)sprm.getOperand(); |
| /** @todo handle paragraph postioning*/ |
| byte pcVert = (byte) ((param & 0x0c) >> 2); |
| byte pcHorz = (byte) (param & 0x03); |
| if (pcVert != 3) |
| { |
| newPAP.setPcVert (pcVert); |
| } |
| if (pcHorz != 3) |
| { |
| newPAP.setPcHorz (pcHorz); |
| } |
| break; |
| |
| // BrcXXX1 is older Version. Brc is used |
| // case 0x1c: |
| // newPAP.setBrcTop1((short)param); |
| // break; |
| // case 0x1d: |
| // newPAP.setBrcLeft1((short)param); |
| // break; |
| // case 0x1e: |
| // newPAP.setBrcBottom1((short)param); |
| // break; |
| // case 0x1f: |
| // newPAP.setBrcRight1((short)param); |
| // break; |
| // case 0x20: |
| // newPAP.setBrcBetween1((short)param); |
| // break; |
| // case 0x21: |
| // newPAP.setBrcBar1((byte)param); |
| // break; |
| |
| case 0x22: |
| newPAP.setDxaFromText (sprm.getOperand()); |
| break; |
| case 0x23: |
| newPAP.setWr((byte)sprm.getOperand()); |
| break; |
| case 0x24: |
| newPAP.setBrcTop(new BorderCode(sprm.getGrpprl(), sprm.getGrpprlOffset())); |
| break; |
| case 0x25: |
| newPAP.setBrcLeft(new BorderCode(sprm.getGrpprl(), sprm.getGrpprlOffset())); |
| break; |
| case 0x26: |
| newPAP.setBrcBottom (new BorderCode(sprm.getGrpprl(), sprm.getGrpprlOffset())); |
| break; |
| case 0x27: |
| newPAP.setBrcRight (new BorderCode(sprm.getGrpprl(), sprm.getGrpprlOffset())); |
| break; |
| case 0x28: |
| newPAP.setBrcBetween (new BorderCode(sprm.getGrpprl(), sprm.getGrpprlOffset())); |
| break; |
| case 0x29: |
| newPAP.setBrcBar (new BorderCode(sprm.getGrpprl(), sprm.getGrpprlOffset())); |
| break; |
| case 0x2a: |
| newPAP.setFNoAutoHyph (sprm.getOperand() != 0); |
| break; |
| case 0x2b: |
| newPAP.setDyaHeight (sprm.getOperand()); |
| break; |
| case 0x2c: |
| newPAP.setDcs (new DropCapSpecifier((short)sprm.getOperand())); |
| break; |
| case 0x2d: |
| newPAP.setShd( new ShadingDescriptor80( (short) sprm.getOperand() ) |
| .toShadingDescriptor() ); |
| break; |
| case 0x2e: |
| newPAP.setDyaFromText (sprm.getOperand()); |
| break; |
| case 0x2f: |
| newPAP.setDxaFromText (sprm.getOperand()); |
| break; |
| case 0x30: |
| newPAP.setFLocked (sprm.getOperand() != 0); |
| break; |
| case 0x31: |
| newPAP.setFWidowControl (sprm.getOperand() != 0); |
| break; |
| case 0x33: |
| newPAP.setFKinsoku (sprm.getOperand() != 0); |
| break; |
| case 0x34: |
| newPAP.setFWordWrap (sprm.getOperand() != 0); |
| break; |
| case 0x35: |
| newPAP.setFOverflowPunct (sprm.getOperand() != 0); |
| break; |
| case 0x36: |
| newPAP.setFTopLinePunct (sprm.getOperand() != 0); |
| break; |
| case 0x37: |
| newPAP.setFAutoSpaceDE (sprm.getOperand() != 0); |
| break; |
| case 0x38: |
| newPAP.setFAutoSpaceDN (sprm.getOperand() != 0); |
| break; |
| case 0x39: |
| newPAP.setWAlignFont (sprm.getOperand()); |
| break; |
| case 0x3a: |
| newPAP.setFontAlign ((short) sprm.getOperand()); |
| break; |
| case 0x3b: |
| //obsolete |
| break; |
| case 0x3e: |
| { |
| byte[] buf = new byte[sprm.size() - 3]; |
| System.arraycopy(buf, 0, sprm.getGrpprl(), sprm.getGrpprlOffset(), |
| buf.length); |
| newPAP.setAnld(buf); |
| break; |
| } |
| case 0x3f: |
| //don't really need this. spec is confusing regarding this |
| //sprm |
| byte[] varParam = sprm.getGrpprl(); |
| int offset = sprm.getGrpprlOffset(); |
| newPAP.setFPropRMark (varParam[offset] != 0 ); |
| newPAP.setIbstPropRMark (LittleEndian.getShort (varParam, offset + 1)); |
| newPAP.setDttmPropRMark (new DateAndTime(varParam, offset + 3)); |
| break; |
| case 0x40: |
| // This condition commented out, as Word seems to set outline levels even for |
| // paragraph with other styles than Heading 1..9, even though specification |
| // does not say so. See bug 49820 for discussion. |
| //if (newPAP.getIstd () < 1 && newPAP.getIstd () > 9) |
| { |
| newPAP.setLvl ((byte) sprm.getOperand()); |
| } |
| break; |
| case 0x41: |
| // sprmPFBiDi |
| newPAP.setFBiDi(sprm.getOperand() != 0); |
| break; |
| case 0x43: |
| |
| //pap.fNumRMIns |
| newPAP.setFNumRMIns (sprm.getOperand() != 0); |
| break; |
| case 0x44: |
| |
| //undocumented |
| break; |
| case 0x45: |
| if (sprm.getSizeCode() == 6) |
| { |
| byte[] buf = new byte[sprm.size() - 3]; |
| System.arraycopy(buf, 0, sprm.getGrpprl(), sprm.getGrpprlOffset(), buf.length); |
| newPAP.setNumrm (buf); |
| } |
| else |
| { |
| /**@todo handle large PAPX from data stream*/ |
| } |
| break; |
| |
| case 0x47: |
| newPAP.setFUsePgsuSettings (sprm.getOperand() != 0); |
| break; |
| case 0x48: |
| newPAP.setFAdjustRight (sprm.getOperand() != 0); |
| break; |
| case 0x49: |
| // sprmPItap -- 0x6649 |
| newPAP.setItap( sprm.getOperand() ); |
| break; |
| case 0x4a: |
| // sprmPDtap -- 0x664a |
| newPAP.setItap( (byte) ( newPAP.getItap() + sprm.getOperand() ) ); |
| break; |
| case 0x4b: |
| // sprmPFInnerTableCell -- 0x244b |
| newPAP.setFInnerTableCell( sprm.getOperand() != 0); |
| break; |
| case 0x4c: |
| // sprmPFInnerTtp -- 0x244c |
| newPAP.setFTtpEmbedded( sprm.getOperand() != 0); |
| break; |
| case 0x4d: |
| // sprmPShd -- 0xc64d |
| ShadingDescriptor shadingDescriptor = new ShadingDescriptor( |
| sprm.getGrpprl(), 3 ); |
| newPAP.setShading( shadingDescriptor ); |
| break; |
| case 0x5d: |
| // sprmPDxaRight -- 0x845d |
| newPAP.setDxaRight( sprm.getOperand() ); |
| break; |
| case 0x5e: |
| // sprmPDxaLeft -- 0x845e |
| newPAP.setDxaLeft( sprm.getOperand() ); |
| break; |
| case 0x60: |
| // sprmPDxaLeft1 -- 0x8460 |
| newPAP.setDxaLeft1( sprm.getOperand() ); |
| break; |
| case 0x61: |
| // sprmPJc |
| newPAP.setJustificationLogical((byte) sprm.getOperand()); |
| break; |
| case 0x67: |
| // sprmPRsid -- 0x6467 |
| newPAP.setRsid( sprm.getOperand() ); |
| break; |
| default: |
| logger.log( POILogger.DEBUG, "Unknown PAP sprm ignored: " + sprm ); |
| break; |
| } |
| } |
| |
| private static void handleTabs(ParagraphProperties pap, SprmOperation sprm) |
| { |
| byte[] grpprl = sprm.getGrpprl(); |
| int offset = sprm.getGrpprlOffset(); |
| int delSize = grpprl[offset++]; |
| int[] tabPositions = pap.getRgdxaTab(); |
| TabDescriptor[] tabDescriptors = pap.getRgtbd(); |
| |
| Map<Integer, TabDescriptor> tabMap = new HashMap<Integer, TabDescriptor>(); |
| for (int x = 0; x < tabPositions.length; x++) |
| { |
| tabMap.put(Integer.valueOf(tabPositions[x]), tabDescriptors[x]); |
| } |
| |
| for (int x = 0; x < delSize; x++) |
| { |
| tabMap.remove(Integer.valueOf(LittleEndian.getShort(grpprl, offset))); |
| offset += LittleEndian.SHORT_SIZE; |
| } |
| |
| int addSize = grpprl[offset++]; |
| int start = offset; |
| for (int x = 0; x < addSize; x++) |
| { |
| Integer key = Integer.valueOf(LittleEndian.getShort(grpprl, offset)); |
| TabDescriptor val = new TabDescriptor( grpprl, start + ((TabDescriptor.getSize() * addSize) + x) ); |
| tabMap.put(key, val); |
| offset += LittleEndian.SHORT_SIZE; |
| } |
| |
| tabPositions = new int[tabMap.size()]; |
| tabDescriptors = new TabDescriptor[tabPositions.length]; |
| |
| List<Integer> list = new ArrayList<Integer>(tabMap.keySet()); |
| Collections.sort(list); |
| |
| for (int x = 0; x < tabPositions.length; x++) |
| { |
| Integer key = list.get(x); |
| tabPositions[x] = key.intValue(); |
| if (tabMap.containsKey( key )) |
| tabDescriptors[x] = tabMap.get(key); |
| else |
| tabDescriptors[x] = new TabDescriptor(); |
| } |
| |
| pap.setRgdxaTab(tabPositions); |
| pap.setRgtbd(tabDescriptors); |
| } |
| |
| // private static void handleTabsAgain(ParagraphProperties pap, SprmOperation sprm) |
| // { |
| // byte[] grpprl = sprm.getGrpprl(); |
| // int offset = sprm.getGrpprlOffset(); |
| // int delSize = grpprl[offset++]; |
| // int[] tabPositions = pap.getRgdxaTab(); |
| // byte[] tabDescriptors = pap.getRgtbd(); |
| // |
| // HashMap tabMap = new HashMap(); |
| // for (int x = 0; x < tabPositions.length; x++) |
| // { |
| // tabMap.put(Integer.valueOf(tabPositions[x]), Byte.valueOf(tabDescriptors[x])); |
| // } |
| // |
| // for (int x = 0; x < delSize; x++) |
| // { |
| // tabMap.remove(Integer.valueOf(LittleEndian.getInt(grpprl, offset))); |
| // offset += LittleEndian.INT_SIZE;; |
| // } |
| // |
| // int addSize = grpprl[offset++]; |
| // for (int x = 0; x < addSize; x++) |
| // { |
| // Integer key = Integer.valueOf(LittleEndian.getInt(grpprl, offset)); |
| // Byte val = Byte.valueOf(grpprl[(LittleEndian.INT_SIZE * (addSize - x)) + x]); |
| // tabMap.put(key, val); |
| // offset += LittleEndian.INT_SIZE; |
| // } |
| // |
| // tabPositions = new int[tabMap.size()]; |
| // tabDescriptors = new byte[tabPositions.length]; |
| // ArrayList list = new ArrayList(); |
| // |
| // Iterator keyIT = tabMap.keySet().iterator(); |
| // while (keyIT.hasNext()) |
| // { |
| // list.add(keyIT.next()); |
| // } |
| // Collections.sort(list); |
| // |
| // for (int x = 0; x < tabPositions.length; x++) |
| // { |
| // Integer key = ((Integer)list.get(x)); |
| // tabPositions[x] = key.intValue(); |
| // tabDescriptors[x] = ((Byte)tabMap.get(key)).byteValue(); |
| // } |
| // |
| // pap.setRgdxaTab(tabPositions); |
| // pap.setRgtbd(tabDescriptors); |
| // } |
| |
| } |