123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605 |
- /* ====================================================================
- 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 java.awt.Color;
-
- import org.apache.poi.ddf.AbstractEscherOptRecord;
- import org.apache.poi.ddf.EscherChildAnchorRecord;
- import org.apache.poi.ddf.EscherClientAnchorRecord;
- import org.apache.poi.ddf.EscherContainerRecord;
- import org.apache.poi.ddf.EscherOptRecord;
- import org.apache.poi.ddf.EscherProperties;
- import org.apache.poi.ddf.EscherProperty;
- import org.apache.poi.ddf.EscherRecord;
- import org.apache.poi.ddf.EscherSimpleProperty;
- import org.apache.poi.ddf.EscherSpRecord;
- import org.apache.poi.hslf.exceptions.HSLFException;
- import org.apache.poi.sl.draw.DrawPaint;
- import org.apache.poi.sl.draw.geom.CustomGeometry;
- import org.apache.poi.sl.draw.geom.Guide;
- import org.apache.poi.sl.draw.geom.PresetGeometries;
- import org.apache.poi.sl.usermodel.LineDecoration;
- import org.apache.poi.sl.usermodel.LineDecoration.DecorationShape;
- import org.apache.poi.sl.usermodel.LineDecoration.DecorationSize;
- 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.Shadow;
- import org.apache.poi.sl.usermodel.ShapeContainer;
- import org.apache.poi.sl.usermodel.ShapeType;
- import org.apache.poi.sl.usermodel.SimpleShape;
- import org.apache.poi.sl.usermodel.StrokeStyle;
- import org.apache.poi.sl.usermodel.StrokeStyle.LineCap;
- import org.apache.poi.sl.usermodel.StrokeStyle.LineCompound;
- import org.apache.poi.sl.usermodel.StrokeStyle.LineDash;
- import org.apache.poi.util.LittleEndian;
- import org.apache.poi.util.POILogFactory;
- import org.apache.poi.util.POILogger;
- import org.apache.poi.util.Units;
-
- /**
- * An abstract simple (non-group) shape.
- * This is the parent class for all primitive shapes like Line, Rectangle, etc.
- */
- public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<HSLFShape,HSLFTextParagraph> {
- private static final POILogger LOG = POILogFactory.getLogger(HSLFSimpleShape.class);
-
- public final static double DEFAULT_LINE_WIDTH = 0.75;
-
- /**
- * Hyperlink
- */
- protected HSLFHyperlink _hyperlink;
-
- /**
- * Create a SimpleShape object and initialize it from the supplied Record container.
- *
- * @param escherRecord <code>EscherSpContainer</code> container which holds information about this shape
- * @param parent the parent of the shape
- */
- protected HSLFSimpleShape(EscherContainerRecord escherRecord, ShapeContainer<HSLFShape,HSLFTextParagraph> parent){
- super(escherRecord, parent);
- }
-
- /**
- * Create a new Shape
- *
- * @param isChild <code>true</code> if the Line is inside a group, <code>false</code> otherwise
- * @return the record container which holds this shape
- */
- @Override
- protected EscherContainerRecord createSpContainer(boolean isChild) {
- EscherContainerRecord ecr = super.createSpContainer(isChild);
- ecr.setRecordId( EscherContainerRecord.SP_CONTAINER );
-
- EscherSpRecord sp = new EscherSpRecord();
- int flags = EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE;
- if (isChild) {
- flags |= EscherSpRecord.FLAG_CHILD;
- }
- sp.setFlags(flags);
- ecr.addChildRecord(sp);
-
- AbstractEscherOptRecord opt = new EscherOptRecord();
- opt.setRecordId(EscherOptRecord.RECORD_ID);
- ecr.addChildRecord(opt);
-
- EscherRecord anchor;
- if(isChild) {
- anchor = new EscherChildAnchorRecord();
- } else {
- anchor = new EscherClientAnchorRecord();
-
- //hack. internal variable EscherClientAnchorRecord.shortRecord can be
- //initialized only in fillFields(). We need to set shortRecord=false;
- byte[] header = new byte[16];
- LittleEndian.putUShort(header, 0, 0);
- LittleEndian.putUShort(header, 2, 0);
- LittleEndian.putInt(header, 4, 8);
- anchor.fillFields(header, 0, null);
- }
- ecr.addChildRecord(anchor);
-
- return ecr;
- }
-
- /**
- * Returns width of the line in in points
- */
- public double getLineWidth(){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEWIDTH);
- return (prop == null) ? DEFAULT_LINE_WIDTH : Units.toPoints(prop.getPropertyValue());
- }
-
- /**
- * Sets the width of line in in points
- * @param width the width of line in in points
- */
- public void setLineWidth(double width){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- setEscherProperty(opt, EscherProperties.LINESTYLE__LINEWIDTH, Units.toEMU(width));
- }
-
- /**
- * Sets the color of line
- *
- * @param color new color of the line
- */
- public void setLineColor(Color color){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- if (color == null) {
- setEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x80000);
- } else {
- int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 0).getRGB();
- setEscherProperty(opt, EscherProperties.LINESTYLE__COLOR, rgb);
- setEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x180018);
- }
- }
-
- /**
- * @return color of the line. If color is not set returns {@code null}
- */
- public Color getLineColor(){
- AbstractEscherOptRecord opt = getEscherOptRecord();
-
- EscherSimpleProperty p = getEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH);
- if(p != null && (p.getPropertyValue() & 0x8) == 0) {
- return null;
- }
-
- Color clr = getColor(EscherProperties.LINESTYLE__COLOR, EscherProperties.LINESTYLE__OPACITY);
- return clr == null ? null : clr;
- }
-
- /**
- * @return background color of the line. If color is not set returns {@code null}
- */
- public Color getLineBackgroundColor(){
- AbstractEscherOptRecord opt = getEscherOptRecord();
-
- EscherSimpleProperty p = getEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH);
- if(p != null && (p.getPropertyValue() & 0x8) == 0) {
- return null;
- }
-
- Color clr = getColor(EscherProperties.LINESTYLE__BACKCOLOR, EscherProperties.LINESTYLE__OPACITY);
- return clr == null ? null : clr;
- }
-
- /**
- * Sets the background color of line
- *
- * @param color new background color of the line
- */
- public void setLineBackgroundColor(Color color){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- if (color == null) {
- setEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x80000);
- opt.removeEscherProperty(EscherProperties.LINESTYLE__BACKCOLOR);
- } else {
- int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 0).getRGB();
- setEscherProperty(opt, EscherProperties.LINESTYLE__BACKCOLOR, rgb);
- setEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x180018);
- }
- }
-
- /**
- * Gets line cap.
- *
- * @return cap of the line.
- */
- public LineCap getLineCap(){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDCAPSTYLE);
- return (prop == null) ? LineCap.FLAT : LineCap.fromNativeId(prop.getPropertyValue());
- }
-
- /**
- * Sets line cap.
- *
- * @param pen new style of the line.
- */
- public void setLineCap(LineCap pen){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDCAPSTYLE, pen == LineCap.FLAT ? -1 : pen.nativeId);
- }
-
- /**
- * Gets line dashing.
- *
- * @return dashing of the line.
- */
- public LineDash getLineDash(){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEDASHING);
- return (prop == null) ? LineDash.SOLID : LineDash.fromNativeId(prop.getPropertyValue());
- }
-
- /**
- * Sets line dashing.
- *
- * @param pen new style of the line.
- */
- public void setLineDash(LineDash pen){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- setEscherProperty(opt, EscherProperties.LINESTYLE__LINEDASHING, pen == LineDash.SOLID ? -1 : pen.nativeId);
- }
-
- /**
- * Gets the line compound style
- *
- * @return the compound style of the line.
- */
- public LineCompound getLineCompound() {
- AbstractEscherOptRecord opt = getEscherOptRecord();
- EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTYLE);
- return (prop == null) ? LineCompound.SINGLE : LineCompound.fromNativeId(prop.getPropertyValue());
- }
-
- /**
- * Sets the line compound style
- *
- * @param style new compound style of the line.
- */
- public void setLineCompound(LineCompound style){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- setEscherProperty(opt, EscherProperties.LINESTYLE__LINESTYLE, style == LineCompound.SINGLE ? -1 : style.nativeId);
- }
-
- /**
- * Returns line style. One of the constants defined in this class.
- *
- * @return style of the line.
- */
- @Override
- public StrokeStyle getStrokeStyle(){
- return new StrokeStyle() {
- @Override
- public PaintStyle getPaint() {
- return DrawPaint.createSolidPaint(HSLFSimpleShape.this.getLineColor());
- }
-
- @Override
- public LineCap getLineCap() {
- return null;
- }
-
- @Override
- public LineDash getLineDash() {
- return HSLFSimpleShape.this.getLineDash();
- }
-
- @Override
- public LineCompound getLineCompound() {
- return HSLFSimpleShape.this.getLineCompound();
- }
-
- @Override
- public double getLineWidth() {
- return HSLFSimpleShape.this.getLineWidth();
- }
-
- };
- }
-
- @Override
- public Color getFillColor() {
- return getFill().getForegroundColor();
- }
-
- @Override
- public void setFillColor(Color color) {
- getFill().setForegroundColor(color);
- }
-
- @Override
- public Guide getAdjustValue(String name) {
- if (name == null || !name.matches("adj([1-9]|10)?")) {
- LOG.log(POILogger.INFO, "Adjust value '"+name+"' not supported. Using default value.");
- return null;
- }
-
- name = name.replace("adj", "");
- if (name.isEmpty()) {
- name = "1";
- }
-
- short escherProp;
- switch (Integer.parseInt(name)) {
- case 1: escherProp = EscherProperties.GEOMETRY__ADJUSTVALUE; break;
- case 2: escherProp = EscherProperties.GEOMETRY__ADJUST2VALUE; break;
- case 3: escherProp = EscherProperties.GEOMETRY__ADJUST3VALUE; break;
- case 4: escherProp = EscherProperties.GEOMETRY__ADJUST4VALUE; break;
- case 5: escherProp = EscherProperties.GEOMETRY__ADJUST5VALUE; break;
- case 6: escherProp = EscherProperties.GEOMETRY__ADJUST6VALUE; break;
- case 7: escherProp = EscherProperties.GEOMETRY__ADJUST7VALUE; break;
- case 8: escherProp = EscherProperties.GEOMETRY__ADJUST8VALUE; break;
- case 9: escherProp = EscherProperties.GEOMETRY__ADJUST9VALUE; break;
- case 10: escherProp = EscherProperties.GEOMETRY__ADJUST10VALUE; break;
- default: throw new HSLFException();
- }
-
- // TODO: the adjust values need to be corrected somehow depending on the shape width/height
- // see https://social.msdn.microsoft.com/Forums/en-US/3f69ebb3-62a0-4fdd-b367-64790dfb2491/presetshapedefinitionsxml-does-not-specify-width-and-height-form-some-autoshapes?forum=os_binaryfile
-
- // the adjust value can be format dependent, e.g. hexagon has different values,
- // other shape types have the same adjust values in OOXML and native
- int adjval = getEscherProperty(escherProp, -1);
-
- return (adjval == -1) ? null : new Guide(name, "val "+adjval);
- }
-
- @Override
- public CustomGeometry getGeometry() {
- PresetGeometries dict = PresetGeometries.getInstance();
- ShapeType st = getShapeType();
- String name = (st != null) ? st.getOoxmlName() : null;
- CustomGeometry geom = dict.get(name);
- if (geom == null) {
- if (name == null) {
- name = (st != null) ? st.toString() : "<unknown>";
- }
- LOG.log(POILogger.WARN, "No preset shape definition for shapeType: "+name);
- }
-
- return geom;
- }
-
-
- public double getShadowAngle() {
- AbstractEscherOptRecord opt = getEscherOptRecord();
- EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.SHADOWSTYLE__OFFSETX);
- int offX = (prop == null) ? 0 : prop.getPropertyValue();
- prop = getEscherProperty(opt, EscherProperties.SHADOWSTYLE__OFFSETY);
- int offY = (prop == null) ? 0 : prop.getPropertyValue();
- return Math.toDegrees(Math.atan2(offY, offX));
- }
-
- public double getShadowDistance() {
- AbstractEscherOptRecord opt = getEscherOptRecord();
- EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.SHADOWSTYLE__OFFSETX);
- int offX = (prop == null) ? 0 : prop.getPropertyValue();
- prop = getEscherProperty(opt, EscherProperties.SHADOWSTYLE__OFFSETY);
- int offY = (prop == null) ? 0 : prop.getPropertyValue();
- return Units.toPoints((long)Math.hypot(offX, offY));
- }
-
- /**
- * @return color of the line. If color is not set returns <code>java.awt.Color.black</code>
- */
- public Color getShadowColor(){
- Color clr = getColor(EscherProperties.SHADOWSTYLE__COLOR, EscherProperties.SHADOWSTYLE__OPACITY);
- return clr == null ? Color.black : clr;
- }
-
- @Override
- public Shadow<HSLFShape,HSLFTextParagraph> getShadow() {
- AbstractEscherOptRecord opt = getEscherOptRecord();
- if (opt == null) {
- return null;
- }
- EscherProperty shadowType = opt.lookup(EscherProperties.SHADOWSTYLE__TYPE);
- if (shadowType == null) {
- return null;
- }
-
- return new Shadow<HSLFShape,HSLFTextParagraph>(){
- @Override
- public SimpleShape<HSLFShape,HSLFTextParagraph> getShadowParent() {
- return HSLFSimpleShape.this;
- }
-
- @Override
- public double getDistance() {
- return getShadowDistance();
- }
-
- @Override
- public double getAngle() {
- return getShadowAngle();
- }
-
- @Override
- public double getBlur() {
- // TODO Auto-generated method stub
- return 0;
- }
-
- @Override
- public SolidPaint getFillStyle() {
- return DrawPaint.createSolidPaint(getShadowColor());
- }
-
- };
- }
-
- public DecorationShape getLineHeadDecoration(){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWHEAD);
- return (prop == null) ? null : DecorationShape.fromNativeId(prop.getPropertyValue());
- }
-
- public void setLineHeadDecoration(DecorationShape decoShape){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- setEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWHEAD, decoShape == null ? -1 : decoShape.nativeId);
- }
-
- public DecorationSize getLineHeadWidth(){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWWIDTH);
- return (prop == null) ? null : DecorationSize.fromNativeId(prop.getPropertyValue());
- }
-
- public void setLineHeadWidth(DecorationSize decoSize){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- setEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWWIDTH, decoSize == null ? -1 : decoSize.nativeId);
- }
-
- public DecorationSize getLineHeadLength(){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWLENGTH);
- return (prop == null) ? null : DecorationSize.fromNativeId(prop.getPropertyValue());
- }
-
- public void setLineHeadLength(DecorationSize decoSize){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- setEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWLENGTH, decoSize == null ? -1 : decoSize.nativeId);
- }
-
- public DecorationShape getLineTailDecoration(){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWHEAD);
- return (prop == null) ? null : DecorationShape.fromNativeId(prop.getPropertyValue());
- }
-
- public void setLineTailDecoration(DecorationShape decoShape){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWHEAD, decoShape == null ? -1 : decoShape.nativeId);
- }
-
- public DecorationSize getLineTailWidth(){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWWIDTH);
- return (prop == null) ? null : DecorationSize.fromNativeId(prop.getPropertyValue());
- }
-
- public void setLineTailWidth(DecorationSize decoSize){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWWIDTH, decoSize == null ? -1 : decoSize.nativeId);
- }
-
- public DecorationSize getLineTailLength(){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWLENGTH);
- return (prop == null) ? null : DecorationSize.fromNativeId(prop.getPropertyValue());
- }
-
- public void setLineTailLength(DecorationSize decoSize){
- AbstractEscherOptRecord opt = getEscherOptRecord();
- setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWLENGTH, decoSize == null ? -1 : decoSize.nativeId);
- }
-
-
-
- @Override
- public LineDecoration getLineDecoration() {
- return new LineDecoration() {
-
- @Override
- public DecorationShape getHeadShape() {
- return HSLFSimpleShape.this.getLineHeadDecoration();
- }
-
- @Override
- public DecorationSize getHeadWidth() {
- return HSLFSimpleShape.this.getLineHeadWidth();
- }
-
- @Override
- public DecorationSize getHeadLength() {
- return HSLFSimpleShape.this.getLineHeadLength();
- }
-
- @Override
- public DecorationShape getTailShape() {
- return HSLFSimpleShape.this.getLineTailDecoration();
- }
-
- @Override
- public DecorationSize getTailWidth() {
- return HSLFSimpleShape.this.getLineTailWidth();
- }
-
- @Override
- public DecorationSize getTailLength() {
- return HSLFSimpleShape.this.getLineTailLength();
- }
- };
- }
-
- @Override
- public HSLFShapePlaceholderDetails getPlaceholderDetails() {
- return new HSLFShapePlaceholderDetails(this);
- }
-
-
- @Override
- public Placeholder getPlaceholder() {
- return getPlaceholderDetails().getPlaceholder();
- }
-
- @Override
- public void setPlaceholder(Placeholder placeholder) {
- getPlaceholderDetails().setPlaceholder(placeholder);
- }
-
-
- @Override
- public void setStrokeStyle(Object... styles) {
- if (styles.length == 0) {
- // remove stroke
- setLineColor(null);
- return;
- }
-
- // TODO: handle PaintStyle
- for (Object st : styles) {
- if (st instanceof Number) {
- setLineWidth(((Number)st).doubleValue());
- } else if (st instanceof LineCap) {
- setLineCap((LineCap)st);
- } else if (st instanceof LineDash) {
- setLineDash((LineDash)st);
- } else if (st instanceof LineCompound) {
- setLineCompound((LineCompound)st);
- } else if (st instanceof Color) {
- setLineColor((Color)st);
- }
- }
- }
-
- @Override
- public HSLFHyperlink getHyperlink(){
- return _hyperlink;
- }
-
- @Override
- public HSLFHyperlink createHyperlink() {
- if (_hyperlink == null) {
- _hyperlink = HSLFHyperlink.createHyperlink(this);
- }
- return _hyperlink;
- }
-
- /**
- * Sets the hyperlink - used when the document is parsed
- *
- * @param link the hyperlink
- */
- protected void setHyperlink(HSLFHyperlink link) {
- _hyperlink = link;
- }
-
- @Override
- public boolean isPlaceholder() {
- // currently we only identify TextShapes as placeholders
- return false;
- }
- }
|