--- src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java (revision 422731) +++ src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java (working copy) @@ -84,7 +84,7 @@ public static final Type TextCharsAtom = new Type(4000,TextCharsAtom.class); public static final Type StyleTextPropAtom = new Type(4001,StyleTextPropAtom.class); public static final Type BaseTextPropAtom = new Type(4002,null); - public static final Type TxMasterStyleAtom = new Type(4003,null); + public static final Type TxMasterStyleAtom = new Type(4003,TxMasterStyleAtom.class); public static final Type TxCFStyleAtom = new Type(4004,null); public static final Type TxPFStyleAtom = new Type(4005,null); public static final Type TextRulerAtom = new Type(4006,null); --- src/scratchpad/src/org/apache/poi/hslf/record/TxMasterStyleAtom.java (revision 0) +++ src/scratchpad/src/org/apache/poi/hslf/record/TxMasterStyleAtom.java (revision 0) @@ -0,0 +1,213 @@ + +/* ==================================================================== + Copyright 2002-2004 Apache Software Foundation + + Licensed 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.hslf.record; + +import org.apache.poi.util.LittleEndian; + +import java.io.OutputStream; +import java.io.IOException; +import org.apache.poi.hslf.record.StyleTextPropAtom.*; + +/** + * TxMasterStyleAtom atom (4003). + *

+ * Stores default character and paragraph styles. + * The atom instance value is the text type and is encoded like the txstyle field in + * TextHeaderAtom. The text styles are located in the MainMaster container, + * except for the "other" style, which is in the Document.Environment container. + *

+ *

+ * This atom can store up to 5 pairs of paragraph+character styles, + * each pair describes an indent level. The first pair describes + * first-level paragraph with no indentation. + *

+ * + * @author Yegor Kozlov + */ +public class TxMasterStyleAtom extends RecordAtom { + + /** + * Maximum number of indentatio levels allowed in PowerPoint documents + */ + private static final int MAX_INDENT = 5; + + private byte[] _header; + private static long _type = 4003; + private byte[] _data; + + private TextPropCollection[] prstyles; + private TextPropCollection[] chstyles; + + protected TxMasterStyleAtom(byte[] source, int start, int len) { + _header = new byte[8]; + System.arraycopy(source,start,_header,0,8); + + _data = new byte[len-8]; + System.arraycopy(source,start+8,_data,0,_data.length); + + //read available styles + try { + init(); + } catch (Exception e){ + e.printStackTrace(); + } + } + + /** + * We are of type 4003 + * + * @return type of this record + * @see RecordTypes.TxMasterStyleAtom + */ + public long getRecordType() { + return _type; + } + + /** + * Write the contents of the record back, so it can be written + * to disk + */ + public void writeOut(OutputStream out) throws IOException { + // Write out the (new) header + out.write(_header); + + // Write out the record data + out.write(_data); + + } + + /** + * Returns array of character styles defined in this record. + * + * @return character styles defined in this record + */ + public TextPropCollection[] getCharacterStyles(){ + return chstyles; + } + + /** + * Returns array of paragraph styles defined in this record. + * + * @return paragraph styles defined in this record + */ + public TextPropCollection[] getParagraphStyles(){ + return prstyles; + } + + /** + * Return type of the text. + * Must be a constant defined in TextHeaderAtom + * + * @return type of the text + * @see TextHeaderAtom + */ + public int getTextType(){ + //The atom instance value is the text type + return LittleEndian.getShort(_header, 0) >> 4; + } + + /** + * parse the record data and initialize styles + */ + protected void init(){ + //type of the text + int type = getTextType(); + + int head; + int pos = 0; + + //number of indentation levels + short levels = LittleEndian.getShort(_data, 0); + pos += LittleEndian.SHORT_SIZE; + + prstyles = new TextPropCollection[levels]; + chstyles = new TextPropCollection[levels]; + + for(short j = 0; j < levels; j++) { + + if (type >= TextHeaderAtom.CENTRE_BODY_TYPE) { + // Fetch the 2 byte value, that is safe to ignore for some types of text + short val = LittleEndian.getShort(_data, pos); + pos += LittleEndian.SHORT_SIZE; + } + + head = LittleEndian.getInt(_data, pos); + pos += LittleEndian.INT_SIZE; + TextPropCollection prprops = new TextPropCollection(0); + pos += prprops.buildTextPropList( head, getParagraphProps(type, j), _data, pos); + prstyles[j] = prprops; + + head = LittleEndian.getInt(_data, pos); + pos += LittleEndian.INT_SIZE; + TextPropCollection chprops = new TextPropCollection(0); + pos += chprops.buildTextPropList( head, getCharacterProps(type, j), _data, pos); + chstyles[j] = chprops; + } + + } + + /** + * Paragraph properties for the specified text type and indent level + */ + protected TextProp[] getParagraphProps(int type, int level){ + if (level != 0 || type >= MAX_INDENT){ + return StyleTextPropAtom.paragraphTextPropTypes; + } else { + return new TextProp[] { + new BitMaskTextProp(2, 0xF, "paragraph_flags", new String[] { + "bullet", "bullet.hardfont", + "bullet.hardcolor", "bullet.hardsize"} + ), + new TextProp(2, 0x80, "bullet.char"), + new TextProp(2, 0x10, "bullet.font"), + new TextProp(2, 0x40, "bullet.size"), + new TextProp(4, 0x20, "bullet.color"), + new TextProp(2, 0xD00, "alignment"), + new TextProp(2, 0x1000, "linespacing"), + new TextProp(2, 0x2000, "spacebefore"), + new TextProp(2, 0x4000, "spaceafter"), + new TextProp(2, 0x8000, "text.offset"), + new TextProp(2, 0x10000, "bullet.offset"), + new TextProp(2, 0x20000, "defaulttab"), + new TextProp(2, 0x40000, "para_unknown_2"), + new TextProp(2, 0x80000, "para_unknown_3"), + new TextProp(2, 0x100000, "para_unknown_4"), + new TextProp(2, 0x200000, "para_unknown_5") + }; + } + } + + /** + * Character properties for the specified text type and indent level + */ + protected TextProp[] getCharacterProps(int type, int level){ + if (level != 0 || type >= MAX_INDENT){ + return StyleTextPropAtom.characterTextPropTypes; + } else + return new TextProp[] { + new CharFlagsTextProp(), + new TextProp(2, 0x10000, "font.index"), + new TextProp(2, 0x20000, "char_unknown_1"), + new TextProp(4, 0x40000, "char_unknown_2"), + new TextProp(2, 0x80000, "font.size"), + new TextProp(2, 0x100000, "char_unknown_3"), + new TextProp(4, 0x200000, "font.color"), + new TextProp(2, 0x800000, "char_unknown_4") + }; + } +} --- src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java (revision 422731) +++ src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java (working copy) @@ -93,9 +93,9 @@ new TextProp(4, 0x20, "bullet.color"), new TextProp(2, 0x40, "bullet.size"), new TextProp(2, 0x80, "bullet.char"), - new TextProp(2, 0x100, "para_unknown_1"), + new TextProp(2, 0x100, "text.offset"), new TextProp(2, 0x200, "para_unknown_2"), - new TextProp(2, 0x400, "para_unknown_3"), + new TextProp(2, 0x400, "bullet.offset"), new TextProp(2, 0x800, "alignment"), new TextProp(2, 0x1000, "linespacing"), new TextProp(2, 0x2000, "spacebefore"), @@ -348,7 +348,7 @@ * Used to hold the number of characters affected, the list of active * properties, and the random reserved field if required. */ - public class TextPropCollection { + public static class TextPropCollection { private int charactersCovered; private short reservedField; private LinkedList textPropList; @@ -445,7 +445,7 @@ * Create a new collection of text properties (be they paragraph * or character) for a run of text without any */ - private TextPropCollection(int textSize) { + public TextPropCollection(int textSize) { charactersCovered = textSize; reservedField = -1; textPropList = new LinkedList(); @@ -511,7 +511,7 @@ /** * Generate the definition of a given type of text property. */ - private TextProp(int sizeOfDataBlock, int maskInHeader, String propName) { + public TextProp(int sizeOfDataBlock, int maskInHeader, String propName) { this.sizeOfDataBlock = sizeOfDataBlock; this.maskInHeader = maskInHeader; this.propName = propName; @@ -580,7 +580,7 @@ /** Fetch the list of if the sub properties match or not */ public boolean[] getSubPropMatches() { return subPropMatches; } - private BitMaskTextProp(int sizeOfDataBlock, int maskInHeader, String overallName, String[] subPropNames) { + public BitMaskTextProp(int sizeOfDataBlock, int maskInHeader, String overallName, String[] subPropNames) { super(sizeOfDataBlock,maskInHeader,"bitmask"); this.subPropNames = subPropNames; this.propName = overallName; @@ -664,7 +664,7 @@ public static final int ENABLE_NUMBERING_1_IDX = 11; public static final int ENABLE_NUMBERING_2_IDX = 12; - private CharFlagsTextProp() { + public CharFlagsTextProp() { super(2,0xffff, "char_flags", new String[] { "bold", // 0x0001 "italic", // 0x0002 --- src/scratchpad/testcases/org/apache/poi/hslf/record/TestTxMasterStyleAtom.java (revision 0) +++ src/scratchpad/testcases/org/apache/poi/hslf/record/TestTxMasterStyleAtom.java (revision 0) @@ -0,0 +1,233 @@ + +/* ==================================================================== + Copyright 2002-2004 Apache Software Foundation + + Licensed 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.hslf.record; + +import junit.framework.TestCase; +import org.apache.poi.hslf.usermodel.SlideShow; +import org.apache.poi.hslf.record.StyleTextPropAtom.*; + +import java.util.ArrayList; + + +/** + * Test TestTxMasterStyleAtom record. + * Check master style for the empty ppt which is created + * by the default constructor of SlideShow + * + * @author Yegor Kozlov + */ +public class TestTxMasterStyleAtom extends TestCase { + protected SlideShow _ppt; + + public void setUp() throws Exception{ + _ppt = new SlideShow(); + + } + + public void testDefaultStyles() { + TxMasterStyleAtom[] txmaster = getMasterStyles(); + for (int i = 0; i < txmaster.length; i++) { + int txtype = txmaster[i].getTextType(); + switch (txtype){ + case TextHeaderAtom.TITLE_TYPE: + checkTitleType(txmaster[i]); + break; + case TextHeaderAtom.BODY_TYPE: + checkBodyType(txmaster[i]); + break; + case TextHeaderAtom.NOTES_TYPE: + checkNotesType(txmaster[i]); + break; + case TextHeaderAtom.OTHER_TYPE: + checkOtherType(txmaster[i]); + break; + case TextHeaderAtom.CENTRE_BODY_TYPE: + break; + case TextHeaderAtom.CENTER_TITLE_TYPE: + break; + case TextHeaderAtom.HALF_BODY_TYPE: + break; + case TextHeaderAtom.QUARTER_BODY_TYPE: + break; + default: + fail("Unknown text type: " + txtype); + } + + } + } + + + + /** + * Test styles for type=TextHeaderAtom.TITLE_TYPE + */ + private void checkTitleType(TxMasterStyleAtom txmaster){ + TextPropCollection props; + TextProp prop; + + //paragraph styles + props = txmaster.getParagraphStyles()[0]; + + prop = props.findByName("alignment"); + assertEquals(1, prop.getValue()); //title has center alignment + + //character styles + props = txmaster.getCharacterStyles()[0]; + + prop = props.findByName("font.color"); + assertEquals(0x3000000, prop.getValue()); + + prop = props.findByName("font.index"); + assertEquals(0, prop.getValue()); + + prop = props.findByName("font.size"); + assertEquals(49, prop.getValue()); + + } + + /** + * Test styles for type=TextHeaderAtom.BODY_TYPE + */ + private void checkBodyType(TxMasterStyleAtom txmaster){ + TextPropCollection props; + TextProp prop; + + TextPropCollection[] prstyles = txmaster.getParagraphStyles(); + TextPropCollection[] chstyles = txmaster.getCharacterStyles(); + assertEquals("TxMasterStyleAtom for TextHeaderAtom.BODY_TYPE " + + "must contain styles for 5 indentation levels", 5, prstyles.length); + assertEquals("TxMasterStyleAtom for TextHeaderAtom.BODY_TYPE " + + "must contain styles for 5 indentation levels", 5, chstyles.length); + + //paragraph styles + props = prstyles[0]; + + prop = props.findByName("alignment"); + assertEquals(0, prop.getValue()); + + + for (int i = 0; i < prstyles.length; i++) { + assertNotNull("text.offset is null for indentation level " + i, prstyles[i].findByName("text.offset")); + assertNotNull("bullet.offset is null for indentation level " + i, prstyles[i].findByName("bullet.offset")); + } + + //character styles + props = chstyles[0]; + + prop = props.findByName("font.color"); + assertEquals(0x1000000, prop.getValue()); + + prop = props.findByName("font.index"); + assertEquals(0, prop.getValue()); + + prop = props.findByName("font.size"); + assertEquals(36, prop.getValue()); + } + + /** + * Test styles for type=TextHeaderAtom.OTHER_TYPE + */ + private void checkOtherType(TxMasterStyleAtom txmaster){ + TextPropCollection props; + TextProp prop; + + //paragraph styles + props = txmaster.getParagraphStyles()[0]; + + prop = props.findByName("alignment"); + assertEquals(0, prop.getValue()); + + //character styles + props = txmaster.getCharacterStyles()[0]; + + prop = props.findByName("font.color"); + assertEquals(0x1000000, prop.getValue()); + + prop = props.findByName("font.index"); + assertEquals(0, prop.getValue()); + + prop = props.findByName("font.size"); + assertEquals(24, prop.getValue()); + } + + /** + * Test styles for type=TextHeaderAtom.NOTES_TYPE + */ + private void checkNotesType(TxMasterStyleAtom txmaster){ + TextPropCollection props; + TextProp prop; + + //paragraph styles + props = txmaster.getParagraphStyles()[0]; + + prop = props.findByName("alignment"); + assertEquals(0, prop.getValue()); //title has center alignment + + //character styles + props = txmaster.getCharacterStyles()[0]; + + prop = props.findByName("font.color"); + assertEquals(0x1000000, prop.getValue()); + + prop = props.findByName("font.index"); + assertEquals(0, prop.getValue()); + + prop = props.findByName("font.size"); + assertEquals(12, prop.getValue()); + + } + + /** + * Collect all TxMasterStyleAtom records contained in the supplied slide show. + * There must be a TxMasterStyleAtom per each type of text defined in TextHeaderAtom + */ + protected TxMasterStyleAtom[] getMasterStyles(){ + ArrayList lst = new ArrayList(); + + Record[] core = _ppt.getMostRecentCoreRecords(); + for (int i = 0; i < core.length; i++) { + if(core[i].getRecordType() == RecordTypes.MainMaster.typeID){ + Record[] rec = core[i].getChildRecords(); + int cnt = 0; + for (int j = 0; j < rec.length; j++) { + if (rec[j] instanceof TxMasterStyleAtom) { + lst.add(rec[j]); + cnt++; + } + } + assertEquals("MainMaster must contain 7 TxMasterStyleAtoms ", 7, cnt); + } else if(core[i].getRecordType() == RecordTypes.Document.typeID){ + TxMasterStyleAtom txstyle = null; + Document doc = (Document)core[i]; + Record[] rec = doc.getEnvironment().getChildRecords(); + for (int j = 0; j < rec.length; j++) { + if (rec[j] instanceof TxMasterStyleAtom) { + if (txstyle != null) fail("Document.Environment must contain 1 TxMasterStyleAtom"); + txstyle = (TxMasterStyleAtom)rec[j]; + } + } + assertNotNull("TxMasterStyleAtom not found in Document.Environment", txstyle); + + assertEquals("Document.Environment must contain TxMasterStyleAtom with type=TextHeaderAtom.OTHER_TYPE", + TextHeaderAtom.OTHER_TYPE, txstyle.getTextType()); + lst.add(txstyle); + } + } + + return (TxMasterStyleAtom[])lst.toArray(new TxMasterStyleAtom[lst.size()]); + } +}