123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 |
- /* ====================================================================
- 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 java.io.ByteArrayInputStream;
- import java.io.InputStream;
- import java.util.List;
-
- import org.apache.poi.ddf.AbstractEscherOptRecord;
- import org.apache.poi.ddf.EscherArrayProperty;
- import org.apache.poi.ddf.EscherBSERecord;
- import org.apache.poi.ddf.EscherColorRef;
- import org.apache.poi.ddf.EscherContainerRecord;
- import org.apache.poi.ddf.EscherProperties;
- import org.apache.poi.ddf.EscherRecord;
- import org.apache.poi.ddf.EscherSimpleProperty;
- import org.apache.poi.hslf.record.Document;
- import org.apache.poi.hslf.record.RecordTypes;
- import org.apache.poi.sl.draw.DrawPaint;
- import org.apache.poi.sl.usermodel.ColorStyle;
- import org.apache.poi.sl.usermodel.FillStyle;
- import org.apache.poi.sl.usermodel.PaintStyle;
- import org.apache.poi.sl.usermodel.PaintStyle.GradientPaint;
- import org.apache.poi.sl.usermodel.PaintStyle.GradientPaint.GradientType;
- import org.apache.poi.sl.usermodel.PaintStyle.TexturePaint;
- import org.apache.poi.util.BitField;
- import org.apache.poi.util.BitFieldFactory;
- import org.apache.poi.util.LittleEndian;
- import org.apache.poi.util.POILogFactory;
- import org.apache.poi.util.POILogger;
- import org.apache.poi.util.Units;
-
- /**
- * Represents functionality provided by the 'Fill Effects' dialog in PowerPoint.
- */
- @SuppressWarnings("WeakerAccess")
- public final class HSLFFill {
- private static final POILogger LOG = POILogFactory.getLogger(HSLFFill.class);
-
- /**
- * Fill with a solid color
- */
- static final int FILL_SOLID = 0;
-
- /**
- * Fill with a pattern (bitmap)
- */
- static final int FILL_PATTERN = 1;
-
- /**
- * A texture (pattern with its own color map)
- */
- static final int FILL_TEXTURE = 2;
-
- /**
- * Center a picture in the shape
- */
- static final int FILL_PICTURE = 3;
-
- /**
- * Shade from start to end points
- */
- static final int FILL_SHADE = 4;
-
- /**
- * Shade from bounding rectangle to end point
- */
- static final int FILL_SHADE_CENTER = 5;
-
- /**
- * Shade from shape outline to end point
- */
- static final int FILL_SHADE_SHAPE = 6;
-
- /**
- * Similar to FILL_SHADE, but the fill angle
- * is additionally scaled by the aspect ratio of
- * the shape. If shape is square, it is the same as FILL_SHADE
- */
- static final int FILL_SHADE_SCALE = 7;
-
- /**
- * shade to title
- */
- static final int FILL_SHADE_TITLE = 8;
-
- /**
- * Use the background fill color/pattern
- */
- static final int FILL_BACKGROUND = 9;
-
- /**
- * A bit that specifies whether the RecolorFillAsPicture bit is set.
- * A value of 0x0 specifies that the fRecolorFillAsPicture MUST be ignored.
- * The default value for this property is 0x0.
- */
- private static final BitField FILL_USE_RECOLOR_FILL_AS_PICTURE = BitFieldFactory.getInstance(0x00400000);
-
- /**
- * A bit that specifies whether the UseShapeAnchor bit is set.
- * A value of 0x0 specifies that the fUseShapeAnchor MUST be ignored.
- * The default value for this property is 0x0.
- */
- private static final BitField FILL_USE_USE_SHAPE_ANCHOR = BitFieldFactory.getInstance(0x00200000);
-
- /**
- * A bit that specifies whether the Filled bit is set.
- * A value of 0x0 specifies that the Filled MUST be ignored.
- * The default value for this property is 0x0.
- */
- private static final BitField FILL_USE_FILLED = BitFieldFactory.getInstance(0x00100000);
-
- /**
- * A bit that specifies whether the HitTestFill bit is set.
- * A value of 0x0 specifies that the HitTestFill MUST be ignored.
- * The default value for this property is 0x0.
- */
- private static final BitField FILL_USE_HIT_TEST_FILL = BitFieldFactory.getInstance(0x00080000);
-
- /**
- * A bit that specifies whether the fillShape bit is set.
- * A value of 0x0 specifies that the fillShape MUST be ignored.
- * The default value for this property is 0x0.
- */
- private static final BitField FILL_USE_FILL_SHAPE = BitFieldFactory.getInstance(0x00040000);
-
- /**
- * A bit that specifies whether the fillUseRect bit is set.
- * A value of 0x0 specifies that the fillUseRect MUST be ignored.
- * The default value for this property is 0x0.
- */
- private static final BitField FILL_USE_FILL_USE_RECT = BitFieldFactory.getInstance(0x00020000);
-
- /**
- * A bit that specifies whether the fNoFillHitTest bit is set.
- * A value of 0x0 specifies that the fNoFillHitTest MUST be ignored.
- * The default value for this property is 0x0.
- */
- private static final BitField FILL_USE_NO_FILL_HIT_TEST = BitFieldFactory.getInstance(0x00010000);
-
- /**
- * A bit that specifies how to recolor a picture fill. If this bit is set to 0x1, the pictureFillCrMod
- * property of the picture fill is used for recoloring. If this bit is set to 0x0, the fillCrMod property,
- * as defined in section 2.3.7.6, is used for recoloring.
- * If UsefRecolorFillAsPicture equals 0x0, this value MUST be ignored.
- * The default value for this property is 0x0.
- */
- private static final BitField FILL_RECOLOR_FILL_AS_PICTURE = BitFieldFactory.getInstance(0x00000040);
-
- /**
- * A bit that specifies whether the fill is rotated with the shape.
- * If UseUseShapeAnchor equals 0x0, this value MUST be ignored.
- * The default value for this property is 0x0.
- */
- private static final BitField FILL_USE_SHAPE_ANCHOR = BitFieldFactory.getInstance(0x00000020);
-
- /**
- * A bit that specifies whether the fill is rendered if the shape is a 2-D shape.
- * If this bit is set to 0x1, the fill of this shape is rendered based on the properties of the Fill Style
- * property set. If this bit is set to 0x0, the fill of this shape is not rendered.
- * If UseFilled is 0x0, this value MUST be ignored. The default value for this property is 0x1.
- */
- private static final BitField FILL_FILLED = BitFieldFactory.getInstance(0x00000010);
-
- /**
- * A bit that specifies whether this fill will be hit tested.
- * If UsefHitTestFill equals 0x0, this value MUST be ignored.
- * The default value for this property is 0x1.
- */
- private static final BitField FILL_HIT_TEST_FILL = BitFieldFactory.getInstance(0x00000008);
-
- /**
- * A bit that specifies how the fill is aligned. If this bit is set to 0x1, the fill is
- * aligned relative to the shape so that it moves with the shape. If this bit is set to 0x0,
- * the fill is aligned with the origin of the view. If fUsefillShape equals 0x0, this value MUST be ignored.
- * The default value for this property is 0x1.
- */
- private static final BitField FILL_FILL_SHAPE = BitFieldFactory.getInstance(0x00000004);
-
- /**
- * A bit that specifies whether to use the rectangle specified by the fillRectLeft, fillRectRight,
- * fillRectTop, and fillRectBottom properties, rather than the bounding rectangle of the shape,
- * as the filled area. If fUsefillUseRect equals 0x0, this value MUST be ignored.
- * The default value for this property is 0x0.
- */
- private static final BitField FILL_FILL_USE_RECT = BitFieldFactory.getInstance(0x00000002);
-
- /**
- * A bit that specifies whether this shape will be hit tested as though it were filled.
- * If UsefNoFillHitTest equals 0x0, this value MUST be ignored.
- * The default value for this property is 0x0.
- */
- private static final BitField FILL_NO_FILL_HIT_TEST = BitFieldFactory.getInstance(0x00000001);
-
-
- /**
- * The shape this background applies to
- */
- private HSLFShape shape;
-
- /**
- * Construct a {@code Fill} object for a shape.
- * Fill information will be read from shape's escher properties.
- *
- * @param shape the shape this background applies to
- */
- public HSLFFill(HSLFShape shape){
- this.shape = shape;
- }
-
-
- public FillStyle getFillStyle() {
- return new FillStyle() {
- @Override
- public PaintStyle getPaint() {
- final int fillType = getFillType();
- // TODO: fix gradient types, this mismatches with the MS-ODRAW definition ...
- // need to handle (not only) the type (radial,rectangular,linear),
- // the direction, e.g. top right, and bounds (e.g. for rectangular boxes)
- switch (fillType) {
- case FILL_SOLID:
- return DrawPaint.createSolidPaint(getForegroundColor());
- case FILL_SHADE_SHAPE:
- return getGradientPaint(GradientType.shape);
- case FILL_SHADE_CENTER:
- case FILL_SHADE_TITLE:
- return getGradientPaint(GradientType.circular);
- case FILL_SHADE:
- case FILL_SHADE_SCALE:
- return getGradientPaint(GradientType.linear);
- case FILL_PICTURE:
- return getTexturePaint();
- default:
- LOG.log(POILogger.WARN, "unsuported fill type: " + fillType);
- return null;
- }
- }
- };
- }
-
-
-
- private GradientPaint getGradientPaint(final GradientType gradientType) {
- AbstractEscherOptRecord opt = shape.getEscherOptRecord();
- final EscherArrayProperty ep = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__SHADECOLORS);
- final int colorCnt = (ep == null) ? 0 : ep.getNumberOfElementsInArray();
-
- // NOFILLHITTEST can be in the normal escher opt record but also in the tertiary record
- // the extended bit fields seem to be in the second
- opt = shape.getEscherChild(RecordTypes.EscherUserDefined);
- EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST);
- int propVal = (p == null) ? 0 : p.getPropertyValue();
- final boolean rotateWithShape = FILL_USE_USE_SHAPE_ANCHOR.isSet(propVal) && FILL_USE_SHAPE_ANCHOR.isSet(propVal);
-
- return new GradientPaint() {
- @Override
- public double getGradientAngle() {
- // A value of type FixedPoint, as specified in [MS-OSHARED] section 2.2.1.6,
- // that specifies the angle of the gradient fill. Zero degrees represents a vertical vector from
- // bottom to top. The default value for this property is 0x00000000.
- int rot = shape.getEscherProperty(EscherProperties.FILL__ANGLE);
- return 90-Units.fixedPointToDouble(rot);
- }
-
- @Override
- public ColorStyle[] getGradientColors() {
- ColorStyle[] cs;
- if (colorCnt == 0) {
- cs = new ColorStyle[2];
- cs[0] = wrapColor(getBackgroundColor());
- cs[1] = wrapColor(getForegroundColor());
- } else {
- cs = new ColorStyle[colorCnt];
- int idx = 0;
- // TODO: handle palette colors and alpha(?) value
- for (byte[] data : ep) {
- EscherColorRef ecr = new EscherColorRef(data, 0, 4);
- cs[idx++] = wrapColor(shape.getColor(ecr));
- }
- }
- return cs;
- }
-
- private ColorStyle wrapColor(Color col) {
- return (col == null) ? null : DrawPaint.createSolidPaint(col).getSolidColor();
- }
-
- @Override
- public float[] getGradientFractions() {
- float[] frc;
- if (colorCnt == 0) {
- frc = new float[]{0, 1};
- } else {
- frc = new float[colorCnt];
- int idx = 0;
- for (byte[] data : ep) {
- double pos = Units.fixedPointToDouble(LittleEndian.getInt(data, 4));
- frc[idx++] = (float)pos;
- }
- }
- return frc;
- }
-
- @Override
- public boolean isRotatedWithShape() {
- return rotateWithShape;
- }
-
- @Override
- public GradientType getGradientType() {
- return gradientType;
- }
- };
- }
-
- private TexturePaint getTexturePaint() {
- final HSLFPictureData pd = getPictureData();
- if (pd == null) {
- return null;
- }
-
- return new TexturePaint() {
- @Override
- public InputStream getImageData() {
- return new ByteArrayInputStream(pd.getData());
- }
-
- @Override
- public String getContentType() {
- return pd.getContentType();
- }
-
- @Override
- public int getAlpha() {
- return (int)(shape.getAlpha(EscherProperties.FILL__FILLOPACITY)*100000.0);
- }
- };
- }
-
- /**
- * Returns fill type.
- * Must be one of the {@code FILL_*} constants defined in this class.
- *
- * @return type of fill
- */
- public int getFillType(){
- AbstractEscherOptRecord opt = shape.getEscherOptRecord();
- EscherSimpleProperty prop = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__FILLTYPE);
- return prop == null ? FILL_SOLID : prop.getPropertyValue();
- }
-
- void afterInsert(HSLFSheet sh){
- AbstractEscherOptRecord opt = shape.getEscherOptRecord();
- EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE);
- if(p != null) {
- int idx = p.getPropertyValue();
- EscherBSERecord bse = getEscherBSERecord(idx);
- if (bse != null) {
- bse.setRef(bse.getRef() + 1);
- }
- }
- }
-
- @SuppressWarnings("resource")
- EscherBSERecord getEscherBSERecord(int idx){
- HSLFSheet sheet = shape.getSheet();
- if(sheet == null) {
- LOG.log(POILogger.DEBUG, "Fill has not yet been assigned to a sheet");
- return null;
- }
- HSLFSlideShow ppt = sheet.getSlideShow();
- Document doc = ppt.getDocumentRecord();
- EscherContainerRecord dggContainer = doc.getPPDrawingGroup().getDggContainer();
- EscherContainerRecord bstore = HSLFShape.getEscherChild(dggContainer, EscherContainerRecord.BSTORE_CONTAINER);
- if(bstore == null) {
- LOG.log(POILogger.DEBUG, "EscherContainerRecord.BSTORE_CONTAINER was not found ");
- return null;
- }
- List<EscherRecord> lst = bstore.getChildRecords();
- return (EscherBSERecord)lst.get(idx-1);
- }
-
- /**
- * Sets fill type.
- * Must be one of the {@code FILL_*} constants defined in this class.
- *
- * @param type type of the fill
- */
- public void setFillType(int type){
- AbstractEscherOptRecord opt = shape.getEscherOptRecord();
- HSLFShape.setEscherProperty(opt, EscherProperties.FILL__FILLTYPE, type);
- }
-
- /**
- * Foreground color
- */
- public Color getForegroundColor(){
- AbstractEscherOptRecord opt = shape.getEscherOptRecord();
- EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST);
- int propVal = (p == null) ? 0 : p.getPropertyValue();
-
- return (FILL_USE_FILLED.isSet(propVal) && !FILL_FILLED.isSet(propVal))
- ? null
- : shape.getColor(EscherProperties.FILL__FILLCOLOR, EscherProperties.FILL__FILLOPACITY);
- }
-
- /**
- * Foreground color
- */
- public void setForegroundColor(Color color){
- AbstractEscherOptRecord opt = shape.getEscherOptRecord();
- opt.removeEscherProperty(EscherProperties.FILL__FILLOPACITY);
- opt.removeEscherProperty(EscherProperties.FILL__FILLCOLOR);
-
- if (color != null) {
- int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 0).getRGB();
- HSLFShape.setEscherProperty(opt, EscherProperties.FILL__FILLCOLOR, rgb);
- int alpha = color.getAlpha();
- if (alpha < 255) {
- int alphaFP = Units.doubleToFixedPoint(alpha/255d);
- HSLFShape.setEscherProperty(opt, EscherProperties.FILL__FILLOPACITY, alphaFP);
- }
- }
-
- EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST);
- int propVal = (p == null) ? 0 : p.getPropertyValue();
- propVal = FILL_FILLED.setBoolean(propVal, color != null);
- propVal = FILL_NO_FILL_HIT_TEST.setBoolean(propVal, color != null);
- propVal = FILL_USE_FILLED.set(propVal);
- propVal = FILL_USE_FILL_SHAPE.set(propVal);
- propVal = FILL_USE_NO_FILL_HIT_TEST.set(propVal);
- // TODO: check why we always clear this ...
- propVal = FILL_FILL_SHAPE.clear(propVal);
-
- HSLFShape.setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, propVal);
- }
-
- /**
- * Background color
- */
- public Color getBackgroundColor(){
- AbstractEscherOptRecord opt = shape.getEscherOptRecord();
- EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST);
- int propVal = (p == null) ? 0 : p.getPropertyValue();
-
- return (FILL_USE_FILLED.isSet(propVal) && !FILL_FILLED.isSet(propVal))
- ? null
- : shape.getColor(EscherProperties.FILL__FILLBACKCOLOR, EscherProperties.FILL__FILLOPACITY);
- }
-
- /**
- * Background color
- */
- public void setBackgroundColor(Color color){
- AbstractEscherOptRecord opt = shape.getEscherOptRecord();
- if (color == null) {
- HSLFShape.setEscherProperty(opt, EscherProperties.FILL__FILLBACKCOLOR, -1);
- }
- else {
- int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 0).getRGB();
- HSLFShape.setEscherProperty(opt, EscherProperties.FILL__FILLBACKCOLOR, rgb);
- }
- }
-
- /**
- * {@code PictureData} object used in a texture, pattern of picture fill.
- */
- @SuppressWarnings("resource")
- public HSLFPictureData getPictureData(){
- AbstractEscherOptRecord opt = shape.getEscherOptRecord();
- EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE);
- if (p == null) {
- return null;
- }
-
- HSLFSlideShow ppt = shape.getSheet().getSlideShow();
- List<HSLFPictureData> pict = ppt.getPictureData();
- Document doc = ppt.getDocumentRecord();
-
- EscherContainerRecord dggContainer = doc.getPPDrawingGroup().getDggContainer();
- EscherContainerRecord bstore = HSLFShape.getEscherChild(dggContainer, EscherContainerRecord.BSTORE_CONTAINER);
-
- java.util.List<EscherRecord> lst = bstore.getChildRecords();
- int idx = p.getPropertyValue();
- if (idx == 0){
- LOG.log(POILogger.WARN, "no reference to picture data found ");
- } else {
- EscherBSERecord bse = (EscherBSERecord)lst.get(idx - 1);
- for (HSLFPictureData pd : pict) {
- if (pd.getOffset() == bse.getOffset()){
- return pd;
- }
- }
- }
-
- return null;
- }
-
- /**
- * Assign picture used to fill the underlying shape.
- *
- * @param data the picture data added to this ppt by {@link HSLFSlideShow#addPicture(byte[], org.apache.poi.sl.usermodel.PictureData.PictureType)} method.
- */
- public void setPictureData(HSLFPictureData data){
- AbstractEscherOptRecord opt = shape.getEscherOptRecord();
- HSLFShape.setEscherProperty(opt, (short)(EscherProperties.FILL__PATTERNTEXTURE + 0x4000), (data == null ? 0 : data.getIndex()));
- if(data != null && shape.getSheet() != null) {
- EscherBSERecord bse = getEscherBSERecord(data.getIndex());
- if (bse != null) {
- bse.setRef(bse.getRef() + 1);
- }
- }
- }
-
- }
|