Browse Source

#62096 - Add support for tabstops

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1823893 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_4_0_0_FINAL
Andreas Beeker 6 years ago
parent
commit
9968e86b14
22 changed files with 1029 additions and 465 deletions
  1. 8
    1
      src/java/org/apache/poi/sl/usermodel/MasterSheet.java
  2. 66
    0
      src/java/org/apache/poi/sl/usermodel/TabStop.java
  3. 30
    0
      src/java/org/apache/poi/sl/usermodel/TextParagraph.java
  4. 4
    0
      src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java
  5. 60
    0
      src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTabStop.java
  6. 66
    38
      src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java
  7. 25
    0
      src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXMLSlideShow.java
  8. 116
    0
      src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStop.java
  9. 158
    0
      src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStopPropCollection.java
  10. 0
    123
      src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TabStopPropCollection.java
  11. 3
    3
      src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextProp.java
  12. 24
    17
      src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java
  13. 106
    90
      src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java
  14. 14
    8
      src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFMasterSheet.java
  15. 31
    28
      src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java
  16. 141
    77
      src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java
  17. 27
    38
      src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java
  18. 5
    5
      src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTitleMaster.java
  19. 35
    22
      src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java
  20. 18
    15
      src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java
  21. 27
    0
      src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestHSLFSlideShow.java
  22. 65
    0
      src/testcases/org/apache/poi/sl/usermodel/BaseTestSlideShow.java

+ 8
- 1
src/java/org/apache/poi/sl/usermodel/MasterSheet.java View File

@@ -21,5 +21,12 @@ public interface MasterSheet<
S extends Shape<S,P>,
P extends TextParagraph<S,P,? extends TextRun>
> extends Sheet<S,P> {

/**
* Return the placeholder shape for the specified type
*
* @return the shape or {@code null} if it is not defined in this mastersheet
*
* @since POI 4.0.0
*/
SimpleShape<S,P> getPlaceholder(Placeholder type);
}

+ 66
- 0
src/java/org/apache/poi/sl/usermodel/TabStop.java View File

@@ -0,0 +1,66 @@
/* ====================================================================
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.sl.usermodel;

public interface TabStop {

enum TabStopType {
LEFT(0,1), CENTER(1,2), RIGHT(2,3), DECIMAL(3,4);
public final int nativeId;
public final int ooxmlId;
TabStopType(int nativeId, int ooxmlId) {
this.nativeId = nativeId;
this.ooxmlId = ooxmlId;
}
public static TabStopType fromNativeId(final int nativeId) {
for (TabStopType tst : values()) {
if (tst.nativeId == nativeId) {
return tst;
}
}
return null;
}
public static TabStopType fromOoxmlId(final int ooxmlId) {
for (TabStopType tst : values()) {
if (tst.ooxmlId == ooxmlId) {
return tst;
}
}
return null;
}
}

/**
* Gets the position in points relative to the left side of the paragraph.
*
* @return position in points
*/
double getPositionInPoints();

/**
* Sets the position in points relative to the left side of the paragraph
*
* @param position position in points
*/
void setPositionInPoints(double position);

TabStopType getType();

void setType(TabStopType type);
}

+ 30
- 0
src/java/org/apache/poi/sl/usermodel/TextParagraph.java View File

@@ -374,4 +374,34 @@ public interface TextParagraph<
* @since POI 3.15-beta2
*/
boolean isHeaderOrFooter();
/**
* Get the {@link TabStop TabStops} - the list can't be and it's entries shouldn't be modified.
* Opposed to other properties, this method is not cascading to the master sheet,
* if the property is not defined on the normal slide level, i.e. the tabstops on
* different levels aren't merged.
*
* @return the tabstop collection or {@code null} if no tabstops are defined
*
* @since POI 4.0.0
*/
List<? extends TabStop> getTabStops();

/**
* Set the {@link TabStop} collection
*
* @param tabStops the {@link TabStop} collection
*
* @since POI 4.0.0
*/
void addTabStops(double positionInPoints, TabStop.TabStopType tabStopType);

/**
* Removes the tabstops of this paragraphs.
* This doesn't affect inherited tabstops, e.g. inherited by the slide master
*
* @since POI 4.0.0
*/
void clearTabStops();
}

+ 4
- 0
src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java View File

@@ -472,6 +472,10 @@ implements XSLFShapeContainer, Sheet<XSLFShape,XSLFTextParagraph> {
return null;
}

public XSLFSimpleShape getPlaceholder(Placeholder ph) {
return getPlaceholderByType(ph.ooxmlId);
}

XSLFSimpleShape getPlaceholder(CTPlaceholder ph) {
XSLFSimpleShape shape = null;
if(ph.isSetIdx()) {

+ 60
- 0
src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTabStop.java View File

@@ -0,0 +1,60 @@
/* ====================================================================
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.xslf.usermodel;

import org.apache.poi.sl.usermodel.TabStop;
import org.apache.poi.util.Units;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextTabStop;
import org.openxmlformats.schemas.drawingml.x2006.main.STTextTabAlignType;

public class XSLFTabStop implements TabStop {

final CTTextTabStop tabStop;
XSLFTabStop(CTTextTabStop tabStop) {
this.tabStop = tabStop;
}
/** position in EMUs */
public int getPosition() {
return tabStop.getPos();
}

/** position in EMUs */
public void setPosition(final int position) {
tabStop.setPos(position);
}

@Override
public double getPositionInPoints() {
return Units.toPoints(getPosition());
}

@Override
public void setPositionInPoints(final double points) {
setPosition(Units.toEMU(points));
}
public TabStopType getType() {
return TabStopType.fromOoxmlId(tabStop.getAlgn().intValue());
}

public void setType(final TabStopType tabStopType) {
tabStop.setAlgn(STTextTabAlignType.Enum.forInt(tabStopType.ooxmlId) );
}
}

+ 66
- 38
src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java View File

@@ -25,6 +25,7 @@ import org.apache.poi.sl.draw.DrawPaint;
import org.apache.poi.sl.usermodel.AutoNumberingScheme;
import org.apache.poi.sl.usermodel.PaintStyle;
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
import org.apache.poi.sl.usermodel.TabStop.TabStopType;
import org.apache.poi.sl.usermodel.TextParagraph;
import org.apache.poi.util.Beta;
import org.apache.poi.util.Internal;
@@ -32,26 +33,7 @@ import org.apache.poi.util.Units;
import org.apache.poi.xslf.model.ParagraphPropertyFetcher;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.drawingml.x2006.main.CTColor;
import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextAutonumberBullet;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBulletSizePercent;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBulletSizePoint;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharBullet;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextField;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextLineBreak;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextNormalAutofit;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextSpacing;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextTabStop;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextTabStopList;
import org.openxmlformats.schemas.drawingml.x2006.main.STTextAlignType;
import org.openxmlformats.schemas.drawingml.x2006.main.STTextAutonumberScheme;
import org.openxmlformats.schemas.drawingml.x2006.main.STTextFontAlignType;
import org.openxmlformats.schemas.drawingml.x2006.main.*;
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType;

@@ -800,25 +782,27 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr

private <T> boolean fetchParagraphProperty(ParagraphPropertyFetcher<T> visitor){
boolean ok = false;
XSLFTextShape shape = getParentShape();
XSLFSheet sheet = shape.getSheet();
final XSLFTextShape shape = getParentShape();
final XSLFSheet sheet = shape.getSheet();
if(_p.isSetPPr()) ok = visitor.fetch(_p.getPPr());
if (ok) return true;

ok = shape.fetchShapeProperty(visitor);
if (ok) return true;
CTPlaceholder ph = shape.getCTPlaceholder();
if(ph == null){
// if it is a plain text box then take defaults from presentation.xml
@SuppressWarnings("resource")
XMLSlideShow ppt = sheet.getSlideShow();
CTTextParagraphProperties themeProps = ppt.getDefaultParagraphStyle(getIndentLevel());
if (themeProps != null) ok = visitor.fetch(themeProps);
if (!(sheet instanceof XSLFSlideMaster)) {
if(_p.isSetPPr()) ok = visitor.fetch(_p.getPPr());
if (ok) return true;
ok = shape.fetchShapeProperty(visitor);
if (ok) return true;
CTPlaceholder ph = shape.getCTPlaceholder();
if(ph == null){
// if it is a plain text box then take defaults from presentation.xml
@SuppressWarnings("resource")
XMLSlideShow ppt = sheet.getSlideShow();
CTTextParagraphProperties themeProps = ppt.getDefaultParagraphStyle(getIndentLevel());
if (themeProps != null) ok = visitor.fetch(themeProps);
}
if (ok) return true;
}
if (ok) return true;

// defaults for placeholders are defined in the slide master
CTTextParagraphProperties defaultProps = getDefaultMasterStyle();
@@ -1011,7 +995,51 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr
}
}
}

@Override
public List<XSLFTabStop> getTabStops() {
ParagraphPropertyFetcher<List<XSLFTabStop>> fetcher = new ParagraphPropertyFetcher<List<XSLFTabStop>>(getIndentLevel()){
public boolean fetch(CTTextParagraphProperties props) {
if (props.isSetTabLst()) {
final List<XSLFTabStop> list = new ArrayList<>();
for (final CTTextTabStop ta : props.getTabLst().getTabArray()) {
list.add(new XSLFTabStop(ta));
}
setValue(list);
return true;
}
return false;
}
};
fetchParagraphProperty(fetcher);
return fetcher.getValue();
}

@Override
public void addTabStops(double positionInPoints, TabStopType tabStopType) {
final XSLFSheet sheet = getParentShape().getSheet();
final CTTextParagraphProperties tpp;
if (sheet instanceof XSLFSlideMaster) {
tpp = getDefaultMasterStyle();
} else {
final CTTextParagraph xo = getXmlObject();
tpp = (xo.isSetPPr()) ? xo.getPPr() : xo.addNewPPr();
}
final CTTextTabStopList stl = (tpp.isSetTabLst()) ? tpp.getTabLst() : tpp.addNewTabLst();
XSLFTabStop tab = new XSLFTabStop(stl.addNewTab());
tab.setPositionInPoints(positionInPoints);
tab.setType(tabStopType);
}

@Override
public void clearTabStops() {
final XSLFSheet sheet = getParentShape().getSheet();
CTTextParagraphProperties tpp = (sheet instanceof XSLFSlideMaster) ? getDefaultMasterStyle() : getXmlObject().getPPr();
if (tpp != null && tpp.isSetTabLst()) {
tpp.unsetTabLst();
}
}

/**
* Helper method for appending text and keeping paragraph and character properties.
* The character properties are moved to the end paragraph marker

+ 25
- 0
src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXMLSlideShow.java View File

@@ -20,12 +20,16 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.sl.usermodel.BaseTestSlideShow;
import org.apache.poi.sl.usermodel.SlideShow;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -179,4 +183,25 @@ public class TestXMLSlideShow extends BaseTestSlideShow {
xmlComments.close();
xml.close();
}
public SlideShow<?, ?> reopen(SlideShow<?, ?> show) {
return reopen((XMLSlideShow)show);
}

public static XMLSlideShow reopen(XMLSlideShow show) {
try {
BufAccessBAOS bos = new BufAccessBAOS();
show.write(bos);
return new XMLSlideShow(new ByteArrayInputStream(bos.getBuf()));
} catch (IOException e) {
fail(e.getMessage());
return null;
}
}

private static class BufAccessBAOS extends ByteArrayOutputStream {
public byte[] getBuf() {
return buf;
}
}
}

+ 116
- 0
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStop.java View File

@@ -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;
}
}

+ 158
- 0
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStopPropCollection.java View File

@@ -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();
}
}

+ 0
- 123
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TabStopPropCollection.java View File

@@ -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;
}
}

+ 3
- 3
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextProp.java View File

@@ -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());
}
}

+ 24
- 17
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java View File

@@ -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;

+ 106
- 90
src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java View File

@@ -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;
}
}

+ 14
- 8
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFMasterSheet.java View File

@@ -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);


/**

+ 31
- 28
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java View File

@@ -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));
}
}
}
}

+ 141
- 77
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java View File

@@ -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.
@@ -160,18 +171,6 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
_paragraphStyle.copy(paragraphStyle);
}

/**
* 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();
}


+ 27
- 38
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java View File

@@ -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;
}

+ 5
- 5
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTitleMaster.java View File

@@ -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.
*/

+ 35
- 22
src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java View File

@@ -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));


+ 18
- 15
src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java View File

@@ -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);
}
}

+ 27
- 0
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestHSLFSlideShow.java View File

@@ -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;
}
}
}

+ 65
- 0
src/testcases/org/apache/poi/sl/usermodel/BaseTestSlideShow.java View File

@@ -21,18 +21,24 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;

import java.awt.Color;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.apache.poi.POIDataSamples;
import org.apache.poi.sl.usermodel.PictureData.PictureType;
import org.apache.poi.sl.usermodel.TabStop.TabStopType;
import org.junit.Test;

public abstract class BaseTestSlideShow {
protected static final POIDataSamples slTests = POIDataSamples.getSlideShowInstance();
public abstract SlideShow<?, ?> createSlideShow();

public abstract SlideShow<?, ?> reopen(SlideShow<?, ?> show);
@Test
public void addPicture_File() throws IOException {
@@ -92,4 +98,63 @@ public abstract class BaseTestSlideShow {
show.close();
}
@Test
public void addTabStops() throws IOException {
try (final SlideShow<?,?> show1 = createSlideShow()) {
// first set the TabStops in the Master sheet
final MasterSheet<?, ?> master1 = show1.getSlideMasters().get(0);
final AutoShape<?, ?> master1_as = (AutoShape<?,?>)master1.getPlaceholder(Placeholder.BODY);
final TextParagraph<?, ?, ? extends TextRun> master1_tp = master1_as.getTextParagraphs().get(0);
master1_tp.clearTabStops();
int i1 = 0;
for (final TabStopType tst : TabStopType.values()) {
master1_tp.addTabStops(10+i1*10, tst);
i1++;
}
// then set it on a normal slide
final Slide<?,?> slide1 = show1.createSlide();
final AutoShape<?, ?> slide1_as = slide1.createAutoShape();
slide1_as.setText("abc");
slide1_as.setAnchor(new Rectangle2D.Double(100,100,100,100));
final TextParagraph<?, ?, ? extends TextRun> slide1_tp = slide1_as.getTextParagraphs().get(0);
slide1_tp.getTextRuns().get(0).setFontColor(new Color(0x563412));
slide1_tp.clearTabStops();
int i2 = 0;
for (final TabStopType tst : TabStopType.values()) {
slide1_tp.addTabStops(15+i2*5, tst);
i2++;
}
try (final SlideShow<?, ?> show2 = reopen(show1)) {
final MasterSheet<?, ?> master2 = show2.getSlideMasters().get(0);
final AutoShape<?, ?> master2_as = (AutoShape<?,?>)master2.getPlaceholder(Placeholder.BODY);
final TextParagraph<?, ?, ? extends TextRun> master2_tp = master2_as.getTextParagraphs().get(0);
final List<? extends TabStop> master2_tabStops = master2_tp.getTabStops();
assertNotNull(master2_tabStops);
int i3 = 0;
for (final TabStopType tst : TabStopType.values()) {
final TabStop ts = master2_tabStops.get(i3);
assertEquals(10+i3*10, ts.getPositionInPoints(), 0.0);
assertEquals(tst, ts.getType());
i3++;
}
final Slide<?,?> slide2 = show2.getSlides().get(0);
final AutoShape<?,?> slide2_as = (AutoShape<?,?>)slide2.getShapes().get(0);
final TextParagraph<?, ?, ? extends TextRun> slide2_tp = slide2_as.getTextParagraphs().get(0);
final List<? extends TabStop> slide2_tabStops = slide2_tp.getTabStops();
assertNotNull(slide2_tabStops);
int i4 = 0;
for (final TabStopType tst : TabStopType.values()) {
final TabStop ts = slide2_tabStops.get(i4);
assertEquals(15+i4*5, ts.getPositionInPoints(), 0.0);
assertEquals(tst, ts.getType());
i4++;
}
}
}
}
}

Loading…
Cancel
Save