diff options
author | Andreas Beeker <kiwiwings@apache.org> | 2018-02-11 20:39:18 +0000 |
---|---|---|
committer | Andreas Beeker <kiwiwings@apache.org> | 2018-02-11 20:39:18 +0000 |
commit | 9968e86b14188b43920f0ef28f68559e3dadf7be (patch) | |
tree | 54e5d238df674471154d47f5ed5746bfb92ee782 /src/scratchpad | |
parent | 5f71c8013157ee2754dcc3109b79f9873d14a086 (diff) | |
download | poi-9968e86b14188b43920f0ef28f68559e3dadf7be.tar.gz poi-9968e86b14188b43920f0ef28f68559e3dadf7be.zip |
#62096 - Add support for tabstops
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1823893 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/scratchpad')
14 files changed, 705 insertions, 426 deletions
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStop.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStop.java new file mode 100644 index 0000000000..78cc3fab4d --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStop.java @@ -0,0 +1,116 @@ +/* ==================================================================== + 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.hslf.model.textproperties; + +import org.apache.poi.sl.usermodel.TabStop; +import org.apache.poi.util.Internal; +import org.apache.poi.util.Units; + +@Internal +public class HSLFTabStop implements TabStop, Cloneable { + /** + * A signed integer that specifies an offset, in master units, of the tab stop. + * + * If the TextPFException record that contains this TabStop structure also contains a + * leftMargin, then the value of position is relative to the left margin of the paragraph; + * otherwise, the value is relative to the left side of the paragraph. + * + * If a TextRuler record contains this TabStop structure, the value is relative to the + * left side of the text ruler. + */ + private int position; + + /** + * A enumeration that specifies how text aligns at the tab stop. + */ + private TabStopType type; + + public HSLFTabStop(int position, TabStopType type) { + this.position = position; + this.type = type; + } + + public int getPosition() { + return position; + } + + public void setPosition(final int position) { + this.position = position; + } + + @Override + public double getPositionInPoints() { + return Units.masterToPoints(getPosition()); + } + + @Override + public void setPositionInPoints(final double points) { + setPosition(Units.pointsToMaster(points)); + } + + @Override + public TabStopType getType() { + return type; + } + + @Override + public void setType(TabStopType type) { + this.type = type; + } + + @Override + public HSLFTabStop clone() { + try { + return (HSLFTabStop)super.clone(); + } catch (CloneNotSupportedException e) { + throw new IllegalStateException(e); + } + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + position; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof HSLFTabStop)) { + return false; + } + HSLFTabStop other = (HSLFTabStop) obj; + if (position != other.position) { + return false; + } + if (type != other.type) { + return false; + } + return true; + } + + @Override + public String toString() { + return type + " @ " + position; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStopPropCollection.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStopPropCollection.java new file mode 100644 index 0000000000..7f7f6bd9cf --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStopPropCollection.java @@ -0,0 +1,158 @@ +/* ==================================================================== + 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.hslf.model.textproperties; + +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.sl.usermodel.TabStop.TabStopType; +import org.apache.poi.util.Internal; +import org.apache.poi.util.LittleEndianByteArrayInputStream; +import org.apache.poi.util.LittleEndianConsts; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; +import org.apache.poi.util.LittleEndianOutputStream; + +/** + * Container for tabstop lists + */ +@Internal +public class HSLFTabStopPropCollection extends TextProp { + public static final String NAME = "tabStops"; + + private final List<HSLFTabStop> tabStops = new ArrayList<>(); + + public HSLFTabStopPropCollection() { + super(0, 0x100000, NAME); + } + + public HSLFTabStopPropCollection(final HSLFTabStopPropCollection copy) { + super(0, copy.getMask(), copy.getName()); + for (HSLFTabStop ts : copy.tabStops) { + tabStops.add(ts.clone()); + } + } + + /** + * Parses the tabstops from TxMasterStyle record + * + * @param data the data stream + * @param offset the offset within the data + */ + public void parseProperty(byte data[], int offset) { + tabStops.addAll(readTabStops(new LittleEndianByteArrayInputStream(data, offset))); + } + + public static List<HSLFTabStop> readTabStops(final LittleEndianInput lei) { + final int count = lei.readUShort(); + final List<HSLFTabStop> tabs = new ArrayList<>(count); + for (int i=0; i<count; i++) { + final int position = lei.readShort(); + final TabStopType type = TabStopType.fromNativeId(lei.readShort()); + tabs.add(new HSLFTabStop(position, type)); + } + return tabs; + } + + + public void writeProperty(OutputStream out) { + writeTabStops(new LittleEndianOutputStream(out), tabStops); + } + + public static void writeTabStops(final LittleEndianOutput leo, List<HSLFTabStop> tabStops) { + final int count = tabStops.size(); + leo.writeShort(count); + for (HSLFTabStop ts : tabStops) { + leo.writeShort(ts.getPosition()); + leo.writeShort(ts.getType().nativeId); + } + + } + + @Override + public int getValue() { return tabStops.size(); } + + + @Override + public int getSize() { + return LittleEndianConsts.SHORT_SIZE + tabStops.size()*LittleEndianConsts.INT_SIZE; + } + + public List<HSLFTabStop> getTabStops() { + return tabStops; + } + + public void clearTabs() { + tabStops.clear(); + } + + public void addTabStop(HSLFTabStop ts) { + tabStops.add(ts); + } + + @Override + public HSLFTabStopPropCollection clone() { + return new HSLFTabStopPropCollection(this); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + + ((tabStops == null) ? 0 : tabStops.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof HSLFTabStopPropCollection)) { + return false; + } + HSLFTabStopPropCollection other = (HSLFTabStopPropCollection) obj; + if (!super.equals(other)) { + return false; + } + + return tabStops.equals(other.tabStops); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(super.toString()); + sb.append(" [ "); + boolean isFirst = true; + for (HSLFTabStop tabStop : tabStops) { + if (!isFirst) { + sb.append(", "); + } + sb.append(tabStop.getType()); + sb.append(" @ "); + sb.append(tabStop.getPosition()); + isFirst = false; + } + sb.append(" ]"); + + return sb.toString(); + } + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TabStopPropCollection.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TabStopPropCollection.java deleted file mode 100644 index 1934cf8983..0000000000 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TabStopPropCollection.java +++ /dev/null @@ -1,123 +0,0 @@ -/* ==================================================================== - 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.hslf.model.textproperties; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.poi.util.LittleEndian; -import org.apache.poi.util.LittleEndianConsts; - -/** - * Container for tabstop lists - */ -public class TabStopPropCollection extends TextProp { - public enum TabStopType { - LEFT(0), CENTER(1), RIGHT(2), DECIMAL(3); - private final int val; - TabStopType(int val) { - this.val = val; - } - public static TabStopType fromRecordVal(int val) { - for (TabStopType tst : values()) { - if (tst.val == val) return tst; - } - return LEFT; - } - } - - public static class TabStop { - /** - * If the TextPFException record that contains this TabStop structure also contains a - * leftMargin, then the value of position is relative to the left margin of the paragraph; - * otherwise, the value is relative to the left side of the paragraph. - * - * If a TextRuler record contains this TabStop structure, the value is relative to the - * left side of the text ruler. - */ - private int position; - - /** - * A enumeration that specifies how text aligns at the tab stop. - */ - private TabStopType type; - - public TabStop(int position, TabStopType type) { - this.position = position; - this.type = type; - } - - public int getPosition() { - return position; - } - - public void setPosition(int position) { - this.position = position; - } - - public TabStopType getType() { - return type; - } - - public void setType(TabStopType type) { - this.type = type; - } - } - - private List<TabStop> tabStops = new ArrayList<>(); - - public TabStopPropCollection() { - super(0, 0x100000, "tabStops"); - } - - /** - * Parses the tabstops from TxMasterStyle record - * - * @param data the data stream - * @param offset the offset within the data - */ - public void parseProperty(byte data[], int offset) { - int count = LittleEndian.getUShort(data, offset); - int off = offset + LittleEndianConsts.SHORT_SIZE; - for (int i=0; i<count; i++) { - int position = LittleEndian.getShort(data, off); - off += LittleEndianConsts.SHORT_SIZE; - int recVal = LittleEndian.getShort(data, off); - TabStopType type = TabStopType.fromRecordVal(recVal); - off += LittleEndianConsts.SHORT_SIZE; - tabStops.add(new TabStop(position, type)); - - } - } - - @Override - public int getSize() { - return LittleEndianConsts.SHORT_SIZE + tabStops.size()*LittleEndianConsts.INT_SIZE; - } - - @Override - public TabStopPropCollection clone() { - TabStopPropCollection other = (TabStopPropCollection)super.clone(); - other.tabStops = new ArrayList<>(); - for (TabStop ts : tabStops) { - TabStop tso = new TabStop(ts.getPosition(), ts.getType()); - other.tabStops.add(tso); - } - return other; - } -} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextProp.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextProp.java index 9333968f07..dfb1a1e544 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextProp.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextProp.java @@ -96,7 +96,7 @@ public class TextProp implements Cloneable { try { return (TextProp)super.clone(); } catch(CloneNotSupportedException e) { - throw new InternalError(e.getMessage()); + throw new IllegalStateException(e); } } @@ -145,11 +145,11 @@ public class TextProp implements Cloneable { @Override public String toString() { int len; - switch (sizeOfDataBlock) { + switch (getSize()) { case 1: len = 4; break; case 2: len = 6; break; default: len = 10; break; } - return String.format(Locale.ROOT, "%s = %d (%0#"+len+"X mask / %d bytes)", propName, dataValue, maskInHeader, sizeOfDataBlock); + return String.format(Locale.ROOT, "%s = %d (%0#"+len+"X mask / %d bytes)", getName(), getValue(), getMask(), getSize()); } }
\ No newline at end of file diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java index 93a036fd07..976e8550f5 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java @@ -58,7 +58,7 @@ public class TextPropCollection { // 0x200 - Undefined and MUST be ignored new TextProp(2, 0x400, "bullet.offset"), // indent new TextProp(2, 0x8000, "defaultTabSize"), - new TabStopPropCollection(), // tabstops size is variable! + new HSLFTabStopPropCollection(), // tabstops size is variable! new FontAlignmentProp(), new WrapFlagsTextProp(), new TextProp(2, 0x200000, "textDirection"), @@ -130,12 +130,14 @@ public class TextPropCollection { } /** Fetch the TextProp with this name, or null if it isn't present */ - public final TextProp findByName(String textPropName) { - return textProps.get(textPropName); + @SuppressWarnings("unchecked") + public final <T extends TextProp> T findByName(String textPropName) { + return (T)textProps.get(textPropName); } - public final TextProp removeByName(String name) { - return textProps.remove(name); + @SuppressWarnings("unchecked") + public final <T extends TextProp> T removeByName(String name) { + return (T)textProps.remove(name); } public final TextPropType getTextPropType() { @@ -153,10 +155,11 @@ public class TextPropCollection { * @param name the property name * @return if found, the property template to copy from */ - private TextProp validatePropName(String name) { + @SuppressWarnings("unchecked") + private <T extends TextProp> T validatePropName(final String name) { for (TextProp tp : getPotentialProperties()) { if (tp.getName().equals(name)) { - return tp; + return (T)tp; } } String errStr = @@ -166,13 +169,14 @@ public class TextPropCollection { } /** Add the TextProp with this name to the list */ - public final TextProp addWithName(String name) { + @SuppressWarnings("unchecked") + public final <T extends TextProp> T addWithName(final String name) { // Find the base TextProp to base on - TextProp existing = findByName(name); + T existing = findByName(name); if (existing != null) return existing; // Add a copy of this property - TextProp textProp = validatePropName(name).clone(); + T textProp = (T)validatePropName(name).clone(); textProps.put(name,textProp); return textProp; } @@ -218,11 +222,13 @@ public class TextPropCollection { // Bingo, data contains this property TextProp prop = tp.clone(); int val = 0; - if (prop.getSize() == 2) { + if (prop instanceof HSLFTabStopPropCollection) { + ((HSLFTabStopPropCollection)prop).parseProperty(data, dataOffset+bytesPassed); + } else if (prop.getSize() == 2) { val = LittleEndian.getShort(data,dataOffset+bytesPassed); } else if(prop.getSize() == 4) { val = LittleEndian.getInt(data,dataOffset+bytesPassed); - } else if (prop.getSize() == 0 && !(prop instanceof TabStopPropCollection)) { + } else if (prop.getSize() == 0) { //remember "special" bits. maskSpecial |= tp.getMask(); continue; @@ -230,9 +236,7 @@ public class TextPropCollection { if (prop instanceof BitMaskTextProp) { ((BitMaskTextProp)prop).setValueWithMask(val, containsField); - } else if (prop instanceof TabStopPropCollection) { - ((TabStopPropCollection)prop).parseProperty(data, dataOffset+bytesPassed); - } else { + } else if (!(prop instanceof HSLFTabStopPropCollection)) { prop.setValue(val); } bytesPassed += prop.getSize(); @@ -311,6 +315,8 @@ public class TextPropCollection { StyleTextPropAtom.writeLittleEndian((short)val,o); } else if (textProp.getSize() == 4) { StyleTextPropAtom.writeLittleEndian(val,o); + } else if (textProp instanceof HSLFTabStopPropCollection) { + ((HSLFTabStopPropCollection)textProp).writeProperty(o); } } } @@ -359,8 +365,9 @@ public class TextPropCollection { out.append(" indent level: "+getIndentLevel()+"\n"); } for(TextProp p : getTextPropList()) { - out.append(" " + p.getName() + " = " + p.getValue() ); - out.append(" (0x" + HexDump.toHex(p.getValue()) + ")\n"); + out.append(" "); + out.append(p.toString()); + out.append("\n"); if (p instanceof BitMaskTextProp) { BitMaskTextProp bm = (BitMaskTextProp)p; int i = 0; diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java index 53666d9184..e0c45a5daf 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java @@ -17,11 +17,21 @@ package org.apache.poi.hslf.record; +import static org.apache.poi.util.BitFieldFactory.getInstance; + +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; -import org.apache.poi.util.IOUtils; +import org.apache.poi.hslf.model.textproperties.HSLFTabStop; +import org.apache.poi.hslf.model.textproperties.HSLFTabStopPropCollection; +import org.apache.poi.util.BitField; import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianByteArrayInputStream; +import org.apache.poi.util.LittleEndianOutputStream; import org.apache.poi.util.POILogger; /** @@ -31,33 +41,38 @@ public final class TextRulerAtom extends RecordAtom { //arbitrarily selected; may need to increase private static final int MAX_RECORD_LENGTH = 100_000; + + private static final BitField DEFAULT_TAB_SIZE = getInstance(0x0001); + private static final BitField C_LEVELS = getInstance(0x0002); + private static final BitField TAB_STOPS = getInstance(0x0004); + private static final BitField[] LEFT_MARGIN = { + getInstance(0x0008), getInstance(0x0010), getInstance(0x0020), + getInstance(0x0040), getInstance(0x0080), + }; + private static final BitField[] INDENT = { + getInstance(0x0100), getInstance(0x0200), getInstance(0x0400), + getInstance(0x0800), getInstance(0x1000), + }; /** * Record header. */ - private byte[] _header; - - /** - * Record data. - */ - private byte[] _data; + private final byte[] _header = new byte[8]; //ruler internals - private int defaultTabSize; - private int numLevels; - private int[] tabStops; - private int[] bulletOffsets = new int[5]; - private int[] textOffsets = new int[5]; + private Integer defaultTabSize; + private Integer numLevels; + private final List<HSLFTabStop> tabStops = new ArrayList<>(); + //bullet.offset + private final Integer[] leftMargin = new Integer[5]; + //text.offset + private final Integer[] indent = new Integer[5]; /** * Constructs a new empty ruler atom. */ public TextRulerAtom() { - _header = new byte[8]; - _data = new byte[0]; - LittleEndian.putShort(_header, 2, (short)getRecordType()); - LittleEndian.putInt(_header, 4, _data.length); } /** @@ -68,18 +83,17 @@ public final class TextRulerAtom extends RecordAtom { * @param start the start offset into the byte array. * @param len the length of the slice in the byte array. */ - protected TextRulerAtom(byte[] source, int start, int len) { - // Get the header. - _header = new byte[8]; - System.arraycopy(source,start,_header,0,8); - - // Get the record data. - _data = IOUtils.safelyAllocate(len-8, MAX_RECORD_LENGTH); - System.arraycopy(source,start+8,_data,0,len-8); + protected TextRulerAtom(final byte[] source, final int start, final int len) { + final LittleEndianByteArrayInputStream leis = new LittleEndianByteArrayInputStream(source, start, Math.min(len, MAX_RECORD_LENGTH)); + try { - read(); - } catch (Exception e){ + // Get the header. + leis.read(_header); + + // Get the record data. + read(leis); + } catch (IOException e){ logger.log(POILogger.ERROR, "Failed to parse TextRulerAtom: " + e.getMessage()); } } @@ -89,6 +103,7 @@ public final class TextRulerAtom extends RecordAtom { * * @return the record type. */ + @Override public long getRecordType() { return RecordTypes.TextRulerAtom.typeID; } @@ -100,109 +115,110 @@ public final class TextRulerAtom extends RecordAtom { * @param out the output stream to write to. * @throws java.io.IOException if an error occurs. */ - public void writeOut(OutputStream out) throws IOException { + @Override + public void writeOut(final OutputStream out) throws IOException { + final ByteArrayOutputStream bos = new ByteArrayOutputStream(200); + final LittleEndianOutputStream lbos = new LittleEndianOutputStream(bos); + int mask = 0; + mask |= writeIf(lbos, numLevels, C_LEVELS); + mask |= writeIf(lbos, defaultTabSize, DEFAULT_TAB_SIZE); + mask |= writeIf(lbos, tabStops, TAB_STOPS); + for (int i=0; i<5; i++) { + mask |= writeIf(lbos, leftMargin[i], LEFT_MARGIN[i]); + mask |= writeIf(lbos, indent[i], INDENT[i]); + } + LittleEndian.putInt(_header, 4, bos.size()+4); out.write(_header); - out.write(_data); + LittleEndian.putUShort(mask, out); + LittleEndian.putUShort(0, out); + bos.writeTo(out); } + private static int writeIf(final LittleEndianOutputStream lbos, Integer value, BitField bit) { + boolean isSet = false; + if (value != null) { + lbos.writeShort(value); + isSet = true; + } + return bit.setBoolean(0, isSet); + } + + private static int writeIf(final LittleEndianOutputStream lbos, List<HSLFTabStop> value, BitField bit) { + boolean isSet = false; + if (value != null && !value.isEmpty()) { + HSLFTabStopPropCollection.writeTabStops(lbos, value); + isSet = true; + } + return bit.setBoolean(0, isSet); + } + /** * Read the record bytes and initialize the internal variables */ - private void read(){ - int pos = 0; - short mask = LittleEndian.getShort(_data); pos += 4; - short val; - int[] bits = {1, 0, 2, 3, 8, 4, 9, 5, 10, 6, 11, 7, 12}; - for (int i = 0; i < bits.length; i++) { - if((mask & 1 << bits[i]) != 0){ - switch (bits[i]){ - case 0: - //defaultTabSize - defaultTabSize = LittleEndian.getShort(_data, pos); pos += 2; - break; - case 1: - //numLevels - numLevels = LittleEndian.getShort(_data, pos); pos += 2; - break; - case 2: - //tabStops - val = LittleEndian.getShort(_data, pos); pos += 2; - tabStops = new int[val*2]; - for (int j = 0; j < tabStops.length; j++) { - tabStops[j] = LittleEndian.getUShort(_data, pos); pos += 2; - } - break; - case 3: - case 4: - case 5: - case 6: - case 7: - //bullet.offset - val = LittleEndian.getShort(_data, pos); pos += 2; - bulletOffsets[bits[i]-3] = val; - break; - case 8: - case 9: - case 10: - case 11: - case 12: - //text.offset - val = LittleEndian.getShort(_data, pos); pos += 2; - textOffsets[bits[i]-8] = val; - break; - default: - break; - } - } + private void read(final LittleEndianByteArrayInputStream leis) { + final int mask = leis.readInt(); + numLevels = readIf(leis, mask, C_LEVELS); + defaultTabSize = readIf(leis, mask, DEFAULT_TAB_SIZE); + if (TAB_STOPS.isSet(mask)) { + tabStops.addAll(HSLFTabStopPropCollection.readTabStops(leis)); + } + for (int i=0; i<5; i++) { + leftMargin[i] = readIf(leis, mask, LEFT_MARGIN[i]); + indent[i] = readIf(leis, mask, INDENT[i]); } } + private static Integer readIf(final LittleEndianByteArrayInputStream leis, final int mask, final BitField bit) { + return (bit.isSet(mask)) ? (int)leis.readShort() : null; + } + /** * Default distance between tab stops, in master coordinates (576 dpi). */ public int getDefaultTabSize(){ - return defaultTabSize; + return defaultTabSize == null ? 0 : defaultTabSize; } /** * Number of indent levels (maximum 5). */ public int getNumberOfLevels(){ - return numLevels; + return numLevels == null ? 0 : numLevels; } /** * Default distance between tab stops, in master coordinates (576 dpi). */ - public int[] getTabStops(){ + public List<HSLFTabStop> getTabStops(){ return tabStops; } /** * Paragraph's distance from shape's left margin, in master coordinates (576 dpi). */ - public int[] getTextOffsets(){ - return textOffsets; + public Integer[] getTextOffsets(){ + return indent; } /** * First line of paragraph's distance from shape's left margin, in master coordinates (576 dpi). */ - public int[] getBulletOffsets(){ - return bulletOffsets; + public Integer[] getBulletOffsets(){ + return leftMargin; } public static TextRulerAtom getParagraphInstance(){ - byte[] data = new byte[] { - 0x00, 0x00, (byte)0xA6, 0x0F, 0x0A, 0x00, 0x00, 0x00, - 0x10, 0x03, 0x00, 0x00, (byte)0xF9, 0x00, 0x41, 0x01, 0x41, 0x01 - }; - return new TextRulerAtom(data, 0, data.length); + final TextRulerAtom tra = new TextRulerAtom(); + tra.indent[0] = 249; + tra.indent[1] = tra.leftMargin[1] = 321; + return tra; } - public void setParagraphIndent(short tetxOffset, short bulletOffset){ - LittleEndian.putShort(_data, 4, tetxOffset); - LittleEndian.putShort(_data, 6, bulletOffset); - LittleEndian.putShort(_data, 8, bulletOffset); + public void setParagraphIndent(short leftMargin, short indent) { + Arrays.fill(this.leftMargin, null); + Arrays.fill(this.indent, null); + this.leftMargin[0] = (int)leftMargin; + this.indent[0] = (int)indent; + this.indent[1] = (int)indent; } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFMasterSheet.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFMasterSheet.java index d73374620d..526a3ceed2 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFMasterSheet.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFMasterSheet.java @@ -17,16 +17,13 @@ package org.apache.poi.hslf.usermodel; +import org.apache.poi.hslf.model.textproperties.TextPropCollection; import org.apache.poi.hslf.record.SheetContainer; -import org.apache.poi.hslf.model.textproperties.TextProp; +import org.apache.poi.hslf.record.TextHeaderAtom; import org.apache.poi.sl.usermodel.MasterSheet; /** * The superclass of all master sheets - Slide masters, Notes masters, etc. - * - * For now it's empty. When we understand more about masters in ppt we will add the common functionality here. - * - * @author Yegor Kozlov */ public abstract class HSLFMasterSheet extends HSLFSheet implements MasterSheet<HSLFShape,HSLFTextParagraph> { public HSLFMasterSheet(SheetContainer container, int sheetNo){ @@ -34,10 +31,19 @@ public abstract class HSLFMasterSheet extends HSLFSheet implements MasterSheet<H } /** - * Pickup a style attribute from the master. - * This is the "workhorse" which returns the default style attrubutes. + * Find the master collection for the given txtype/level/name. + * This is the "workhorse" which returns the default style attributes. + * If {@code name = "*"} return the current collection, otherwise if the name is not found + * in the current selection of txtype/level/name, first try lower levels then try parent types, + * if it wasn't found there return {@code null}. + * + * @param txtype the {@link TextHeaderAtom} type + * @param level the indent level of the paragraph, if the level is not defined for the found + * collection, the highest existing level will be used + * @param name the property name, + * @param isCharacter if {@code true} use character styles, otherwise use paragraph styles */ - public abstract TextProp getStyleAttribute(int txtype, int level, String name, boolean isCharacter) ; + public abstract TextPropCollection getPropCollection(int txtype, int level, String name, boolean isCharacter); /** diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java index f6cd41f41d..c7535f709a 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java @@ -74,42 +74,50 @@ public final class HSLFSlideMaster extends HSLFMasterSheet { } /** - * Pickup a style attribute from the master. + * Find the master collection for the given txtype/level/name. * This is the "workhorse" which returns the default style attributes. + * If {@code name = "*"} return the current collection, otherwise if the name is not found + * in the current selection of txtype/level/name, first try lower levels then try parent types, + * if it wasn't found there return {@code null}. + * + * @param txtype the {@link TextHeaderAtom} type + * @param level the indent level of the paragraph, if the level is not defined for the found + * collection, the highest existing level will be used + * @param name the property name, + * @param isCharacter if {@code true} use character styles, otherwise use paragraph styles */ @Override - public TextProp getStyleAttribute(int txtype, int level, String name, boolean isCharacter) { - if (_txmaster.length <= txtype) { - return null; - } - TxMasterStyleAtom t = _txmaster[txtype]; - List<TextPropCollection> styles = isCharacter ? t.getCharacterStyles() : t.getParagraphStyles(); - - TextProp prop = null; - for (int i = Math.min(level, styles.size()-1); prop == null && i >= 0; i--) { - prop = styles.get(i).findByName(name); + public TextPropCollection getPropCollection(final int txtype, final int level, final String name, final boolean isCharacter) { + if (txtype < _txmaster.length) { + final TxMasterStyleAtom t = _txmaster[txtype]; + final List<TextPropCollection> styles = isCharacter ? t.getCharacterStyles() : t.getParagraphStyles(); + // TODO: what is the reaction for readOnly=false and styles.isEmpty()? + final int minLevel = Math.min(level, styles.size()-1); + if ("*".equals(name)) { + return styles.get(minLevel); + } + + for (int i=minLevel; i >= 0; i--) { + final TextPropCollection col = styles.get(i); + final TextProp tp = col.findByName(name); + if (tp != null) { + return col; + } + } } - if (prop != null) { - return prop; - } - switch (txtype) { case TextHeaderAtom.CENTRE_BODY_TYPE: case TextHeaderAtom.HALF_BODY_TYPE: case TextHeaderAtom.QUARTER_BODY_TYPE: - txtype = TextHeaderAtom.BODY_TYPE; - break; + return getPropCollection(TextHeaderAtom.BODY_TYPE, level, name, isCharacter); case TextHeaderAtom.CENTER_TITLE_TYPE: - txtype = TextHeaderAtom.TITLE_TYPE; - break; + return getPropCollection(TextHeaderAtom.TITLE_TYPE, level, name, isCharacter); default: return null; } - - return getStyleAttribute(txtype, level, name, isCharacter); } - + /** * Assign SlideShow for this slide master. */ @@ -132,7 +140,7 @@ public final class HSLFSlideMaster extends HSLFMasterSheet { _txmaster[txType] = txrec[i]; } } - + for (List<HSLFTextParagraph> paras : getTextParagraphs()) { for (HSLFTextParagraph htp : paras) { int txType = htp.getRunType(); @@ -148,11 +156,6 @@ public final class HSLFSlideMaster extends HSLFMasterSheet { charStyles.size() <= level || paragraphStyles.size() <= level) { throw new HSLFException("Master styles not initialized"); } - - htp.setMasterStyleReference(paragraphStyles.get(level)); - for (HSLFTextRun htr : htp.getTextRuns()) { - htr.setMasterStyleReference(charStyles.get(level)); - } } } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java index 695500eb01..4e68c63fed 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java @@ -24,12 +24,16 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; import org.apache.poi.common.usermodel.fonts.FontGroup; import org.apache.poi.common.usermodel.fonts.FontInfo; import org.apache.poi.hslf.exceptions.HSLFException; import org.apache.poi.hslf.model.textproperties.BitMaskTextProp; import org.apache.poi.hslf.model.textproperties.FontAlignmentProp; +import org.apache.poi.hslf.model.textproperties.HSLFTabStop; +import org.apache.poi.hslf.model.textproperties.HSLFTabStopPropCollection; import org.apache.poi.hslf.model.textproperties.IndentProp; import org.apache.poi.hslf.model.textproperties.ParagraphFlagsTextProp; import org.apache.poi.hslf.model.textproperties.TextAlignmentProp; @@ -37,34 +41,15 @@ import org.apache.poi.hslf.model.textproperties.TextPFException9; import org.apache.poi.hslf.model.textproperties.TextProp; import org.apache.poi.hslf.model.textproperties.TextPropCollection; import org.apache.poi.hslf.model.textproperties.TextPropCollection.TextPropType; -import org.apache.poi.hslf.record.ColorSchemeAtom; -import org.apache.poi.hslf.record.EscherTextboxWrapper; -import org.apache.poi.hslf.record.InteractiveInfo; -import org.apache.poi.hslf.record.MasterTextPropAtom; -import org.apache.poi.hslf.record.OutlineTextRefAtom; -import org.apache.poi.hslf.record.PPDrawing; -import org.apache.poi.hslf.record.Record; -import org.apache.poi.hslf.record.RecordContainer; -import org.apache.poi.hslf.record.RecordTypes; -import org.apache.poi.hslf.record.RoundTripHFPlaceholder12; -import org.apache.poi.hslf.record.SlideListWithText; -import org.apache.poi.hslf.record.SlidePersistAtom; -import org.apache.poi.hslf.record.StyleTextProp9Atom; -import org.apache.poi.hslf.record.StyleTextPropAtom; -import org.apache.poi.hslf.record.TextBytesAtom; -import org.apache.poi.hslf.record.TextCharsAtom; -import org.apache.poi.hslf.record.TextHeaderAtom; -import org.apache.poi.hslf.record.TextRulerAtom; -import org.apache.poi.hslf.record.TextSpecInfoAtom; -import org.apache.poi.hslf.record.TxInteractiveInfoAtom; +import org.apache.poi.hslf.record.*; import org.apache.poi.sl.draw.DrawPaint; import org.apache.poi.sl.usermodel.AutoNumberingScheme; -import org.apache.poi.sl.usermodel.MasterSheet; import org.apache.poi.sl.usermodel.PaintStyle; import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; import org.apache.poi.sl.usermodel.Placeholder; +import org.apache.poi.sl.usermodel.TabStop; +import org.apache.poi.sl.usermodel.TabStop.TabStopType; import org.apache.poi.sl.usermodel.TextParagraph; -import org.apache.poi.util.Internal; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; import org.apache.poi.util.StringUtil; @@ -93,7 +78,6 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText private TextBytesAtom _byteAtom; private TextCharsAtom _charAtom; private TextPropCollection _paragraphStyle = new TextPropCollection(1, TextPropType.paragraph); - private TextPropCollection _masterStyle; protected TextRulerAtom _ruler; protected final List<HSLFTextRun> _runs = new ArrayList<>(); @@ -107,6 +91,33 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText private final List<HSLFTextParagraph> parentList; + private class HSLFTabStopDecorator implements TabStop { + final HSLFTabStop tabStop; + + HSLFTabStopDecorator(final HSLFTabStop tabStop) { + this.tabStop = tabStop; + } + + public double getPositionInPoints() { + return tabStop.getPositionInPoints(); + } + + public void setPositionInPoints(double position) { + tabStop.setPositionInPoints(position); + setDirty(); + } + + public TabStopType getType() { + return tabStop.getType(); + } + + public void setType(TabStopType type) { + tabStop.setType(type); + setDirty(); + } + } + + /** * Constructs a Text Run from a Unicode text block. * Either a {@link TextCharsAtom} or a {@link TextBytesAtom} needs to be provided. @@ -161,18 +172,6 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText } /** - * Setting a master style reference - * - * @param paragraphStyle the master style reference - * - * @since POI 3.14-Beta1 - */ - @Internal - /* package */ void setMasterStyleReference(TextPropCollection masterStyle) { - _masterStyle = masterStyle; - } - - /** * Supply the Sheet we belong to, which might have an assigned SlideShow * Also passes it on to our child RichTextRuns */ @@ -341,7 +340,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText @Override public Double getLeftMargin() { - TextProp tp = getPropVal(_paragraphStyle, _masterStyle, "text.offset"); + TextProp tp = getPropVal(_paragraphStyle, "text.offset"); return (tp == null) ? null : Units.masterToPoints(tp.getValue()); } @@ -364,7 +363,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText @Override public Double getIndent() { - TextProp tp = getPropVal(_paragraphStyle, _masterStyle, "bullet.offset"); + TextProp tp = getPropVal(_paragraphStyle, "bullet.offset"); return (tp == null) ? null : Units.masterToPoints(tp.getValue()); } @@ -388,7 +387,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText if (fontInfo == null) { fontInfo = HSLFFontInfoPredefined.ARIAL; } - + return fontInfo.getTypeface(); } @@ -422,7 +421,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText @Override public TextAlign getTextAlign() { - TextProp tp = getPropVal(_paragraphStyle, _masterStyle, "alignment"); + TextProp tp = getPropVal(_paragraphStyle, "alignment"); if (tp == null) { return null; } @@ -440,7 +439,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText @Override public FontAlign getFontAlign() { - TextProp tp = getPropVal(_paragraphStyle, _masterStyle, FontAlignmentProp.NAME); + TextProp tp = getPropVal(_paragraphStyle, FontAlignmentProp.NAME); if (tp == null) { return null; } @@ -606,7 +605,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText * Returns the bullet character */ public Character getBulletChar() { - TextProp tp = getPropVal(_paragraphStyle, _masterStyle, "bullet.char"); + TextProp tp = getPropVal(_paragraphStyle, "bullet.char"); return (tp == null) ? null : (char)tp.getValue(); } @@ -637,7 +636,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText * Returns the bullet color */ public Color getBulletColor() { - TextProp tp = getPropVal(_paragraphStyle, _masterStyle, "bullet.color"); + TextProp tp = getPropVal(_paragraphStyle, "bullet.color"); boolean hasColor = getFlag(ParagraphFlagsTextProp.BULLET_HARDCOLOR_IDX); if (tp == null || !hasColor) { // if bullet color is undefined, return color of first run @@ -661,7 +660,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText */ public void setBulletFont(String typeface) { if (typeface == null) { - setPropVal(_paragraphStyle, _masterStyle, "bullet.font", null); + setPropVal(_paragraphStyle, "bullet.font", null); setFlag(ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, false); return; } @@ -677,7 +676,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText * Returns the bullet font */ public String getBulletFont() { - TextProp tp = getPropVal(_paragraphStyle, _masterStyle, "bullet.font"); + TextProp tp = getPropVal(_paragraphStyle, "bullet.font"); boolean hasFont = getFlag(ParagraphFlagsTextProp.BULLET_HARDFONT_IDX); if (tp == null || !hasFont) { return getDefaultFontFamily(); @@ -723,8 +722,64 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText return null; } + + @Override + public List<? extends TabStop> getTabStops() { + final List<HSLFTabStop> tabStops; + final TextRulerAtom textRuler; + if (getSheet() instanceof HSLFSlideMaster) { + final HSLFTabStopPropCollection tpc = getMasterPropVal(_paragraphStyle, HSLFTabStopPropCollection.NAME); + if (tpc == null) { + return null; + } + tabStops = tpc.getTabStops(); + textRuler = null; + } else { + textRuler = (TextRulerAtom)_headerAtom.getParentRecord().findFirstOfType(RecordTypes.TextRulerAtom.typeID); + if (textRuler == null) { + return null; + } + tabStops = textRuler.getTabStops(); + } + + return tabStops.stream().map((tabStop) -> new HSLFTabStopDecorator(tabStop)).collect(Collectors.toList()); + } + + @Override + public void addTabStops(final double positionInPoints, final TabStopType tabStopType) { + final HSLFTabStop ts = new HSLFTabStop(0, tabStopType); + ts.setPositionInPoints(positionInPoints); + + if (getSheet() instanceof HSLFSlideMaster) { + final Consumer<HSLFTabStopPropCollection> con = (tp) -> tp.addTabStop(ts); + setPropValInner(_paragraphStyle, HSLFTabStopPropCollection.NAME, con); + } else { + final RecordContainer cont = _headerAtom.getParentRecord(); + TextRulerAtom textRuler = (TextRulerAtom)cont.findFirstOfType(RecordTypes.TextRulerAtom.typeID); + if (textRuler == null) { + textRuler = TextRulerAtom.getParagraphInstance(); + cont.appendChildRecord(textRuler); + } + textRuler.getTabStops().add(ts); + } + } + + @Override + public void clearTabStops() { + if (getSheet() instanceof HSLFSlideMaster) { + setPropValInner(_paragraphStyle, HSLFTabStopPropCollection.NAME, null); + } else { + final RecordContainer cont = _headerAtom.getParentRecord(); + final TextRulerAtom textRuler = (TextRulerAtom)cont.findFirstOfType(RecordTypes.TextRulerAtom.typeID); + if (textRuler == null) { + return; + } + textRuler.getTabStops().clear(); + } + } + private Double getPctOrPoints(String propName) { - TextProp tp = getPropVal(_paragraphStyle, _masterStyle, propName); + TextProp tp = getPropVal(_paragraphStyle, propName); if (tp == null) { return null; } @@ -741,7 +796,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText } private boolean getFlag(int index) { - BitMaskTextProp tp = (BitMaskTextProp)getPropVal(_paragraphStyle, _masterStyle, ParagraphFlagsTextProp.NAME); + BitMaskTextProp tp = getPropVal(_paragraphStyle, ParagraphFlagsTextProp.NAME); return (tp == null) ? false : tp.getSubValue(index); } @@ -759,56 +814,54 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText * The propName can be a comma-separated list, in case multiple equivalent values * are queried */ - protected TextProp getPropVal(TextPropCollection props, TextPropCollection masterProps, String propName) { + protected <T extends TextProp> T getPropVal(TextPropCollection props, String propName) { String propNames[] = propName.split(","); for (String pn : propNames) { - TextProp prop = props.findByName(pn); + T prop = props.findByName(pn); if (isValidProp(prop)) { return prop; } } - return getMasterPropVal(props, masterProps, propName); + return getMasterPropVal(props, propName); } - private TextProp getMasterPropVal(TextPropCollection props, TextPropCollection masterProps, String propName) { + private <T extends TextProp> T getMasterPropVal(final TextPropCollection props, final String propName) { boolean isChar = props.getTextPropType() == TextPropType.character; // check if we can delegate to master for the property if (!isChar) { - BitMaskTextProp maskProp = (BitMaskTextProp) props.findByName(ParagraphFlagsTextProp.NAME); + BitMaskTextProp maskProp = props.findByName(ParagraphFlagsTextProp.NAME); boolean hardAttribute = (maskProp != null && maskProp.getValue() == 0); if (hardAttribute) { return null; } } - String propNames[] = propName.split(","); - if (masterProps == null) { - HSLFSheet sheet = getSheet(); - int txtype = getRunType(); - HSLFMasterSheet master = sheet.getMasterSheet(); + final String propNames[] = propName.split(","); + final HSLFSheet sheet = getSheet(); + final int txtype = getRunType(); + final HSLFMasterSheet master; + if (sheet instanceof HSLFMasterSheet) { + master = (HSLFMasterSheet)sheet; + } else { + master = sheet.getMasterSheet(); if (master == null) { logger.log(POILogger.WARN, "MasterSheet is not available"); return null; } + } - for (String pn : propNames) { - TextProp prop = master.getStyleAttribute(txtype, getIndentLevel(), pn, isChar); - if (isValidProp(prop)) { - return prop; - } - } - } else { - for (String pn : propNames) { - TextProp prop = masterProps.findByName(pn); + for (String pn : propNames) { + TextPropCollection masterProps = master.getPropCollection(txtype, getIndentLevel(), pn, isChar); + if (masterProps != null) { + T prop = masterProps.findByName(pn); if (isValidProp(prop)) { return prop; } } } - return null; } @@ -826,22 +879,33 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText * @param name the name of the TextProp to fetch/add * @param val the value, null if unset */ - protected void setPropVal(TextPropCollection props, TextPropCollection masterProps, String name, Integer val) { - TextPropCollection pc = props; - if (getSheet() instanceof MasterSheet && masterProps != null) { - pc = masterProps; + protected void setPropVal(final TextPropCollection props, final String name, final Integer val) { + setPropValInner(props, name, val == null ? null : tp -> tp.setValue(val)); + } + + private void setPropValInner(final TextPropCollection props, final String name, Consumer<? extends TextProp> handler) { + final boolean isChar = props.getTextPropType() == TextPropType.character; + + final TextPropCollection pc; + if (_sheet instanceof HSLFMasterSheet) { + pc = ((HSLFMasterSheet)_sheet).getPropCollection(getRunType(), getIndentLevel(), "*", isChar); + if (pc == null) { + throw new HSLFException("Master text property collection can't be determined."); + } + } else { + pc = props; } - if (val == null) { + if (handler == null) { pc.removeByName(name); - return; + } else { + // Fetch / Add the TextProp + handler.accept(pc.addWithName(name)); } - - // Fetch / Add the TextProp - TextProp tp = pc.addWithName(name); - tp.setValue(val); + setDirty(); } + /** * Check and add linebreaks to text runs leading other paragraphs * @@ -1595,7 +1659,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText * @param val The value to set for the TextProp */ public void setParagraphTextPropVal(String propName, Integer val) { - setPropVal(_paragraphStyle, _masterStyle, propName, val); + setPropVal(_paragraphStyle, propName, val); setDirty(); } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java index c33272e05b..a444a5b2b6 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java @@ -36,7 +36,6 @@ import org.apache.poi.sl.usermodel.Placeholder; import org.apache.poi.sl.usermodel.TextParagraph; import org.apache.poi.sl.usermodel.TextRun; import org.apache.poi.sl.usermodel.TextShape; -import org.apache.poi.util.Internal; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; @@ -61,8 +60,6 @@ public final class HSLFTextRun implements TextRun { */ private TextPropCollection characterStyle = new TextPropCollection(1, TextPropType.character); - private TextPropCollection masterStyle; - /** * Create a new wrapper around a rich text string * @param parentParagraph the parent paragraph @@ -80,19 +77,6 @@ public final class HSLFTextRun implements TextRun { this.characterStyle.updateTextSize(_runText.length()); } - /** - * Setting a master style reference - * - * @param characterStyle the master style reference - * - * @since POI 3.14-Beta1 - */ - @Internal - /* package */ void setMasterStyleReference(TextPropCollection masterStyle) { - this.masterStyle = masterStyle; - } - - /** * Supply the SlideShow we belong to */ @@ -149,28 +133,34 @@ public final class HSLFTextRun implements TextRun { } protected boolean getFlag(int index) { - if (characterStyle == null) { - return false; - } - - BitMaskTextProp prop = (BitMaskTextProp)characterStyle.findByName(CharFlagsTextProp.NAME); + BitMaskTextProp prop = (characterStyle == null) ? null : characterStyle.findByName(CharFlagsTextProp.NAME); if (prop == null || !prop.getSubPropMatches()[index]) { - int txtype = parentParagraph.getRunType(); - HSLFSheet sheet = parentParagraph.getSheet(); - if (sheet != null) { - HSLFMasterSheet master = sheet.getMasterSheet(); - if (master != null){ - prop = (BitMaskTextProp)master.getStyleAttribute(txtype, parentParagraph.getIndentLevel(), CharFlagsTextProp.NAME, true); - } - } else { - logger.log(POILogger.WARN, "MasterSheet is not available"); - } + prop = getMasterProp(CharFlagsTextProp.NAME); } return prop == null ? false : prop.getSubValue(index); } + private <T extends TextProp> T getMasterProp(final String name) { + final int txtype = parentParagraph.getRunType(); + final HSLFSheet sheet = parentParagraph.getSheet(); + if (sheet == null) { + logger.log(POILogger.ERROR, "Sheet is not available"); + return null; + } + + final HSLFMasterSheet master = sheet.getMasterSheet(); + if (master == null) { + logger.log(POILogger.WARN, "MasterSheet is not available"); + return null; + } + + final TextPropCollection col = master.getPropCollection(txtype, parentParagraph.getIndentLevel(), name, true); + return (col == null) ? null : col.findByName(name); + } + + /** * Set the value of the given flag in the CharFlagsTextProp, adding * it if required. @@ -189,8 +179,7 @@ public final class HSLFTextRun implements TextRun { * @param val The value to set for the TextProp */ public void setCharTextPropVal(String propName, Integer val) { - getTextParagraph().setPropVal(characterStyle, masterStyle, propName, val); - getTextParagraph().setDirty(); + getTextParagraph().setPropVal(characterStyle, propName, val); } @@ -270,7 +259,7 @@ public final class HSLFTextRun implements TextRun { * @return the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript */ public int getSuperscript() { - TextProp tp = getTextParagraph().getPropVal(characterStyle, masterStyle, "superscript"); + TextProp tp = getTextParagraph().getPropVal(characterStyle, "superscript"); return tp == null ? 0 : tp.getValue(); } @@ -285,7 +274,7 @@ public final class HSLFTextRun implements TextRun { @Override public Double getFontSize() { - TextProp tp = getTextParagraph().getPropVal(characterStyle, masterStyle, "font.size"); + TextProp tp = getTextParagraph().getPropVal(characterStyle, "font.size"); return tp == null ? null : (double)tp.getValue(); } @@ -300,7 +289,7 @@ public final class HSLFTextRun implements TextRun { * Gets the font index */ public int getFontIndex() { - TextProp tp = getTextParagraph().getPropVal(characterStyle, masterStyle, "font.index"); + TextProp tp = getTextParagraph().getPropVal(characterStyle, "font.index"); return tp == null ? -1 : tp.getValue(); } @@ -401,7 +390,7 @@ public final class HSLFTextRun implements TextRun { break; } - TextProp tp = getTextParagraph().getPropVal(characterStyle, masterStyle, propName); + TextProp tp = getTextParagraph().getPropVal(characterStyle, propName); return (tp != null) ? slideShow.getFont(tp.getValue()) : null; } @@ -410,7 +399,7 @@ public final class HSLFTextRun implements TextRun { */ @Override public SolidPaint getFontColor() { - TextProp tp = getTextParagraph().getPropVal(characterStyle, masterStyle, "font.color"); + TextProp tp = getTextParagraph().getPropVal(characterStyle, "font.color"); if (tp == null) { return null; } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTitleMaster.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTitleMaster.java index b8e3948ccc..59b0e3f287 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTitleMaster.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTitleMaster.java @@ -20,7 +20,7 @@ package org.apache.poi.hslf.usermodel; import java.util.ArrayList; import java.util.List; -import org.apache.poi.hslf.model.textproperties.TextProp; +import org.apache.poi.hslf.model.textproperties.TextPropCollection; import org.apache.poi.hslf.record.SlideAtom; /** @@ -55,11 +55,11 @@ public final class HSLFTitleMaster extends HSLFMasterSheet { * Delegate the call to the underlying slide master. */ @Override - public TextProp getStyleAttribute(int txtype, int level, String name, boolean isCharacter) { - HSLFMasterSheet master = getMasterSheet(); - return (master == null) ? null : master.getStyleAttribute(txtype, level, name, isCharacter); + public TextPropCollection getPropCollection(final int txtype, final int level, final String name, final boolean isCharacter) { + final HSLFMasterSheet master = getMasterSheet(); + return (master == null) ? null : master.getPropCollection(txtype, level, name, isCharacter); } - + /** * Returns the slide master for this title master. */ diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java index 80772d3a1c..db56e0e775 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java @@ -17,6 +17,10 @@ package org.apache.poi.hslf.model; +import static org.apache.poi.hslf.record.TextHeaderAtom.BODY_TYPE; +import static org.apache.poi.hslf.record.TextHeaderAtom.CENTER_TITLE_TYPE; +import static org.apache.poi.hslf.record.TextHeaderAtom.CENTRE_BODY_TYPE; +import static org.apache.poi.hslf.record.TextHeaderAtom.TITLE_TYPE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -27,6 +31,7 @@ import java.util.List; import org.apache.poi.POIDataSamples; import org.apache.poi.hslf.model.textproperties.CharFlagsTextProp; +import org.apache.poi.hslf.model.textproperties.TextProp; import org.apache.poi.hslf.record.Environment; import org.apache.poi.hslf.record.TextHeaderAtom; import org.apache.poi.hslf.usermodel.HSLFMasterSheet; @@ -51,62 +56,70 @@ public final class TestSlideMaster { */ @Test public void testSlideMaster() throws IOException { - HSLFSlideShow ppt = new HSLFSlideShow(_slTests.openResourceAsStream("slide_master.ppt")); + final HSLFSlideShow ppt = new HSLFSlideShow(_slTests.openResourceAsStream("slide_master.ppt")); - Environment env = ppt.getDocumentRecord().getEnvironment(); + final Environment env = ppt.getDocumentRecord().getEnvironment(); - List<HSLFSlideMaster> master = ppt.getSlideMasters(); - assertEquals(2, master.size()); + assertEquals(2, ppt.getSlideMasters().size()); //character attributes - assertEquals(40, master.get(0).getStyleAttribute(TextHeaderAtom.TITLE_TYPE, 0, "font.size", true).getValue()); - assertEquals(48, master.get(1).getStyleAttribute(TextHeaderAtom.TITLE_TYPE, 0, "font.size", true).getValue()); + assertEquals(40, getMasterVal(ppt, 0, TITLE_TYPE, "font.size", true)); + assertEquals(48, getMasterVal(ppt, 1, TITLE_TYPE, "font.size", true)); - int font1 = master.get(0).getStyleAttribute(TextHeaderAtom.TITLE_TYPE, 0, "font.index", true).getValue(); - int font2 = master.get(1).getStyleAttribute(TextHeaderAtom.TITLE_TYPE, 0, "font.index", true).getValue(); + int font1 = getMasterVal(ppt, 0, TITLE_TYPE, "font.index", true); + int font2 = getMasterVal(ppt, 1, TITLE_TYPE, "font.index", true); assertEquals("Arial", env.getFontCollection().getFontInfo(font1).getTypeface()); assertEquals("Georgia", env.getFontCollection().getFontInfo(font2).getTypeface()); - CharFlagsTextProp prop1 = (CharFlagsTextProp)master.get(0).getStyleAttribute(TextHeaderAtom.TITLE_TYPE, 0, "char_flags", true); + CharFlagsTextProp prop1 = getMasterProp(ppt, 0, TITLE_TYPE, "char_flags", true); assertEquals(false, prop1.getSubValue(CharFlagsTextProp.BOLD_IDX)); assertEquals(false, prop1.getSubValue(CharFlagsTextProp.ITALIC_IDX)); assertEquals(true, prop1.getSubValue(CharFlagsTextProp.UNDERLINE_IDX)); - CharFlagsTextProp prop2 = (CharFlagsTextProp)master.get(1).getStyleAttribute(TextHeaderAtom.TITLE_TYPE, 0, "char_flags", true); + CharFlagsTextProp prop2 = getMasterProp(ppt, 1, TITLE_TYPE, "char_flags", true); assertEquals(false, prop2.getSubValue(CharFlagsTextProp.BOLD_IDX)); assertEquals(true, prop2.getSubValue(CharFlagsTextProp.ITALIC_IDX)); assertEquals(false, prop2.getSubValue(CharFlagsTextProp.UNDERLINE_IDX)); //now paragraph attributes - assertEquals(0x266B, master.get(0).getStyleAttribute(TextHeaderAtom.BODY_TYPE, 0, "bullet.char", false).getValue()); - assertEquals(0x2022, master.get(1).getStyleAttribute(TextHeaderAtom.BODY_TYPE, 0, "bullet.char", false).getValue()); + assertEquals(0x266B, getMasterVal(ppt, 0, BODY_TYPE, "bullet.char", false)); + assertEquals(0x2022, getMasterVal(ppt, 1, BODY_TYPE, "bullet.char", false)); - int b1 = master.get(0).getStyleAttribute(TextHeaderAtom.BODY_TYPE, 0, "bullet.font", false).getValue(); - int b2 = master.get(1).getStyleAttribute(TextHeaderAtom.BODY_TYPE, 0, "bullet.font", false).getValue(); + int b1 = getMasterVal(ppt, 0, BODY_TYPE, "bullet.font", false); + int b2 = getMasterVal(ppt, 1, BODY_TYPE, "bullet.font", false); assertEquals("Arial", env.getFontCollection().getFontInfo(b1).getTypeface()); assertEquals("Georgia", env.getFontCollection().getFontInfo(b2).getTypeface()); ppt.close(); } + @SuppressWarnings("unchecked") + private static <T extends TextProp> T getMasterProp(HSLFSlideShow ppt, int masterIdx, int txtype, String propName, boolean isCharacter) { + return (T)ppt.getSlideMasters().get(masterIdx).getPropCollection(txtype, 0, propName, isCharacter).findByName(propName); + } + + private static int getMasterVal(HSLFSlideShow ppt, int masterIdx, int txtype, String propName, boolean isCharacter) { + return getMasterProp(ppt, masterIdx, txtype, propName, isCharacter).getValue(); + } + + /** * Test we can read default text attributes for a title master sheet */ @Test public void testTitleMasterTextAttributes() throws IOException { HSLFSlideShow ppt = new HSLFSlideShow(_slTests.openResourceAsStream("slide_master.ppt")); - List<HSLFTitleMaster> master = ppt.getTitleMasters(); - assertEquals(1, master.size()); + assertEquals(1, ppt.getTitleMasters().size()); - assertEquals(32, master.get(0).getStyleAttribute(TextHeaderAtom.CENTER_TITLE_TYPE, 0, "font.size", true).getValue()); - CharFlagsTextProp prop1 = (CharFlagsTextProp)master.get(0).getStyleAttribute(TextHeaderAtom.CENTER_TITLE_TYPE, 0, "char_flags", true); - assertEquals(true, prop1.getSubValue(CharFlagsTextProp.BOLD_IDX)); + assertEquals(40, getMasterVal(ppt, 0, CENTER_TITLE_TYPE, "font.size", true)); + CharFlagsTextProp prop1 = getMasterProp(ppt, 0, CENTER_TITLE_TYPE, "char_flags", true); + assertEquals(false, prop1.getSubValue(CharFlagsTextProp.BOLD_IDX)); assertEquals(false, prop1.getSubValue(CharFlagsTextProp.ITALIC_IDX)); assertEquals(true, prop1.getSubValue(CharFlagsTextProp.UNDERLINE_IDX)); - assertEquals(20, master.get(0).getStyleAttribute(TextHeaderAtom.CENTRE_BODY_TYPE, 0, "font.size", true).getValue()); - CharFlagsTextProp prop2 = (CharFlagsTextProp)master.get(0).getStyleAttribute(TextHeaderAtom.CENTRE_BODY_TYPE, 0, "char_flags", true); - assertEquals(true, prop2.getSubValue(CharFlagsTextProp.BOLD_IDX)); + assertEquals(32, getMasterVal(ppt, 0, CENTRE_BODY_TYPE, "font.size", true)); + CharFlagsTextProp prop2 = getMasterProp(ppt, 0, CENTRE_BODY_TYPE, "char_flags", true); + assertEquals(false, prop2.getSubValue(CharFlagsTextProp.BOLD_IDX)); assertEquals(false, prop2.getSubValue(CharFlagsTextProp.ITALIC_IDX)); assertEquals(false, prop2.getSubValue(CharFlagsTextProp.UNDERLINE_IDX)); diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java index 475918cd1c..eee4ffe88c 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java @@ -18,17 +18,17 @@ package org.apache.poi.hslf.record; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import java.io.ByteArrayOutputStream; +import java.util.List; -import junit.framework.TestCase; +import org.apache.poi.hslf.model.textproperties.HSLFTabStop; +import org.junit.Test; -/** - * Tests TextRulerAtom - * - * @author Yegor Kozlov - */ -public final class TestTextRulerAtom extends TestCase { +public final class TestTextRulerAtom { //from a real file private final byte[] data_1 = new byte[] { @@ -40,25 +40,27 @@ public final class TestTextRulerAtom extends TestCase { private final byte[] data_2 = new byte[] { 0x00, 0x00, (byte)0xA6, 0x0F, 0x0A, 0x00, 0x00, 0x00, - 0x10, 0x03, 0x00, 0x00, (byte)0xF9, 0x00, 0x41, 0x01, 0x41, 0x01 + 0x08, 0x03, 0x00, 0x00, (byte)0xF9, 0x00, 0x41, 0x01, 0x41, 0x01 }; + @Test public void testReadRuler() { TextRulerAtom ruler = new TextRulerAtom(data_1, 0, data_1.length); assertEquals(ruler.getNumberOfLevels(), 0); assertEquals(ruler.getDefaultTabSize(), 0); - int[] tabStops = ruler.getTabStops(); - assertNull(tabStops); + List<HSLFTabStop> tabStops = ruler.getTabStops(); + assertNotNull(tabStops); - int[] textOffsets = ruler.getTextOffsets(); - assertArrayEquals(new int[]{226, 451, 903, 1129, 1526}, textOffsets); + Integer[] textOffsets = ruler.getTextOffsets(); + assertArrayEquals(new Integer[]{226, 451, 903, 1129, 1526}, textOffsets); - int[] bulletOffsets = ruler.getBulletOffsets(); - assertArrayEquals(new int[]{117, 345, 794, 1016, 1526}, bulletOffsets); + Integer[] bulletOffsets = ruler.getBulletOffsets(); + assertArrayEquals(new Integer[]{117, 345, 794, 1016, 1526}, bulletOffsets); } + @Test public void testWriteRuler() throws Exception { TextRulerAtom ruler = new TextRulerAtom(data_1, 0, data_1.length); ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -68,6 +70,7 @@ public final class TestTextRulerAtom extends TestCase { assertArrayEquals(result, data_1); } + @Test public void testRead2() throws Exception { TextRulerAtom ruler = TextRulerAtom.getParagraphInstance(); ruler.setParagraphIndent((short)249, (short)321); @@ -75,6 +78,6 @@ public final class TestTextRulerAtom extends TestCase { ruler.writeOut(out); byte[] result = out.toByteArray(); - assertArrayEquals(result, data_2); + assertArrayEquals(data_2, result); } } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestHSLFSlideShow.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestHSLFSlideShow.java index 7f5385e352..7c2f7fe473 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestHSLFSlideShow.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestHSLFSlideShow.java @@ -17,8 +17,14 @@ package org.apache.poi.hslf.usermodel; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import org.apache.poi.sl.usermodel.BaseTestSlideShow; +import org.apache.poi.sl.usermodel.SlideShow; import org.junit.Test; public class TestHSLFSlideShow extends BaseTestSlideShow { @@ -32,4 +38,25 @@ public class TestHSLFSlideShow extends BaseTestSlideShow { public void dummy() { assertNotNull(createSlideShow()); } + + public SlideShow<?, ?> reopen(SlideShow<?, ?> show) { + return reopen((HSLFSlideShow)show); + } + + public static HSLFSlideShow reopen(HSLFSlideShow show) { + try { + BufAccessBAOS bos = new BufAccessBAOS(); + show.write(bos); + return new HSLFSlideShow(new ByteArrayInputStream(bos.getBuf())); + } catch (IOException e) { + fail(e.getMessage()); + return null; + } + } + + private static class BufAccessBAOS extends ByteArrayOutputStream { + public byte[] getBuf() { + return buf; + } + } } |