459 lines
13 KiB
Java
459 lines
13 KiB
Java
/* ====================================================================
|
|
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.usermodel;
|
|
|
|
import static org.apache.poi.hslf.usermodel.HSLFTextParagraph.getPropVal;
|
|
|
|
import java.awt.Color;
|
|
|
|
import org.apache.poi.hslf.exceptions.HSLFException;
|
|
import org.apache.poi.hslf.model.textproperties.BitMaskTextProp;
|
|
import org.apache.poi.hslf.model.textproperties.CharFlagsTextProp;
|
|
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.ExHyperlink;
|
|
import org.apache.poi.hslf.record.InteractiveInfo;
|
|
import org.apache.poi.hslf.record.InteractiveInfoAtom;
|
|
import org.apache.poi.hslf.record.Record;
|
|
import org.apache.poi.sl.draw.DrawPaint;
|
|
import org.apache.poi.sl.usermodel.PaintStyle;
|
|
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
|
|
import org.apache.poi.sl.usermodel.TextRun;
|
|
import org.apache.poi.util.Internal;
|
|
import org.apache.poi.util.POILogFactory;
|
|
import org.apache.poi.util.POILogger;
|
|
|
|
|
|
/**
|
|
* Represents a run of text, all with the same style
|
|
*
|
|
*/
|
|
public final class HSLFTextRun implements TextRun {
|
|
protected POILogger logger = POILogFactory.getLogger(this.getClass());
|
|
|
|
/** The TextRun we belong to */
|
|
private HSLFTextParagraph parentParagraph;
|
|
private String _runText = "";
|
|
private String _fontFamily;
|
|
private int hyperlinkId = -1;
|
|
|
|
/**
|
|
* Our paragraph and character style.
|
|
* Note - we may share these styles with other RichTextRuns
|
|
*/
|
|
private TextPropCollection characterStyle = new TextPropCollection(1, TextPropType.character);
|
|
|
|
/**
|
|
* Create a new wrapper around a rich text string
|
|
* @param parentParagraph the parent paragraph
|
|
*/
|
|
public HSLFTextRun(HSLFTextParagraph parentParagraph) {
|
|
this.parentParagraph = parentParagraph;
|
|
}
|
|
|
|
public TextPropCollection getCharacterStyle() {
|
|
return characterStyle;
|
|
}
|
|
|
|
public void setCharacterStyle(TextPropCollection characterStyle) {
|
|
this.characterStyle.copy(characterStyle);
|
|
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 characterStyle) {
|
|
this.characterStyle = characterStyle;
|
|
}
|
|
|
|
|
|
/**
|
|
* Supply the SlideShow we belong to
|
|
*/
|
|
public void updateSheet() {
|
|
if (_fontFamily != null) {
|
|
setFontFamily(_fontFamily);
|
|
_fontFamily = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the length of the text
|
|
*/
|
|
public int getLength() {
|
|
return _runText.length();
|
|
}
|
|
|
|
/**
|
|
* Fetch the text, in raw storage form
|
|
*/
|
|
public String getRawText() {
|
|
return _runText;
|
|
}
|
|
|
|
/**
|
|
* Change the text
|
|
*/
|
|
public void setText(String text) {
|
|
if (text == null) {
|
|
throw new HSLFException("text must not be null");
|
|
}
|
|
String newText = HSLFTextParagraph.toInternalString(text);
|
|
if (!newText.equals(_runText)) {
|
|
_runText = newText;
|
|
if (HSLFSlideShow.getLoadSavePhase() == HSLFSlideShow.LoadSavePhase.LOADED) {
|
|
parentParagraph.setDirty();
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------- Internal helpers on rich text properties -------
|
|
|
|
/**
|
|
* Fetch the value of the given flag in the CharFlagsTextProp.
|
|
* Returns false if the CharFlagsTextProp isn't present, since the
|
|
* text property won't be set if there's no CharFlagsTextProp.
|
|
*/
|
|
private boolean isCharFlagsTextPropVal(int index) {
|
|
return getFlag(index);
|
|
}
|
|
|
|
protected boolean getFlag(int index) {
|
|
if (characterStyle == null) return false;
|
|
|
|
BitMaskTextProp prop = (BitMaskTextProp)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");
|
|
}
|
|
}
|
|
|
|
return prop == null ? false : prop.getSubValue(index);
|
|
}
|
|
|
|
/**
|
|
* Set the value of the given flag in the CharFlagsTextProp, adding
|
|
* it if required.
|
|
*/
|
|
private void setCharFlagsTextPropVal(int index, boolean value) {
|
|
// TODO: check if paragraph/chars can be handled the same ...
|
|
if (getFlag(index) != value) {
|
|
setFlag(index, value);
|
|
parentParagraph.setDirty();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the value of the given Paragraph TextProp, add if required
|
|
* @param propName The name of the Paragraph TextProp
|
|
* @param val The value to set for the TextProp
|
|
*/
|
|
public void setCharTextPropVal(String propName, Integer val) {
|
|
HSLFTextParagraph.setPropVal(characterStyle, propName, val);
|
|
parentParagraph.setDirty();
|
|
}
|
|
|
|
|
|
// --------------- Friendly getters / setters on rich text properties -------
|
|
|
|
@Override
|
|
public boolean isBold() {
|
|
return isCharFlagsTextPropVal(CharFlagsTextProp.BOLD_IDX);
|
|
}
|
|
|
|
@Override
|
|
public void setBold(boolean bold) {
|
|
setCharFlagsTextPropVal(CharFlagsTextProp.BOLD_IDX, bold);
|
|
}
|
|
|
|
@Override
|
|
public boolean isItalic() {
|
|
return isCharFlagsTextPropVal(CharFlagsTextProp.ITALIC_IDX);
|
|
}
|
|
|
|
@Override
|
|
public void setItalic(boolean italic) {
|
|
setCharFlagsTextPropVal(CharFlagsTextProp.ITALIC_IDX, italic);
|
|
}
|
|
|
|
@Override
|
|
public boolean isUnderlined() {
|
|
return isCharFlagsTextPropVal(CharFlagsTextProp.UNDERLINE_IDX);
|
|
}
|
|
|
|
@Override
|
|
public void setUnderlined(boolean underlined) {
|
|
setCharFlagsTextPropVal(CharFlagsTextProp.UNDERLINE_IDX, underlined);
|
|
}
|
|
|
|
/**
|
|
* Does the text have a shadow?
|
|
*/
|
|
public boolean isShadowed() {
|
|
return isCharFlagsTextPropVal(CharFlagsTextProp.SHADOW_IDX);
|
|
}
|
|
|
|
/**
|
|
* Does the text have a shadow?
|
|
*/
|
|
public void setShadowed(boolean flag) {
|
|
setCharFlagsTextPropVal(CharFlagsTextProp.SHADOW_IDX, flag);
|
|
}
|
|
|
|
/**
|
|
* Is this text embossed?
|
|
*/
|
|
public boolean isEmbossed() {
|
|
return isCharFlagsTextPropVal(CharFlagsTextProp.RELIEF_IDX);
|
|
}
|
|
|
|
/**
|
|
* Is this text embossed?
|
|
*/
|
|
public void setEmbossed(boolean flag) {
|
|
setCharFlagsTextPropVal(CharFlagsTextProp.RELIEF_IDX, flag);
|
|
}
|
|
|
|
@Override
|
|
public boolean isStrikethrough() {
|
|
return isCharFlagsTextPropVal(CharFlagsTextProp.STRIKETHROUGH_IDX);
|
|
}
|
|
|
|
@Override
|
|
public void setStrikethrough(boolean flag) {
|
|
setCharFlagsTextPropVal(CharFlagsTextProp.STRIKETHROUGH_IDX, flag);
|
|
}
|
|
|
|
/**
|
|
* Gets the subscript/superscript option
|
|
*
|
|
* @return the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript
|
|
*/
|
|
public int getSuperscript() {
|
|
TextProp tp = getPropVal(characterStyle, "superscript", parentParagraph);
|
|
return tp == null ? 0 : tp.getValue();
|
|
}
|
|
|
|
/**
|
|
* Sets the subscript/superscript option
|
|
*
|
|
* @param val the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript
|
|
*/
|
|
public void setSuperscript(int val) {
|
|
setCharTextPropVal("superscript", val);
|
|
}
|
|
|
|
@Override
|
|
public Double getFontSize() {
|
|
TextProp tp = getPropVal(characterStyle, "font.size", parentParagraph);
|
|
return tp == null ? null : (double)tp.getValue();
|
|
}
|
|
|
|
|
|
@Override
|
|
public void setFontSize(Double fontSize) {
|
|
Integer iFontSize = (fontSize == null) ? null : fontSize.intValue();
|
|
setCharTextPropVal("font.size", iFontSize);
|
|
}
|
|
|
|
/**
|
|
* Gets the font index
|
|
*/
|
|
public int getFontIndex() {
|
|
TextProp tp = getPropVal(characterStyle, "font.index", parentParagraph);
|
|
return tp == null ? -1 : tp.getValue();
|
|
}
|
|
|
|
/**
|
|
* Sets the font index
|
|
*/
|
|
public void setFontIndex(int idx) {
|
|
setCharTextPropVal("font.index", idx);
|
|
}
|
|
|
|
@Override
|
|
public void setFontFamily(String fontFamily) {
|
|
HSLFSheet sheet = parentParagraph.getSheet();
|
|
@SuppressWarnings("resource")
|
|
HSLFSlideShow slideShow = (sheet == null) ? null : sheet.getSlideShow();
|
|
if (sheet == null || slideShow == null) {
|
|
//we can't set font since slideshow is not assigned yet
|
|
_fontFamily = fontFamily;
|
|
return;
|
|
}
|
|
// Get the index for this font (adding if needed)
|
|
Integer fontIdx = (fontFamily == null) ? null : slideShow.getFontCollection().addFont(fontFamily);
|
|
setCharTextPropVal("font.index", fontIdx);
|
|
}
|
|
|
|
@Override
|
|
public String getFontFamily() {
|
|
HSLFSheet sheet = parentParagraph.getSheet();
|
|
@SuppressWarnings("resource")
|
|
HSLFSlideShow slideShow = (sheet == null) ? null : sheet.getSlideShow();
|
|
if (sheet == null || slideShow == null) {
|
|
return _fontFamily;
|
|
}
|
|
TextProp tp = getPropVal(characterStyle, "font.index,asian.font.index,ansi.font.index,symbol.font.index", parentParagraph);
|
|
if (tp == null) { return null; }
|
|
return slideShow.getFontCollection().getFontWithId(tp.getValue());
|
|
}
|
|
|
|
/**
|
|
* @return font color as PaintStyle
|
|
*/
|
|
@Override
|
|
public SolidPaint getFontColor() {
|
|
TextProp tp = getPropVal(characterStyle, "font.color", parentParagraph);
|
|
if (tp == null) return null;
|
|
Color color = HSLFTextParagraph.getColorFromColorIndexStruct(tp.getValue(), parentParagraph.getSheet());
|
|
SolidPaint ps = DrawPaint.createSolidPaint(color);
|
|
return ps;
|
|
}
|
|
|
|
/**
|
|
* Sets color of the text, as a int bgr.
|
|
* (PowerPoint stores as BlueGreenRed, not the more
|
|
* usual RedGreenBlue)
|
|
* @see java.awt.Color
|
|
*/
|
|
public void setFontColor(int bgr) {
|
|
setCharTextPropVal("font.color", bgr);
|
|
}
|
|
|
|
|
|
@Override
|
|
public void setFontColor(Color color) {
|
|
setFontColor(DrawPaint.createSolidPaint(color));
|
|
}
|
|
|
|
@Override
|
|
public void setFontColor(PaintStyle color) {
|
|
if (!(color instanceof SolidPaint)) {
|
|
throw new IllegalArgumentException("HSLF only supports solid paint");
|
|
}
|
|
// In PowerPont RGB bytes are swapped, as BGR
|
|
SolidPaint sp = (SolidPaint)color;
|
|
Color c = DrawPaint.applyColorTransform(sp.getSolidColor());
|
|
int rgb = new Color(c.getBlue(), c.getGreen(), c.getRed(), 254).getRGB();
|
|
setFontColor(rgb);
|
|
}
|
|
|
|
protected void setFlag(int index, boolean value) {
|
|
BitMaskTextProp prop = (BitMaskTextProp)characterStyle.addWithName(CharFlagsTextProp.NAME);
|
|
prop.setSubValue(value, index);
|
|
}
|
|
|
|
public HSLFTextParagraph getTextParagraph() {
|
|
return parentParagraph;
|
|
}
|
|
|
|
public TextCap getTextCap() {
|
|
return TextCap.NONE;
|
|
}
|
|
|
|
@Override
|
|
public boolean isSubscript() {
|
|
return getSuperscript() < 0;
|
|
}
|
|
|
|
@Override
|
|
public boolean isSuperscript() {
|
|
return getSuperscript() > 0;
|
|
}
|
|
|
|
public byte getPitchAndFamily() {
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Sets the associated hyperlink id - currently this is only used while parsing and
|
|
* can't be used for update a ppt
|
|
*
|
|
* @param hyperlinkId the id or -1 to unset it
|
|
*/
|
|
public void setHyperlinkId(int hyperlinkId) {
|
|
this.hyperlinkId = hyperlinkId;
|
|
}
|
|
|
|
/**
|
|
* Returns the associated hyperlink id
|
|
*
|
|
* @return the hyperlink id
|
|
*/
|
|
public int getHyperlinkId() {
|
|
return hyperlinkId;
|
|
}
|
|
|
|
|
|
@Override
|
|
public HSLFHyperlink getHyperlink() {
|
|
if (hyperlinkId == -1) {
|
|
return null;
|
|
}
|
|
|
|
ExHyperlink linkRecord = parentParagraph.getSheet().getSlideShow().getDocumentRecord().getExObjList().get(hyperlinkId);
|
|
if (linkRecord == null) {
|
|
return null;
|
|
}
|
|
|
|
InteractiveInfoAtom info = null;
|
|
for (Record r : parentParagraph.getRecords()) {
|
|
if (r instanceof InteractiveInfo) {
|
|
InteractiveInfo ii = (InteractiveInfo)r;
|
|
InteractiveInfoAtom iia = ii.getInteractiveInfoAtom();
|
|
if (iia.getHyperlinkID() == hyperlinkId) {
|
|
info = iia;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (info == null) {
|
|
return null;
|
|
}
|
|
|
|
// TODO: check previous/next sibling runs for same hyperlink id and return the whole string length
|
|
int startIdx = parentParagraph.getStartIdxOfTextRun(this);
|
|
|
|
HSLFHyperlink link = new HSLFHyperlink();
|
|
link.setId(hyperlinkId);
|
|
link.setType(info.getAction());
|
|
link.setLabel(linkRecord.getLinkTitle());
|
|
link.setAddress(linkRecord.getLinkURL());
|
|
link.setStartIndex(startIdx);
|
|
link.setEndIndex(startIdx+getLength());
|
|
|
|
return link;
|
|
}
|
|
}
|