mirror of https://github.com/apache/poi.git
Bug 60656 - EMF image support in slideshows
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1859159 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
049baa86d7
commit
d6ee139b39
|
@ -0,0 +1,480 @@
|
|||
/* ====================================================================
|
||||
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.hemf.record.emfplus;
|
||||
|
||||
import static org.apache.poi.hemf.record.emf.HemfFill.readXForm;
|
||||
import static org.apache.poi.hemf.record.emfplus.HemfPlusDraw.readARGB;
|
||||
import static org.apache.poi.hemf.record.emfplus.HemfPlusDraw.readPointF;
|
||||
import static org.apache.poi.hemf.record.emfplus.HemfPlusDraw.readRectF;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.io.IOException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusHeader.EmfPlusGraphicsVersion;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusImage.EmfPlusImage;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusImage.EmfPlusWrapMode;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectData;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectType;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusPath.EmfPlusPath;
|
||||
import org.apache.poi.util.BitField;
|
||||
import org.apache.poi.util.BitFieldFactory;
|
||||
import org.apache.poi.util.LittleEndianConsts;
|
||||
import org.apache.poi.util.LittleEndianInputStream;
|
||||
|
||||
public class HemfPlusBrush {
|
||||
/** The BrushType enumeration defines types of graphics brushes, which are used to fill graphics regions. */
|
||||
public enum EmfPlusBrushType {
|
||||
SOLID_COLOR(0X00000000, EmfPlusSolidBrushData::new),
|
||||
HATCH_FILL(0X00000001, EmfPlusHatchBrushData::new),
|
||||
TEXTURE_FILL(0X00000002, EmfPlusTextureBrushData::new),
|
||||
PATH_GRADIENT(0X00000003, EmfPlusPathGradientBrushData::new),
|
||||
LINEAR_GRADIENT(0X00000004, EmfPlusLinearGradientBrushData::new)
|
||||
;
|
||||
|
||||
public final int id;
|
||||
public final Supplier<? extends EmfPlusBrushData> constructor;
|
||||
|
||||
EmfPlusBrushType(int id, Supplier<? extends EmfPlusBrushData> constructor) {
|
||||
this.id = id;
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
public static EmfPlusBrushType valueOf(int id) {
|
||||
for (EmfPlusBrushType wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum EmfPlusHatchStyle {
|
||||
HORIZONTAL(0X00000000),
|
||||
VERTICAL(0X00000001),
|
||||
FORWARD_DIAGONAL(0X00000002),
|
||||
BACKWARD_DIAGONAL(0X00000003),
|
||||
LARGE_GRID(0X00000004),
|
||||
DIAGONAL_CROSS(0X00000005),
|
||||
PERCENT_05(0X00000006),
|
||||
PERCENT_10(0X00000007),
|
||||
PERCENT_20(0X00000008),
|
||||
PERCENT_25(0X00000009),
|
||||
PERCENT_30(0X0000000A),
|
||||
PERCENT_40(0X0000000B),
|
||||
PERCENT_50(0X0000000C),
|
||||
PERCENT_60(0X0000000D),
|
||||
PERCENT_70(0X0000000E),
|
||||
PERCENT_75(0X0000000F),
|
||||
PERCENT_80(0X00000010),
|
||||
PERCENT_90(0X00000011),
|
||||
LIGHT_DOWNWARD_DIAGONAL(0X00000012),
|
||||
LIGHT_UPWARD_DIAGONAL(0X00000013),
|
||||
DARK_DOWNWARD_DIAGONAL(0X00000014),
|
||||
DARK_UPWARD_DIAGONAL(0X00000015),
|
||||
WIDE_DOWNWARD_DIAGONAL(0X00000016),
|
||||
WIDE_UPWARD_DIAGONAL(0X00000017),
|
||||
LIGHT_VERTICAL(0X00000018),
|
||||
LIGHT_HORIZONTAL(0X00000019),
|
||||
NARROW_VERTICAL(0X0000001A),
|
||||
NARROW_HORIZONTAL(0X0000001B),
|
||||
DARK_VERTICAL(0X0000001C),
|
||||
DARK_HORIZONTAL(0X0000001D),
|
||||
DASHED_DOWNWARD_DIAGONAL(0X0000001E),
|
||||
DASHED_UPWARD_DIAGONAL(0X0000001F),
|
||||
DASHED_HORIZONTAL(0X00000020),
|
||||
DASHED_VERTICAL(0X00000021),
|
||||
SMALL_CONFETTI(0X00000022),
|
||||
LARGE_CONFETTI(0X00000023),
|
||||
ZIGZAG(0X00000024),
|
||||
WAVE(0X00000025),
|
||||
DIAGONAL_BRICK(0X00000026),
|
||||
HORIZONTAL_BRICK(0X00000027),
|
||||
WEAVE(0X00000028),
|
||||
PLAID(0X00000029),
|
||||
DIVOT(0X0000002A),
|
||||
DOTTED_GRID(0X0000002B),
|
||||
DOTTED_DIAMOND(0X0000002C),
|
||||
SHINGLE(0X0000002D),
|
||||
TRELLIS(0X0000002E),
|
||||
SPHERE(0X0000002F),
|
||||
SMALL_GRID(0X00000030),
|
||||
SMALL_CHECKER_BOARD(0X00000031),
|
||||
LARGE_CHECKER_BOARD(0X00000032),
|
||||
OUTLINED_DIAMOND(0X00000033),
|
||||
SOLID_DIAMOND(0X00000034)
|
||||
;
|
||||
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusHatchStyle(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusHatchStyle valueOf(int id) {
|
||||
for (EmfPlusHatchStyle wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public interface EmfPlusBrushData {
|
||||
/**
|
||||
* This flag is meaningful in EmfPlusPathGradientBrushData objects.
|
||||
*
|
||||
* If set, an EmfPlusBoundaryPathData object MUST be specified in the BoundaryData field of the brush data object.
|
||||
* If clear, an EmfPlusBoundaryPointData object MUST be specified in the BoundaryData field of the brush data object.
|
||||
*/
|
||||
BitField PATH = BitFieldFactory.getInstance(0x00000001);
|
||||
|
||||
/**
|
||||
* This flag is meaningful in EmfPlusLinearGradientBrushData objects , EmfPlusPathGradientBrushData objects,
|
||||
* and EmfPlusTextureBrushData objects.
|
||||
*
|
||||
* If set, a 2x3 world space to device space transform matrix MUST be specified in the OptionalData field of
|
||||
* the brush data object.
|
||||
*/
|
||||
BitField TRANSFORM = BitFieldFactory.getInstance(0x00000002);
|
||||
|
||||
/**
|
||||
* This flag is meaningful in EmfPlusLinearGradientBrushData and EmfPlusPathGradientBrushData objects.
|
||||
*
|
||||
* If set, an EmfPlusBlendColors object MUST be specified in the OptionalData field of the brush data object.
|
||||
*/
|
||||
BitField PRESET_COLORS = BitFieldFactory.getInstance(0x00000004);
|
||||
|
||||
/**
|
||||
* This flag is meaningful in EmfPlusLinearGradientBrushData and EmfPlusPathGradientBrushData objects.
|
||||
*
|
||||
* If set, an EmfPlusBlendFactors object that specifies a blend pattern along a horizontal gradient MUST be
|
||||
* specified in the OptionalData field of the brush data object.
|
||||
*/
|
||||
BitField BLEND_FACTORS_H = BitFieldFactory.getInstance(0x00000008);
|
||||
|
||||
/**
|
||||
* This flag is meaningful in EmfPlusLinearGradientBrushData objects.
|
||||
*
|
||||
* If set, an EmfPlusBlendFactors object that specifies a blend pattern along a vertical gradient MUST be
|
||||
* specified in the OptionalData field of the brush data object.
|
||||
*/
|
||||
BitField BLEND_FACTORS_V = BitFieldFactory.getInstance(0x00000010);
|
||||
|
||||
/**
|
||||
* This flag is meaningful in EmfPlusPathGradientBrushData objects.
|
||||
*
|
||||
* If set, an EmfPlusFocusScaleData object MUST be specified in the OptionalData field of the brush data object.
|
||||
*/
|
||||
BitField FOCUS_SCALES = BitFieldFactory.getInstance(0x00000040);
|
||||
|
||||
/**
|
||||
* This flag is meaningful in EmfPlusLinearGradientBrushData, EmfPlusPathGradientBrushData, and
|
||||
* EmfPlusTextureBrushData objects.
|
||||
*
|
||||
* If set, the brush MUST already be gamma corrected; that is, output brightness and intensity have been
|
||||
* corrected to match the input image.
|
||||
*/
|
||||
BitField IS_GAMMA_CORRECTED = BitFieldFactory.getInstance(0x00000080);
|
||||
|
||||
/**
|
||||
* This flag is meaningful in EmfPlusTextureBrushData objects.
|
||||
*
|
||||
* If set, a world space to device space transform SHOULD NOT be applied to the texture brush.
|
||||
*/
|
||||
BitField DO_NOT_TRANSFORM = BitFieldFactory.getInstance(0x00000100);
|
||||
|
||||
long init(LittleEndianInputStream leis, long dataSize) throws IOException;
|
||||
}
|
||||
|
||||
/** The EmfPlusBrush object specifies a graphics brush for filling regions. */
|
||||
public static class EmfPlusBrush implements EmfPlusObjectData {
|
||||
private final EmfPlusGraphicsVersion version = new EmfPlusGraphicsVersion();
|
||||
private EmfPlusBrushType brushType;
|
||||
private EmfPlusBrushData brushData;
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long dataSize, EmfPlusObjectType objectType, int flags) throws IOException {
|
||||
long size = version.init(leis);
|
||||
|
||||
brushType = EmfPlusBrushType.valueOf(leis.readInt());
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
assert(brushType != null);
|
||||
|
||||
size += (brushData = brushType.constructor.get()).init(leis, dataSize-size);
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
/** The EmfPlusSolidBrushData object specifies a solid color for a graphics brush. */
|
||||
public static class EmfPlusSolidBrushData implements EmfPlusBrushData {
|
||||
private Color solidColor;
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long dataSize) throws IOException {
|
||||
solidColor = readARGB(leis.readInt());
|
||||
return LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** The EmfPlusHatchBrushData object specifies a hatch pattern for a graphics brush. */
|
||||
public static class EmfPlusHatchBrushData implements EmfPlusBrushData {
|
||||
private EmfPlusHatchStyle style;
|
||||
private Color foreColor, backColor;
|
||||
public long init(LittleEndianInputStream leis, long dataSize) {
|
||||
style = EmfPlusHatchStyle.valueOf(leis.readInt());
|
||||
foreColor = readARGB(leis.readInt());
|
||||
backColor = readARGB(leis.readInt());
|
||||
return 3*LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/** The EmfPlusLinearGradientBrushData object specifies a linear gradient for a graphics brush. */
|
||||
public static class EmfPlusLinearGradientBrushData implements EmfPlusBrushData {
|
||||
private int dataFlags;
|
||||
private EmfPlusWrapMode wrapMode;
|
||||
private Rectangle2D rect = new Rectangle2D.Double();
|
||||
private Color startColor, endColor;
|
||||
private AffineTransform transform;
|
||||
private double[] positions;
|
||||
private Color[] blendColors;
|
||||
private double[] positionsV;
|
||||
private double[] blendFactorsV;
|
||||
private double[] positionsH;
|
||||
private double[] blendFactorsH;
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long dataSize) throws IOException {
|
||||
// A 32-bit unsigned integer that specifies the data in the OptionalData field.
|
||||
// This value MUST be composed of BrushData flags
|
||||
dataFlags = leis.readInt();
|
||||
|
||||
// A 32-bit signed integer from the WrapMode enumeration that specifies whether to paint the area outside
|
||||
// the boundary of the brush. When painting outside the boundary, the wrap mode specifies how the color
|
||||
// gradient is repeated.
|
||||
wrapMode = EmfPlusWrapMode.valueOf(leis.readInt());
|
||||
|
||||
int size = 2*LittleEndianConsts.INT_SIZE;
|
||||
size += readRectF(leis, rect);
|
||||
|
||||
// An EmfPlusARGB object that specifies the color at the starting/ending boundary point of the linear gradient brush.
|
||||
startColor = readARGB(leis.readInt());
|
||||
endColor = readARGB(leis.readInt());
|
||||
|
||||
// skip reserved1/2 fields
|
||||
leis.skipFully(2*LittleEndianConsts.INT_SIZE);
|
||||
|
||||
size += 4*LittleEndianConsts.INT_SIZE;
|
||||
|
||||
if (TRANSFORM.isSet(dataFlags)) {
|
||||
size += readXForm(leis, (transform = new AffineTransform()));
|
||||
}
|
||||
|
||||
final boolean isPreset = PRESET_COLORS.isSet(dataFlags);
|
||||
final boolean blendH = BLEND_FACTORS_H.isSet(dataFlags);
|
||||
final boolean blendV = BLEND_FACTORS_V.isSet(dataFlags);
|
||||
if (isPreset && (blendH || blendV)) {
|
||||
throw new RuntimeException("invalid combination of preset colors and blend factors v/h");
|
||||
}
|
||||
|
||||
size += (isPreset) ? readColors(leis, d -> positions = d, c -> blendColors = c) : 0;
|
||||
size += (blendV) ? readFactors(leis, d -> positionsV = d, f -> blendFactorsV = f) : 0;
|
||||
size += (blendH) ? readFactors(leis, d -> positionsH = d, f -> blendFactorsH = f) : 0;
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
/** The EmfPlusPathGradientBrushData object specifies a path gradient for a graphics brush. */
|
||||
public static class EmfPlusPathGradientBrushData implements EmfPlusBrushData {
|
||||
private int dataFlags;
|
||||
private EmfPlusWrapMode wrapMode;
|
||||
private Color centerColor;
|
||||
private final Point2D centerPoint = new Point2D.Double();
|
||||
private Color[] surroundingColor;
|
||||
private EmfPlusPath boundaryPath;
|
||||
private Point2D[] boundaryPoints;
|
||||
private AffineTransform transform;
|
||||
private double[] positions;
|
||||
private Color[] blendColors;
|
||||
private double[] blendFactorsH;
|
||||
private Double focusScaleX, focusScaleY;
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long dataSize) throws IOException {
|
||||
// A 32-bit unsigned integer that specifies the data in the OptionalData field.
|
||||
// This value MUST be composed of BrushData flags
|
||||
dataFlags = leis.readInt();
|
||||
|
||||
// A 32-bit signed integer from the WrapMode enumeration that specifies whether to paint the area outside
|
||||
// the boundary of the brush. When painting outside the boundary, the wrap mode specifies how the color
|
||||
// gradient is repeated.
|
||||
wrapMode = EmfPlusWrapMode.valueOf(leis.readInt());
|
||||
|
||||
// An EmfPlusARGB object that specifies the center color of the path gradient brush, which is the color
|
||||
// that appears at the center point of the brush. The color of the brush changes gradually from the
|
||||
// boundary color to the center color as it moves from the boundary to the center point.
|
||||
centerColor = readARGB(leis.readInt());
|
||||
|
||||
int size = 3*LittleEndianConsts.INT_SIZE;
|
||||
size += readPointF(leis, centerPoint);
|
||||
|
||||
// An unsigned 32-bit integer that specifies the number of colors specified in the SurroundingColor field.
|
||||
// The surrounding colors are colors specified for discrete points on the boundary of the brush.
|
||||
final int colorCount = leis.readInt();
|
||||
|
||||
// An array of SurroundingColorCount EmfPlusARGB objects that specify the colors for discrete points on the
|
||||
// boundary of the brush.
|
||||
surroundingColor = new Color[colorCount];
|
||||
for (int i=0; i<colorCount; i++) {
|
||||
surroundingColor[i] = readARGB(leis.readInt());
|
||||
}
|
||||
size += (colorCount+1) * LittleEndianConsts.INT_SIZE;
|
||||
|
||||
// The boundary of the path gradient brush, which is specified by either a path or a closed cardinal spline.
|
||||
// If the BrushDataPath flag is set in the BrushDataFlags field, this field MUST contain an
|
||||
// EmfPlusBoundaryPathData object; otherwise, this field MUST contain an EmfPlusBoundaryPointData object.
|
||||
if (PATH.isSet(dataFlags)) {
|
||||
// A 32-bit signed integer that specifies the size in bytes of the BoundaryPathData field.
|
||||
int pathDataSize = leis.readInt();
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
|
||||
// An EmfPlusPath object that specifies the boundary of the brush.
|
||||
size += (boundaryPath = new EmfPlusPath()).init(leis, pathDataSize, EmfPlusObjectType.PATH, 0);
|
||||
} else {
|
||||
// A 32-bit signed integer that specifies the number of points in the BoundaryPointData field.
|
||||
int pointCount = leis.readInt();
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
|
||||
// An array of BoundaryPointCount EmfPlusPointF objects that specify the boundary of the brush.
|
||||
boundaryPoints = new Point2D[pointCount];
|
||||
for (int i=0; i<pointCount; i++) {
|
||||
size += readPointF(leis, boundaryPoints[i] = new Point2D.Double());
|
||||
}
|
||||
}
|
||||
|
||||
// An optional EmfPlusTransformMatrix object that specifies a world space to device space transform for
|
||||
// the path gradient brush. This field MUST be present if the BrushDataTransform flag is set in the
|
||||
// BrushDataFlags field of the EmfPlusPathGradientBrushData object.
|
||||
if (TRANSFORM.isSet(dataFlags)) {
|
||||
size += readXForm(leis, (transform = new AffineTransform()));
|
||||
}
|
||||
|
||||
// An optional blend pattern for the path gradient brush. If this field is present, it MUST contain either
|
||||
// an EmfPlusBlendColors object, or an EmfPlusBlendFactors object, but it MUST NOT contain both.
|
||||
final boolean isPreset = PRESET_COLORS.isSet(dataFlags);
|
||||
final boolean blendH = BLEND_FACTORS_H.isSet(dataFlags);
|
||||
if (isPreset && blendH) {
|
||||
throw new RuntimeException("invalid combination of preset colors and blend factors h");
|
||||
}
|
||||
|
||||
size += (isPreset) ? readColors(leis, d -> positions = d, c -> blendColors = c) : 0;
|
||||
size += (blendH) ? readFactors(leis, d -> positions = d, f -> blendFactorsH = f) : 0;
|
||||
|
||||
// An optional EmfPlusFocusScaleData object that specifies focus scales for the path gradient brush.
|
||||
// This field MUST be present if the BrushDataFocusScales flag is set in the BrushDataFlags field of the
|
||||
// EmfPlusPathGradientBrushData object.
|
||||
if (FOCUS_SCALES.isSet(dataFlags)) {
|
||||
// A 32-bit unsigned integer that specifies the number of focus scales. This value MUST be 2.
|
||||
int focusScaleCount = leis.readInt();
|
||||
if (focusScaleCount != 2) {
|
||||
throw new RuntimeException("invalid focus scale count");
|
||||
}
|
||||
// A floating-point value that defines the horizontal/vertical focus scale.
|
||||
// The focus scale MUST be a value between 0.0 and 1.0, exclusive.
|
||||
focusScaleX = (double)leis.readFloat();
|
||||
focusScaleY = (double)leis.readFloat();
|
||||
size += 3*LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
/** The EmfPlusTextureBrushData object specifies a texture image for a graphics brush. */
|
||||
public static class EmfPlusTextureBrushData implements EmfPlusBrushData {
|
||||
private int dataFlags;
|
||||
private EmfPlusWrapMode wrapMode;
|
||||
private AffineTransform transform;
|
||||
private EmfPlusImage image;
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long dataSize) throws IOException {
|
||||
// A 32-bit unsigned integer that specifies the data in the OptionalData field.
|
||||
// This value MUST be composed of BrushData flags.
|
||||
dataFlags = leis.readInt();
|
||||
|
||||
// A 32-bit signed integer from the WrapMode enumeration that specifies how to repeat the texture image
|
||||
// across a shape, when the image is smaller than the area being filled.
|
||||
wrapMode = EmfPlusWrapMode.valueOf(leis.readInt());
|
||||
|
||||
int size = 2*LittleEndianConsts.INT_SIZE;
|
||||
|
||||
if (TRANSFORM.isSet(dataFlags)) {
|
||||
size += readXForm(leis, (transform = new AffineTransform()));
|
||||
}
|
||||
|
||||
if (dataSize > size) {
|
||||
size += (image = new EmfPlusImage()).init(leis, dataSize-size, EmfPlusObjectType.IMAGE, 0);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
private static int readPositions(LittleEndianInputStream leis, Consumer<double[]> pos) {
|
||||
final int count = leis.readInt();
|
||||
int size = LittleEndianConsts.INT_SIZE;
|
||||
|
||||
double[] positions = new double[count];
|
||||
for (int i=0; i<count; i++) {
|
||||
positions[i] = leis.readFloat();
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
pos.accept(positions);
|
||||
return size;
|
||||
}
|
||||
|
||||
private static int readColors(LittleEndianInputStream leis, Consumer<double[]> pos, Consumer<Color[]> cols) {
|
||||
int[] count = { 0 };
|
||||
int size = readPositions(leis, p -> { count[0] = p.length; pos.accept(p); });
|
||||
Color[] colors = new Color[count[0]];
|
||||
for (int i=0; i<colors.length; i++) {
|
||||
colors[i] = readARGB(leis.readInt());
|
||||
}
|
||||
cols.accept(colors);
|
||||
return size + colors.length * LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
private static int readFactors(LittleEndianInputStream leis, Consumer<double[]> pos, Consumer<double[]> facs) {
|
||||
int[] count = { 0 };
|
||||
int size = readPositions(leis, p -> { count[0] = p.length; pos.accept(p); });
|
||||
double[] factors = new double[count[0]];
|
||||
for (int i=0; i<factors.length; i++) {
|
||||
factors[i] = leis.readFloat();
|
||||
}
|
||||
facs.accept(factors);
|
||||
return size + factors.length * LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
}
|
|
@ -35,7 +35,6 @@ import org.apache.poi.hemf.record.emfplus.HemfPlusMisc.EmfPlusObjectId;
|
|||
import org.apache.poi.util.BitField;
|
||||
import org.apache.poi.util.BitFieldFactory;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.LittleEndianConsts;
|
||||
import org.apache.poi.util.LittleEndianInputStream;
|
||||
import org.apache.poi.util.StringUtil;
|
||||
|
@ -43,7 +42,7 @@ import org.apache.poi.util.StringUtil;
|
|||
public class HemfPlusDraw {
|
||||
private static final int MAX_OBJECT_SIZE = 1_000_000;
|
||||
|
||||
public enum UnitType {
|
||||
public enum EmfPlusUnitType {
|
||||
/** Specifies a unit of logical distance within the world space. */
|
||||
World(0x00),
|
||||
/** Specifies a unit of distance based on the characteristics of the physical display. */
|
||||
|
@ -62,12 +61,12 @@ public class HemfPlusDraw {
|
|||
|
||||
public final int id;
|
||||
|
||||
UnitType(int id) {
|
||||
EmfPlusUnitType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static UnitType valueOf(int id) {
|
||||
for (UnitType wrt : values()) {
|
||||
public static EmfPlusUnitType valueOf(int id) {
|
||||
for (EmfPlusUnitType wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
|
@ -98,6 +97,25 @@ public class HemfPlusDraw {
|
|||
}
|
||||
}
|
||||
|
||||
public interface EmfPlusRelativePosition {
|
||||
/**
|
||||
* This bit indicates whether the PointData field specifies relative or absolute locations.
|
||||
* If set, each element in PointData specifies a location in the coordinate space that is relative to the
|
||||
* location specified by the previous element in the array. In the case of the first element in PointData,
|
||||
* a previous location at coordinates (0,0) is assumed.
|
||||
* If clear, PointData specifies absolute locations according to the {@link #isCompressed()} flag.
|
||||
*
|
||||
* Note If this flag is set, the {@link #isCompressed()} flag (above) is undefined and MUST be ignored.
|
||||
*/
|
||||
BitField POSITION = BitFieldFactory.getInstance(0x0800);
|
||||
|
||||
int getFlags();
|
||||
|
||||
default boolean isRelativePosition() {
|
||||
return POSITION.isSet(getFlags());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
@ -182,27 +200,16 @@ public class HemfPlusDraw {
|
|||
}
|
||||
}
|
||||
|
||||
public static class EmfPlusDrawImagePoints implements HemfPlusRecord, EmfPlusObjectId, EmfPlusCompressed {
|
||||
public static class EmfPlusDrawImagePoints implements HemfPlusRecord, EmfPlusObjectId, EmfPlusCompressed, EmfPlusRelativePosition {
|
||||
/**
|
||||
* This bit indicates that the rendering of the image includes applying an effect.
|
||||
* If set, an object of the Effect class MUST have been specified in an earlier EmfPlusSerializableObject record.
|
||||
*/
|
||||
private static final BitField EFFECT = BitFieldFactory.getInstance(0x2000);
|
||||
|
||||
/**
|
||||
* This bit indicates whether the PointData field specifies relative or absolute locations.
|
||||
* If set, each element in PointData specifies a location in the coordinate space that is relative to the
|
||||
* location specified by the previous element in the array. In the case of the first element in PointData,
|
||||
* a previous location at coordinates (0,0) is assumed.
|
||||
* If clear, PointData specifies absolute locations according to the {@link #isCompressed()} flag.
|
||||
*
|
||||
* Note If this flag is set, the {@link #isCompressed()} flag (above) is undefined and MUST be ignored.
|
||||
*/
|
||||
private static final BitField POSITION = BitFieldFactory.getInstance(0x0800);
|
||||
|
||||
private int flags;
|
||||
private int imageAttributesID;
|
||||
private UnitType srcUnit;
|
||||
private EmfPlusUnitType srcUnit;
|
||||
private final Rectangle2D srcRect = new Rectangle2D.Double();
|
||||
private final Point2D upperLeft = new Point2D.Double();
|
||||
private final Point2D lowerRight = new Point2D.Double();
|
||||
|
@ -229,8 +236,8 @@ public class HemfPlusDraw {
|
|||
|
||||
// A 32-bit signed integer that defines the units of the SrcRect field.
|
||||
// It MUST be the UnitPixel value of the UnitType enumeration
|
||||
srcUnit = UnitType.valueOf(leis.readInt());
|
||||
assert(srcUnit == UnitType.Pixel);
|
||||
srcUnit = EmfPlusUnitType.valueOf(leis.readInt());
|
||||
assert(srcUnit == EmfPlusUnitType.Pixel);
|
||||
|
||||
int size = 2 * LittleEndianConsts.INT_SIZE;
|
||||
|
||||
|
@ -245,7 +252,7 @@ public class HemfPlusDraw {
|
|||
|
||||
BiFunction<LittleEndianInputStream, Point2D, Integer> readPoint;
|
||||
|
||||
if (POSITION.isSet(flags)) {
|
||||
if (isRelativePosition()) {
|
||||
// If the POSITION flag is set in the Flags, the points specify relative locations.
|
||||
readPoint = HemfPlusDraw::readPointR;
|
||||
} else if (isCompressed()) {
|
||||
|
@ -301,7 +308,7 @@ public class HemfPlusDraw {
|
|||
public static class EmfPlusDrawImage implements HemfPlusRecord, EmfPlusObjectId, EmfPlusCompressed {
|
||||
private int flags;
|
||||
private int imageAttributesID;
|
||||
private UnitType srcUnit;
|
||||
private EmfPlusUnitType srcUnit;
|
||||
private final Rectangle2D srcRect = new Rectangle2D.Double();
|
||||
private final Rectangle2D rectData = new Rectangle2D.Double();
|
||||
|
||||
|
@ -325,8 +332,8 @@ public class HemfPlusDraw {
|
|||
|
||||
// A 32-bit signed integer that defines the units of the SrcRect field.
|
||||
// It MUST be the UnitPixel value of the UnitType enumeration
|
||||
srcUnit = UnitType.valueOf(leis.readInt());
|
||||
assert(srcUnit == UnitType.Pixel);
|
||||
srcUnit = EmfPlusUnitType.valueOf(leis.readInt());
|
||||
assert(srcUnit == EmfPlusUnitType.Pixel);
|
||||
|
||||
int size = 2 * LittleEndianConsts.INT_SIZE;
|
||||
|
||||
|
@ -347,7 +354,7 @@ public class HemfPlusDraw {
|
|||
private static final BitField SOLID_COLOR = BitFieldFactory.getInstance(0x8000);
|
||||
|
||||
private int flags;
|
||||
private final byte[] brushId = new byte[LittleEndianConsts.INT_SIZE];
|
||||
private int brushId;
|
||||
|
||||
@Override
|
||||
public HemfPlusRecordType getEmfPlusRecordType() {
|
||||
|
@ -364,11 +371,11 @@ public class HemfPlusDraw {
|
|||
}
|
||||
|
||||
public int getBrushId() {
|
||||
return (isSolidColor()) ? -1 : LittleEndian.getInt(brushId);
|
||||
return (isSolidColor()) ? -1 : brushId;
|
||||
}
|
||||
|
||||
public Color getSolidColor() {
|
||||
return (isSolidColor()) ? new Color(brushId[2], brushId[1], brushId[0], brushId[3]) : null;
|
||||
return (isSolidColor()) ? readARGB(brushId) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -379,7 +386,7 @@ public class HemfPlusDraw {
|
|||
// the SOLID_COLOR bit in the Flags field.
|
||||
// If SOLID_COLOR is set, BrushId specifies a color as an EmfPlusARGB object.
|
||||
// If clear, BrushId contains the index of an EmfPlusBrush object in the EMF+ Object Table.
|
||||
leis.readFully(brushId);
|
||||
brushId = leis.readInt();
|
||||
|
||||
return LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
@ -606,4 +613,9 @@ public class HemfPlusDraw {
|
|||
value[0] = ((value[0] << 8) | leis.readByte()) & 0x7FFF;
|
||||
return LittleEndianConsts.SHORT_SIZE;
|
||||
}
|
||||
|
||||
static Color readARGB(int argb) {
|
||||
return new Color( (argb >>> 8) & 0xFF, (argb >>> 16) & 0xFF, (argb >>> 24) & 0xFF, argb & 0xFF);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
/* ====================================================================
|
||||
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.hemf.record.emfplus;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusDraw.EmfPlusUnitType;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusHeader.EmfPlusGraphicsVersion;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectData;
|
||||
import org.apache.poi.util.BitField;
|
||||
import org.apache.poi.util.BitFieldFactory;
|
||||
import org.apache.poi.util.LittleEndianConsts;
|
||||
import org.apache.poi.util.LittleEndianInputStream;
|
||||
import org.apache.poi.util.StringUtil;
|
||||
|
||||
public class HemfPlusFont {
|
||||
|
||||
|
||||
public static class EmfPlusFont implements EmfPlusObjectData {
|
||||
/**
|
||||
* If set, the font typeface MUST be rendered with a heavier weight or thickness.
|
||||
* If clear, the font typeface MUST be rendered with a normal thickness.
|
||||
*/
|
||||
private static final BitField BOLD = BitFieldFactory.getInstance(0x00000001);
|
||||
|
||||
/**
|
||||
* If set, the font typeface MUST be rendered with the vertical stems of the characters at an increased angle
|
||||
* or slant relative to the baseline.
|
||||
*
|
||||
* If clear, the font typeface MUST be rendered with the vertical stems of the characters at a normal angle.
|
||||
*/
|
||||
private static final BitField ITALIC = BitFieldFactory.getInstance(0x00000002);
|
||||
|
||||
/**
|
||||
* If set, the font typeface MUST be rendered with a line underneath the baseline of the characters.
|
||||
* If clear, the font typeface MUST be rendered without a line underneath the baseline.
|
||||
*/
|
||||
private static final BitField UNDERLINE = BitFieldFactory.getInstance(0x00000004);
|
||||
|
||||
/**
|
||||
* If set, the font typeface MUST be rendered with a line parallel to the baseline drawn through the middle of
|
||||
* the characters.
|
||||
* If clear, the font typeface MUST be rendered without a line through the characters.
|
||||
*/
|
||||
private static final BitField STRIKEOUT = BitFieldFactory.getInstance(0x00000008);
|
||||
|
||||
|
||||
private final EmfPlusGraphicsVersion version = new EmfPlusGraphicsVersion();
|
||||
private double emSize;
|
||||
private EmfPlusUnitType sizeUnit;
|
||||
private int styleFlags;
|
||||
private String family;
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long dataSize, HemfPlusObject.EmfPlusObjectType objectType, int flags) throws IOException {
|
||||
// An EmfPlusGraphicsVersion object that specifies the version of operating system graphics that was used
|
||||
// to create this object.
|
||||
long size = version.init(leis);
|
||||
|
||||
// A 32-bit floating-point value that specifies the em size of the font in units specified by the SizeUnit field.
|
||||
emSize = leis.readFloat();
|
||||
|
||||
// A 32-bit unsigned integer that specifies the units used for the EmSize field. These are typically the
|
||||
// units that were employed when designing the font. The value MUST be in the UnitType enumeration
|
||||
sizeUnit = EmfPlusUnitType.valueOf(leis.readInt());
|
||||
|
||||
// A 32-bit signed integer that specifies attributes of the character glyphs that affect the appearance of
|
||||
// the font, such as bold and italic. This value MUST be composed of FontStyle flags
|
||||
styleFlags = leis.readInt();
|
||||
|
||||
// A 32-bit unsigned integer that is reserved and MUST be ignored.
|
||||
leis.skipFully(LittleEndianConsts.INT_SIZE);
|
||||
|
||||
// A 32-bit unsigned integer that specifies the number of characters in the FamilyName field.
|
||||
int len = leis.readInt();
|
||||
size += 5*LittleEndianConsts.INT_SIZE;
|
||||
|
||||
// A string of Length Unicode characters that contains the name of the font family.
|
||||
family = StringUtil.readUnicodeLE(leis, len);
|
||||
size += len*LittleEndianConsts.SHORT_SIZE;
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,439 @@
|
|||
/* ====================================================================
|
||||
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.hemf.record.emfplus;
|
||||
|
||||
import static org.apache.poi.hemf.record.emfplus.HemfPlusDraw.readARGB;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusHeader.EmfPlusGraphicsVersion;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectData;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectType;
|
||||
import org.apache.poi.util.BitField;
|
||||
import org.apache.poi.util.BitFieldFactory;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.apache.poi.util.LittleEndianConsts;
|
||||
import org.apache.poi.util.LittleEndianInputStream;
|
||||
|
||||
public class HemfPlusImage {
|
||||
/** The ImageDataType enumeration defines types of image data formats. */
|
||||
public enum EmfPlusImageDataType {
|
||||
/** The type of image is not known. */
|
||||
UNKNOWN(0x00000000),
|
||||
/** Specifies a bitmap image. */
|
||||
BITMAP(0x00000001),
|
||||
/** Specifies a metafile image. */
|
||||
METAFILE(0x00000002),
|
||||
/** POI-specific - marks an unfinished/continuable image part */
|
||||
CONTINUED(-1);
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusImageDataType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusImageDataType valueOf(int id) {
|
||||
for (EmfPlusImageDataType wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum EmfPlusPixelFormat {
|
||||
UNDEFINED(0X00000000),
|
||||
INDEXED_1BPP(0X00030101),
|
||||
INDEXED_4BPP(0X00030402),
|
||||
INDEXED_8BPP(0X00030803),
|
||||
GRAYSCALE_16BPP(0X00101004),
|
||||
RGB555_16BPP(0X00021005),
|
||||
RGB565_16BPP(0X00021006),
|
||||
ARGB1555_16BPP(0X00061007),
|
||||
RGB_24BPP(0X00021808),
|
||||
RGB_32BPP(0X00022009),
|
||||
ARGB_32BPP(0X0026200A),
|
||||
PARGB_32BPP(0X000E200B),
|
||||
RGB_48BPP(0X0010300C),
|
||||
ARGB_64BPP(0X0034400D),
|
||||
PARGB_64BPP(0X001A400E),
|
||||
;
|
||||
|
||||
private static final BitField CANONICAL = BitFieldFactory.getInstance(0x00200000);
|
||||
private static final BitField EXTCOLORS = BitFieldFactory.getInstance(0x00100000);
|
||||
private static final BitField PREMULTI = BitFieldFactory.getInstance(0x00080000);
|
||||
private static final BitField ALPHA = BitFieldFactory.getInstance(0x00040000);
|
||||
private static final BitField GDI = BitFieldFactory.getInstance(0x00020000);
|
||||
private static final BitField PALETTE = BitFieldFactory.getInstance(0x00010000);
|
||||
private static final BitField BPP = BitFieldFactory.getInstance(0x0000FF00);
|
||||
private static final BitField INDEX = BitFieldFactory.getInstance(0x000000FF);
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusPixelFormat(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusPixelFormat valueOf(int id) {
|
||||
for (EmfPlusPixelFormat wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The pixel format enumeration index.
|
||||
*/
|
||||
public int getGDIEnumIndex() {
|
||||
return id == -1 ? -1 : INDEX.getValue(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* The total number of bits per pixel.
|
||||
*/
|
||||
public int getBitsPerPixel() {
|
||||
return id == -1 ? -1 : BPP.getValue(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, the pixel values are indexes into a palette.
|
||||
* If clear, the pixel values are actual colors.
|
||||
*/
|
||||
public boolean isPaletteIndexed() {
|
||||
return id != -1 && PALETTE.isSet(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, the pixel format is supported in Windows GDI.
|
||||
* If clear, the pixel format is not supported in Windows GDI.
|
||||
*/
|
||||
public boolean isGDISupported() {
|
||||
return id != -1 && GDI.isSet(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, the pixel format includes an alpha transparency component.
|
||||
* If clear, the pixel format does not include a component that specifies transparency.
|
||||
*/
|
||||
public boolean isAlpha() {
|
||||
return id != -1 && ALPHA.isSet(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, each color component in the pixel has been premultiplied by the pixel's alpha transparency value.
|
||||
* If clear, each color component is multiplied by the pixel's alpha transparency value when the source pixel
|
||||
* is blended with the destination pixel.
|
||||
*/
|
||||
public boolean isPreMultiplied() {
|
||||
return id != -1 && PREMULTI.isSet(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, the pixel format supports extended colors in 16-bits per channel.
|
||||
* If clear, extended colors are not supported.
|
||||
*/
|
||||
public boolean isExtendedColors() {
|
||||
return id != -1 && EXTCOLORS.isSet(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, the pixel format is "canonical", which means that 32 bits per pixel are
|
||||
* supported, with 24-bits for color components and an 8-bit alpha channel.
|
||||
* If clear, the pixel format is not canonical.
|
||||
*/
|
||||
public boolean isCanonical() {
|
||||
return id != -1 && CANONICAL.isSet(id);
|
||||
}
|
||||
}
|
||||
|
||||
public enum EmfPlusBitmapDataType {
|
||||
PIXEL(0x00000000),
|
||||
COMPRESSED(0x00000001);
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusBitmapDataType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusBitmapDataType valueOf(int id) {
|
||||
for (EmfPlusBitmapDataType wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum EmfPlusMetafileDataType {
|
||||
Wmf(0x00000001),
|
||||
WmfPlaceable(0x00000002),
|
||||
Emf(0x00000003),
|
||||
EmfPlusOnly(0x00000004),
|
||||
EmfPlusDual(0x00000005);
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusMetafileDataType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusMetafileDataType valueOf(int id) {
|
||||
for (EmfPlusMetafileDataType wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The WrapMode enumeration defines how the pattern from a texture or gradient brush is tiled
|
||||
* across a shape or at shape boundaries, when it is smaller than the area being filled.
|
||||
*/
|
||||
public enum EmfPlusWrapMode {
|
||||
WRAP_MODE_TILE(0x00000000),
|
||||
WRAP_MODE_TILE_FLIP_X(0x00000001),
|
||||
WRAP_MODE_TILE_FLIP_Y(0x00000002),
|
||||
WRAP_MODE_TILE_FLIP_XY(0x00000003),
|
||||
WRAP_MODE_CLAMP(0x00000004)
|
||||
;
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusWrapMode(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusWrapMode valueOf(int id) {
|
||||
for (EmfPlusWrapMode wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum EmfPlusObjectClamp {
|
||||
/** The object is clamped to a rectangle. */
|
||||
RectClamp(0x00000000),
|
||||
/** The object is clamped to a bitmap. */
|
||||
BitmapClamp(0x00000001)
|
||||
;
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusObjectClamp(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusObjectClamp valueOf(int id) {
|
||||
for (EmfPlusObjectClamp wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class EmfPlusImage implements EmfPlusObjectData {
|
||||
private static final int MAX_OBJECT_SIZE = 50_000_000;
|
||||
|
||||
|
||||
private final EmfPlusGraphicsVersion graphicsVersion = new EmfPlusGraphicsVersion();
|
||||
private EmfPlusImageDataType imageDataType;
|
||||
private int bitmapWidth;
|
||||
private int bitmapHeight;
|
||||
private int bitmapStride;
|
||||
private EmfPlusPixelFormat pixelFormat;
|
||||
private EmfPlusBitmapDataType bitmapType;
|
||||
private byte[] imageData;
|
||||
private EmfPlusMetafileDataType metafileType;
|
||||
private int metafileDataSize;
|
||||
|
||||
public EmfPlusImageDataType getImageDataType() {
|
||||
return imageDataType;
|
||||
}
|
||||
|
||||
public byte[] getImageData() {
|
||||
return imageData;
|
||||
}
|
||||
|
||||
public EmfPlusPixelFormat getPixelFormat() {
|
||||
return pixelFormat;
|
||||
}
|
||||
|
||||
public EmfPlusBitmapDataType getBitmapType() {
|
||||
return bitmapType;
|
||||
}
|
||||
|
||||
public int getBitmapWidth() {
|
||||
return bitmapWidth;
|
||||
}
|
||||
|
||||
public int getBitmapHeight() {
|
||||
return bitmapHeight;
|
||||
}
|
||||
|
||||
public int getBitmapStride() {
|
||||
return bitmapStride;
|
||||
}
|
||||
|
||||
public EmfPlusMetafileDataType getMetafileType() {
|
||||
return metafileType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long dataSize, EmfPlusObjectType objectType, int flags) throws IOException {
|
||||
leis.mark(LittleEndianConsts.INT_SIZE);
|
||||
long size = graphicsVersion.init(leis);
|
||||
|
||||
if (graphicsVersion.getGraphicsVersion() == null || graphicsVersion.getMetafileSignature() != 0xDBC01) {
|
||||
// CONTINUABLE is not always correctly set, so we check the version field if this record is continued
|
||||
imageDataType = EmfPlusImageDataType.CONTINUED;
|
||||
leis.reset();
|
||||
size = 0;
|
||||
} else {
|
||||
imageDataType = EmfPlusImageDataType.valueOf(leis.readInt());
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
if (imageDataType == null) {
|
||||
imageDataType = EmfPlusImageDataType.UNKNOWN;
|
||||
}
|
||||
|
||||
int fileSize;
|
||||
switch (imageDataType) {
|
||||
default:
|
||||
case UNKNOWN:
|
||||
case CONTINUED:
|
||||
bitmapWidth = -1;
|
||||
bitmapHeight = -1;
|
||||
bitmapStride = -1;
|
||||
bitmapType = null;
|
||||
pixelFormat = null;
|
||||
|
||||
fileSize = (int) (dataSize);
|
||||
break;
|
||||
|
||||
case BITMAP:
|
||||
// A 32-bit signed integer that specifies the width in pixels of the area occupied by the bitmap.
|
||||
// If the image is compressed, according to the Type field, this value is undefined and MUST be ignored.
|
||||
bitmapWidth = leis.readInt();
|
||||
// A 32-bit signed integer that specifies the height in pixels of the area occupied by the bitmap.
|
||||
// If the image is compressed, according to the Type field, this value is undefined and MUST be ignored.
|
||||
bitmapHeight = leis.readInt();
|
||||
// A 32-bit signed integer that specifies the byte offset between the beginning of one scan-line
|
||||
// and the next. This value is the number of bytes per pixel, which is specified in the PixelFormat
|
||||
// field, multiplied by the width in pixels, which is specified in the Width field.
|
||||
// The value of this field MUST be a multiple of four. If the image is compressed, according to the
|
||||
// Type field, this value is undefined and MUST be ignored.
|
||||
bitmapStride = leis.readInt();
|
||||
// A 32-bit unsigned integer that specifies the format of the pixels that make up the bitmap image.
|
||||
// The supported pixel formats are specified in the PixelFormat enumeration
|
||||
int pixelFormatInt = leis.readInt();
|
||||
// A 32-bit unsigned integer that specifies the metafileType of data in the BitmapData field.
|
||||
// This value MUST be defined in the BitmapDataType enumeration
|
||||
bitmapType = EmfPlusBitmapDataType.valueOf(leis.readInt());
|
||||
size += 5 * LittleEndianConsts.INT_SIZE;
|
||||
|
||||
pixelFormat = (bitmapType == EmfPlusBitmapDataType.PIXEL)
|
||||
? EmfPlusPixelFormat.valueOf(pixelFormatInt)
|
||||
: EmfPlusPixelFormat.UNDEFINED;
|
||||
assert (pixelFormat != null);
|
||||
|
||||
fileSize = (int) (dataSize - size);
|
||||
|
||||
break;
|
||||
|
||||
case METAFILE:
|
||||
// A 32-bit unsigned integer that specifies the type of metafile that is embedded in the
|
||||
// MetafileData field. This value MUST be defined in the MetafileDataType enumeration
|
||||
metafileType = EmfPlusMetafileDataType.valueOf(leis.readInt());
|
||||
|
||||
// A 32-bit unsigned integer that specifies the size in bytes of the
|
||||
// metafile data in the MetafileData field.
|
||||
metafileDataSize = leis.readInt();
|
||||
|
||||
size += 2 * LittleEndianConsts.INT_SIZE;
|
||||
|
||||
// ignore metafileDataSize, which might ignore a (placeable) header in front
|
||||
// and also use the remaining bytes, which might contain padding bytes ...
|
||||
fileSize = (int) (dataSize - size);
|
||||
break;
|
||||
}
|
||||
|
||||
assert (fileSize <= dataSize - size);
|
||||
|
||||
imageData = IOUtils.toByteArray(leis, fileSize, MAX_OBJECT_SIZE);
|
||||
|
||||
// TODO: remove padding bytes between placeable WMF header and body?
|
||||
|
||||
return size + fileSize;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EmfPlusImageAttributes implements EmfPlusObjectData {
|
||||
private final EmfPlusGraphicsVersion graphicsVersion = new EmfPlusGraphicsVersion();
|
||||
private EmfPlusWrapMode wrapMode;
|
||||
private Color clampColor;
|
||||
private EmfPlusObjectClamp objectClamp;
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long dataSize, EmfPlusObjectType objectType, int flags) throws IOException {
|
||||
// An EmfPlusGraphicsVersion object that specifies the version of operating system graphics that
|
||||
// was used to create this object.
|
||||
long size = graphicsVersion.init(leis);
|
||||
|
||||
// A 32-bit field that is not used and MUST be ignored.
|
||||
leis.skipFully(LittleEndianConsts.INT_SIZE);
|
||||
|
||||
// A 32-bit unsigned integer that specifies how to handle edge conditions with a value from the WrapMode enumeration
|
||||
wrapMode = EmfPlusWrapMode.valueOf(leis.readInt());
|
||||
|
||||
// An EmfPlusARGB object that specifies the edge color to use when the WrapMode value is WrapModeClamp.
|
||||
// This color is visible when the source rectangle processed by an EmfPlusDrawImage record is larger than the image itself.
|
||||
clampColor = readARGB(leis.readInt());
|
||||
|
||||
// A 32-bit signed integer that specifies the object clamping behavior. It is not used until this object
|
||||
// is applied to an image being drawn. This value MUST be one of the values defined in the following table.
|
||||
objectClamp = EmfPlusObjectClamp.valueOf(leis.readInt());
|
||||
|
||||
// A value that SHOULD be set to zero and MUST be ignored upon receipt.
|
||||
leis.skipFully(LittleEndianConsts.INT_SIZE);
|
||||
|
||||
return size + 5*LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
public EmfPlusGraphicsVersion getGraphicsVersion() {
|
||||
return graphicsVersion;
|
||||
}
|
||||
|
||||
public EmfPlusWrapMode getWrapMode() {
|
||||
return wrapMode;
|
||||
}
|
||||
|
||||
public Color getClampColor() {
|
||||
return clampColor;
|
||||
}
|
||||
|
||||
public EmfPlusObjectClamp getObjectClamp() {
|
||||
return objectClamp;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -17,12 +17,18 @@
|
|||
|
||||
package org.apache.poi.hemf.record.emfplus;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.IOException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusBrush.EmfPlusBrush;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusFont.EmfPlusFont;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusHeader.EmfPlusGraphicsVersion;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusImage.EmfPlusImage;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusImage.EmfPlusImageAttributes;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusMisc.EmfPlusObjectId;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusPath.EmfPlusPath;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusPen.EmfPlusPen;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusRegion.EmfPlusRegion;
|
||||
import org.apache.poi.util.BitField;
|
||||
import org.apache.poi.util.BitFieldFactory;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
|
@ -43,19 +49,19 @@ public class HemfPlusObject {
|
|||
/**
|
||||
* Brush objects fill graphics regions.
|
||||
*/
|
||||
BRUSH(0x00000001, EmfPlusUnknownData::new),
|
||||
BRUSH(0x00000001, EmfPlusBrush::new),
|
||||
/**
|
||||
* Pen objects draw graphics lines.
|
||||
*/
|
||||
PEN(0x00000002, EmfPlusUnknownData::new),
|
||||
PEN(0x00000002, EmfPlusPen::new),
|
||||
/**
|
||||
* Path objects specify sequences of lines, curves, and shapes.
|
||||
*/
|
||||
PATH(0x00000003, EmfPlusUnknownData::new),
|
||||
PATH(0x00000003, EmfPlusPath::new),
|
||||
/**
|
||||
* Region objects specify areas of the output surface.
|
||||
*/
|
||||
REGION(0x00000004, EmfPlusUnknownData::new),
|
||||
REGION(0x00000004, EmfPlusRegion::new),
|
||||
/**
|
||||
* Image objects encapsulate bitmaps and metafiles.
|
||||
*/
|
||||
|
@ -63,7 +69,7 @@ public class HemfPlusObject {
|
|||
/**
|
||||
* Font objects specify font properties, including typeface style, em size, and font family.
|
||||
*/
|
||||
FONT(0x00000006, EmfPlusUnknownData::new),
|
||||
FONT(0x00000006, EmfPlusFont::new),
|
||||
/**
|
||||
* String format objects specify text layout, including alignment, orientation, tab stops, clipping,
|
||||
* and digit substitution for languages that do not use Western European digits.
|
||||
|
@ -96,220 +102,10 @@ public class HemfPlusObject {
|
|||
}
|
||||
}
|
||||
|
||||
public enum EmfPlusImageDataType {
|
||||
UNKNOWN(0x00000000),
|
||||
BITMAP(0x00000001),
|
||||
METAFILE(0x00000002),
|
||||
CONTINUED(-1);
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusImageDataType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusImageDataType valueOf(int id) {
|
||||
for (EmfPlusImageDataType wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum EmfPlusBitmapDataType {
|
||||
PIXEL(0x00000000),
|
||||
COMPRESSED(0x00000001);
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusBitmapDataType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusBitmapDataType valueOf(int id) {
|
||||
for (EmfPlusBitmapDataType wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum EmfPlusPixelFormat {
|
||||
UNDEFINED(0X00000000),
|
||||
INDEXED_1BPP(0X00030101),
|
||||
INDEXED_4BPP(0X00030402),
|
||||
INDEXED_8BPP(0X00030803),
|
||||
GRAYSCALE_16BPP(0X00101004),
|
||||
RGB555_16BPP(0X00021005),
|
||||
RGB565_16BPP(0X00021006),
|
||||
ARGB1555_16BPP(0X00061007),
|
||||
RGB_24BPP(0X00021808),
|
||||
RGB_32BPP(0X00022009),
|
||||
ARGB_32BPP(0X0026200A),
|
||||
PARGB_32BPP(0X000E200B),
|
||||
RGB_48BPP(0X0010300C),
|
||||
ARGB_64BPP(0X0034400D),
|
||||
PARGB_64BPP(0X001A400E),
|
||||
;
|
||||
|
||||
private static final BitField CANONICAL = BitFieldFactory.getInstance(0x00200000);
|
||||
private static final BitField EXTCOLORS = BitFieldFactory.getInstance(0x00100000);
|
||||
private static final BitField PREMULTI = BitFieldFactory.getInstance(0x00080000);
|
||||
private static final BitField ALPHA = BitFieldFactory.getInstance(0x00040000);
|
||||
private static final BitField GDI = BitFieldFactory.getInstance(0x00020000);
|
||||
private static final BitField PALETTE = BitFieldFactory.getInstance(0x00010000);
|
||||
private static final BitField BPP = BitFieldFactory.getInstance(0x0000FF00);
|
||||
private static final BitField INDEX = BitFieldFactory.getInstance(0x000000FF);
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusPixelFormat(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusPixelFormat valueOf(int id) {
|
||||
for (EmfPlusPixelFormat wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The pixel format enumeration index.
|
||||
*/
|
||||
public int getGDIEnumIndex() {
|
||||
return id == -1 ? -1 : INDEX.getValue(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* The total number of bits per pixel.
|
||||
*/
|
||||
public int getBitsPerPixel() {
|
||||
return id == -1 ? -1 : BPP.getValue(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, the pixel values are indexes into a palette.
|
||||
* If clear, the pixel values are actual colors.
|
||||
*/
|
||||
public boolean isPaletteIndexed() {
|
||||
return id != -1 && PALETTE.isSet(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, the pixel format is supported in Windows GDI.
|
||||
* If clear, the pixel format is not supported in Windows GDI.
|
||||
*/
|
||||
public boolean isGDISupported() {
|
||||
return id != -1 && GDI.isSet(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, the pixel format includes an alpha transparency component.
|
||||
* If clear, the pixel format does not include a component that specifies transparency.
|
||||
*/
|
||||
public boolean isAlpha() {
|
||||
return id != -1 && ALPHA.isSet(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, each color component in the pixel has been premultiplied by the pixel's alpha transparency value.
|
||||
* If clear, each color component is multiplied by the pixel's alpha transparency value when the source pixel
|
||||
* is blended with the destination pixel.
|
||||
*/
|
||||
public boolean isPreMultiplied() {
|
||||
return id != -1 && PREMULTI.isSet(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, the pixel format supports extended colors in 16-bits per channel.
|
||||
* If clear, extended colors are not supported.
|
||||
*/
|
||||
public boolean isExtendedColors() {
|
||||
return id != -1 && EXTCOLORS.isSet(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, the pixel format is "canonical", which means that 32 bits per pixel are
|
||||
* supported, with 24-bits for color components and an 8-bit alpha channel.
|
||||
* If clear, the pixel format is not canonical.
|
||||
*/
|
||||
public boolean isCanonical() {
|
||||
return id != -1 && CANONICAL.isSet(id);
|
||||
}
|
||||
}
|
||||
|
||||
public enum EmfPlusMetafileDataType {
|
||||
Wmf(0x00000001),
|
||||
WmfPlaceable(0x00000002),
|
||||
Emf(0x00000003),
|
||||
EmfPlusOnly(0x00000004),
|
||||
EmfPlusDual(0x00000005);
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusMetafileDataType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusMetafileDataType valueOf(int id) {
|
||||
for (EmfPlusMetafileDataType wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The WrapMode enumeration defines how the pattern from a texture or gradient brush is tiled
|
||||
* across a shape or at shape boundaries, when it is smaller than the area being filled.
|
||||
*/
|
||||
public enum EmfPlusWrapMode {
|
||||
WRAP_MODE_TILE(0x00000000),
|
||||
WRAP_MODE_TILE_FLIP_X(0x00000001),
|
||||
WRAP_MODE_TILE_FLIP_Y(0x00000002),
|
||||
WRAP_MODE_TILE_FLIP_XY(0x00000003),
|
||||
WRAP_MODE_CLAMP(0x00000004)
|
||||
;
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusWrapMode(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusWrapMode valueOf(int id) {
|
||||
for (EmfPlusWrapMode wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum EmfPlusObjectClamp {
|
||||
/** The object is clamped to a rectangle. */
|
||||
RectClamp(0x00000000),
|
||||
/** The object is clamped to a bitmap. */
|
||||
BitmapClamp(0x00000001)
|
||||
;
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusObjectClamp(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusObjectClamp valueOf(int id) {
|
||||
for (EmfPlusObjectClamp wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The EmfPlusObject record specifies an object for use in graphics operations. The object definition
|
||||
* can span multiple records, which is indicated by the value of the Flags field.
|
||||
* can span multiple records), which is indicated by the value of the Flags field.
|
||||
*/
|
||||
public static class EmfPlusObject implements HemfPlusRecord, EmfPlusObjectId {
|
||||
|
||||
|
@ -400,189 +196,4 @@ public class HemfPlusObject {
|
|||
return dataSize;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EmfPlusImage implements EmfPlusObjectData {
|
||||
private final EmfPlusGraphicsVersion graphicsVersion = new EmfPlusGraphicsVersion();
|
||||
private EmfPlusImageDataType imageDataType;
|
||||
private int bitmapWidth;
|
||||
private int bitmapHeight;
|
||||
private int bitmapStride;
|
||||
private EmfPlusPixelFormat pixelFormat;
|
||||
private EmfPlusBitmapDataType bitmapType;
|
||||
private byte[] imageData;
|
||||
private EmfPlusMetafileDataType metafileType;
|
||||
private int metafileDataSize;
|
||||
|
||||
public EmfPlusImageDataType getImageDataType() {
|
||||
return imageDataType;
|
||||
}
|
||||
|
||||
public byte[] getImageData() {
|
||||
return imageData;
|
||||
}
|
||||
|
||||
public EmfPlusPixelFormat getPixelFormat() {
|
||||
return pixelFormat;
|
||||
}
|
||||
|
||||
public EmfPlusBitmapDataType getBitmapType() {
|
||||
return bitmapType;
|
||||
}
|
||||
|
||||
public int getBitmapWidth() {
|
||||
return bitmapWidth;
|
||||
}
|
||||
|
||||
public int getBitmapHeight() {
|
||||
return bitmapHeight;
|
||||
}
|
||||
|
||||
public int getBitmapStride() {
|
||||
return bitmapStride;
|
||||
}
|
||||
|
||||
public EmfPlusMetafileDataType getMetafileType() {
|
||||
return metafileType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long dataSize, EmfPlusObjectType objectType, int flags) throws IOException {
|
||||
leis.mark(LittleEndianConsts.INT_SIZE);
|
||||
long size = graphicsVersion.init(leis);
|
||||
|
||||
if (graphicsVersion.getGraphicsVersion() == null || graphicsVersion.getMetafileSignature() != 0xDBC01) {
|
||||
// CONTINUABLE is not always correctly set, so we check the version field if this record is continued
|
||||
imageDataType = EmfPlusImageDataType.CONTINUED;
|
||||
leis.reset();
|
||||
size = 0;
|
||||
} else {
|
||||
imageDataType = EmfPlusImageDataType.valueOf(leis.readInt());
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
if (imageDataType == null) {
|
||||
imageDataType = EmfPlusImageDataType.UNKNOWN;
|
||||
}
|
||||
|
||||
int fileSize;
|
||||
switch (imageDataType) {
|
||||
default:
|
||||
case UNKNOWN:
|
||||
case CONTINUED:
|
||||
bitmapWidth = -1;
|
||||
bitmapHeight = -1;
|
||||
bitmapStride = -1;
|
||||
bitmapType = null;
|
||||
pixelFormat = null;
|
||||
|
||||
fileSize = (int) (dataSize);
|
||||
break;
|
||||
|
||||
case BITMAP:
|
||||
// A 32-bit signed integer that specifies the width in pixels of the area occupied by the bitmap.
|
||||
// If the image is compressed, according to the Type field, this value is undefined and MUST be ignored.
|
||||
bitmapWidth = leis.readInt();
|
||||
// A 32-bit signed integer that specifies the height in pixels of the area occupied by the bitmap.
|
||||
// If the image is compressed, according to the Type field, this value is undefined and MUST be ignored.
|
||||
bitmapHeight = leis.readInt();
|
||||
// A 32-bit signed integer that specifies the byte offset between the beginning of one scan-line
|
||||
// and the next. This value is the number of bytes per pixel, which is specified in the PixelFormat
|
||||
// field, multiplied by the width in pixels, which is specified in the Width field.
|
||||
// The value of this field MUST be a multiple of four. If the image is compressed, according to the
|
||||
// Type field, this value is undefined and MUST be ignored.
|
||||
bitmapStride = leis.readInt();
|
||||
// A 32-bit unsigned integer that specifies the format of the pixels that make up the bitmap image.
|
||||
// The supported pixel formats are specified in the PixelFormat enumeration
|
||||
int pixelFormatInt = leis.readInt();
|
||||
// A 32-bit unsigned integer that specifies the metafileType of data in the BitmapData field.
|
||||
// This value MUST be defined in the BitmapDataType enumeration
|
||||
bitmapType = EmfPlusBitmapDataType.valueOf(leis.readInt());
|
||||
size += 5 * LittleEndianConsts.INT_SIZE;
|
||||
|
||||
pixelFormat = (bitmapType == EmfPlusBitmapDataType.PIXEL)
|
||||
? EmfPlusPixelFormat.valueOf(pixelFormatInt)
|
||||
: EmfPlusPixelFormat.UNDEFINED;
|
||||
assert (pixelFormat != null);
|
||||
|
||||
fileSize = (int) (dataSize - size);
|
||||
|
||||
break;
|
||||
|
||||
case METAFILE:
|
||||
// A 32-bit unsigned integer that specifies the type of metafile that is embedded in the
|
||||
// MetafileData field. This value MUST be defined in the MetafileDataType enumeration
|
||||
metafileType = EmfPlusMetafileDataType.valueOf(leis.readInt());
|
||||
|
||||
// A 32-bit unsigned integer that specifies the size in bytes of the
|
||||
// metafile data in the MetafileData field.
|
||||
metafileDataSize = leis.readInt();
|
||||
|
||||
size += 2 * LittleEndianConsts.INT_SIZE;
|
||||
|
||||
// ignore metafileDataSize, which might ignore a (placeable) header in front
|
||||
// and also use the remaining bytes, which might contain padding bytes ...
|
||||
fileSize = (int) (dataSize - size);
|
||||
break;
|
||||
}
|
||||
|
||||
assert (fileSize <= dataSize - size);
|
||||
|
||||
imageData = IOUtils.toByteArray(leis, fileSize, MAX_OBJECT_SIZE);
|
||||
|
||||
// TODO: remove padding bytes between placeable WMF header and body?
|
||||
|
||||
return size + fileSize;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EmfPlusImageAttributes implements EmfPlusObjectData {
|
||||
private final EmfPlusGraphicsVersion graphicsVersion = new EmfPlusGraphicsVersion();
|
||||
private EmfPlusWrapMode wrapMode;
|
||||
private Color clampColor;
|
||||
private EmfPlusObjectClamp objectClamp;
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long dataSize, EmfPlusObjectType objectType, int flags) throws IOException {
|
||||
// An EmfPlusGraphicsVersion object that specifies the version of operating system graphics that
|
||||
// was used to create this object.
|
||||
long size = graphicsVersion.init(leis);
|
||||
|
||||
// A 32-bit field that is not used and MUST be ignored.
|
||||
leis.skip(LittleEndianConsts.INT_SIZE);
|
||||
|
||||
// A 32-bit unsigned integer that specifies how to handle edge conditions with a value from the WrapMode enumeration
|
||||
wrapMode = EmfPlusWrapMode.valueOf(leis.readInt());
|
||||
|
||||
// An EmfPlusARGB object that specifies the edge color to use when the WrapMode value is WrapModeClamp.
|
||||
// This color is visible when the source rectangle processed by an EmfPlusDrawImage record is larger than the image itself.
|
||||
byte[] buf = new byte[LittleEndianConsts.INT_SIZE];
|
||||
leis.readFully(buf);
|
||||
clampColor = new Color(buf[2], buf[1], buf[0], buf[3]);
|
||||
|
||||
// A 32-bit signed integer that specifies the object clamping behavior. It is not used until this object
|
||||
// is applied to an image being drawn. This value MUST be one of the values defined in the following table.
|
||||
objectClamp = EmfPlusObjectClamp.valueOf(leis.readInt());
|
||||
|
||||
// A value that SHOULD be set to zero and MUST be ignored upon receipt.
|
||||
leis.skip(LittleEndianConsts.INT_SIZE);
|
||||
|
||||
return size + 5*LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
public EmfPlusGraphicsVersion getGraphicsVersion() {
|
||||
return graphicsVersion;
|
||||
}
|
||||
|
||||
public EmfPlusWrapMode getWrapMode() {
|
||||
return wrapMode;
|
||||
}
|
||||
|
||||
public Color getClampColor() {
|
||||
return clampColor;
|
||||
}
|
||||
|
||||
public EmfPlusObjectClamp getObjectClamp() {
|
||||
return objectClamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
/* ====================================================================
|
||||
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.hemf.record.emfplus;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusDraw.EmfPlusCompressed;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusDraw.EmfPlusRelativePosition;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectData;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectType;
|
||||
import org.apache.poi.util.BitField;
|
||||
import org.apache.poi.util.BitFieldFactory;
|
||||
import org.apache.poi.util.LittleEndianConsts;
|
||||
import org.apache.poi.util.LittleEndianInputStream;
|
||||
|
||||
public class HemfPlusPath {
|
||||
|
||||
/** The PathPointType enumeration defines types of points on a graphics path. */
|
||||
public enum EmfPlusPathPointType {
|
||||
/** Specifies that the point is the starting point of a path. */
|
||||
START,
|
||||
/** Specifies that the point is one of the two endpoints of a line. */
|
||||
LINE,
|
||||
// not defined
|
||||
UNUSED,
|
||||
/** Specifies that the point is an endpoint or control point of a cubic Bezier curve */
|
||||
BEZIER;
|
||||
}
|
||||
|
||||
public static class EmfPlusPath implements EmfPlusObjectData, EmfPlusCompressed, EmfPlusRelativePosition {
|
||||
/**
|
||||
* If set, the point types in the PathPointTypes array are specified by EmfPlusPathPointTypeRLE objects,
|
||||
* which use run-length encoding (RLE) compression, and/or EmfPlusPathPointType objects.
|
||||
* If clear, the point types in the PathPointTypes array are specified by EmfPlusPathPointType objects.
|
||||
*/
|
||||
private static final BitField RLE_COMPRESSED = BitFieldFactory.getInstance(0x00001000);
|
||||
|
||||
/** Specifies that a line segment that passes through the point is dashed. */
|
||||
private static final BitField POINT_TYPE_DASHED = BitFieldFactory.getInstance(0x10);
|
||||
|
||||
/** Specifies that the point is a position marker. */
|
||||
private static final BitField POINT_TYPE_MARKER = BitFieldFactory.getInstance(0x20);
|
||||
|
||||
/** Specifies that the point is the endpoint of a subpath. */
|
||||
private static final BitField POINT_TYPE_CLOSE = BitFieldFactory.getInstance(0x80);
|
||||
|
||||
private static final BitField POINT_TYPE_ENUM = BitFieldFactory.getInstance(0x0F);
|
||||
|
||||
|
||||
private static final BitField POINT_RLE_BEZIER = BitFieldFactory.getInstance(0x80);
|
||||
|
||||
private static final BitField POINT_RLE_COUNT = BitFieldFactory.getInstance(0x3F);
|
||||
|
||||
private final HemfPlusHeader.EmfPlusGraphicsVersion version = new HemfPlusHeader.EmfPlusGraphicsVersion();
|
||||
private int pointFlags;
|
||||
private Point2D[] pathPoints;
|
||||
private byte[] pointTypes;
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long dataSize, EmfPlusObjectType objectType, int flags) throws IOException {
|
||||
long size = version.init(leis);
|
||||
|
||||
// A 32-bit unsigned integer that specifies the number of points and associated point types that
|
||||
// are defined by this object.
|
||||
int pointCount = leis.readInt();
|
||||
|
||||
// A 16-bit unsigned integer that specifies how to interpret the points
|
||||
// and associated point types that are defined by this object.
|
||||
pointFlags = leis.readShort();
|
||||
|
||||
leis.skipFully(LittleEndianConsts.SHORT_SIZE);
|
||||
size += 2* LittleEndianConsts.INT_SIZE;
|
||||
|
||||
BiFunction<LittleEndianInputStream,Point2D,Integer> readPoint;
|
||||
|
||||
if (isRelativePosition()) {
|
||||
readPoint = HemfPlusDraw::readPointR;
|
||||
} else if (isCompressed()) {
|
||||
readPoint = HemfPlusDraw::readPointS;
|
||||
} else {
|
||||
readPoint = HemfPlusDraw::readPointF;
|
||||
}
|
||||
|
||||
pathPoints = new Point2D[pointCount];
|
||||
for (int i=0; i<pointCount; i++) {
|
||||
pathPoints[i] = new Point2D.Double();
|
||||
size += readPoint.apply(leis,pathPoints[i]);
|
||||
}
|
||||
|
||||
pointTypes = new byte[pointCount];
|
||||
final boolean isRLE = RLE_COMPRESSED.isSet(pointFlags);
|
||||
if (isRLE) {
|
||||
for (int i=0, rleCount; i<pointCount; i+=rleCount, size+=2) {
|
||||
rleCount = POINT_RLE_COUNT.getValue(leis.readByte());
|
||||
Arrays.fill(pointTypes, pointCount, pointCount+rleCount, leis.readByte());
|
||||
}
|
||||
} else {
|
||||
leis.readFully(pointTypes);
|
||||
size += pointCount;
|
||||
}
|
||||
|
||||
int padding = (int)((4 - (size % 4)) % 4);
|
||||
leis.skipFully(padding);
|
||||
size += padding;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
public boolean isPointDashed(int index) {
|
||||
return POINT_TYPE_DASHED.isSet(pointTypes[index]);
|
||||
}
|
||||
|
||||
public boolean isPointMarker(int index) {
|
||||
return POINT_TYPE_MARKER.isSet(pointTypes[index]);
|
||||
}
|
||||
|
||||
public boolean isPointClosed(int index) {
|
||||
return POINT_TYPE_CLOSE.isSet(pointTypes[index]);
|
||||
}
|
||||
|
||||
public EmfPlusPathPointType getPointType(int index) {
|
||||
return EmfPlusPathPointType.values()[POINT_TYPE_ENUM.getValue(pointTypes[index])];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFlags() {
|
||||
return pointFlags;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,602 @@
|
|||
/* ====================================================================
|
||||
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.hemf.record.emfplus;
|
||||
|
||||
import static org.apache.poi.hemf.record.emf.HemfFill.readXForm;
|
||||
import static org.apache.poi.hemf.record.emfplus.HemfPlusDraw.readPointF;
|
||||
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.io.IOException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusHeader.EmfPlusGraphicsVersion;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectData;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectType;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusPath.EmfPlusPath;
|
||||
import org.apache.poi.util.BitField;
|
||||
import org.apache.poi.util.BitFieldFactory;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.util.LittleEndianConsts;
|
||||
import org.apache.poi.util.LittleEndianInputStream;
|
||||
|
||||
public class HemfPlusPen {
|
||||
/**
|
||||
* The LineCapType enumeration defines types of line caps to use at the ends of lines that are drawn
|
||||
* with graphics pens.
|
||||
*/
|
||||
public enum EmfPlusLineCapType {
|
||||
/** Specifies a squared-off line cap. The end of the line MUST be the last point in the line. */
|
||||
FLAT(0X00000000),
|
||||
/**
|
||||
* Specifies a square line cap. The center of the square MUST be located at
|
||||
* the last point in the line. The width of the square is the line width.
|
||||
*/
|
||||
SQUARE(0X00000001),
|
||||
/**
|
||||
* Specifies a circular line cap. The center of the circle MUST be located at
|
||||
* the last point in the line. The diameter of the circle is the line width.
|
||||
*/
|
||||
ROUND(0X00000002),
|
||||
/**
|
||||
* Specifies a triangular line cap. The base of the triangle MUST be located
|
||||
* at the last point in the line. The base of the triangle is the line width.
|
||||
*/
|
||||
TRIANGLE(0X00000003),
|
||||
/** Specifies that the line end is not anchored. */
|
||||
NO_ANCHOR(0X00000010),
|
||||
/**
|
||||
* Specifies that the line end is anchored with a square line cap. The center of the square MUST be located
|
||||
* at the last point in the line. The height and width of the square are the line width.
|
||||
*/
|
||||
SQUARE_ANCHOR(0X00000011),
|
||||
/**
|
||||
* Specifies that the line end is anchored with a circular line cap. The center of the circle MUST be located
|
||||
* at the last point in the line. The circle SHOULD be wider than the line.
|
||||
*/
|
||||
ROUND_ANCHOR(0X00000012),
|
||||
/**
|
||||
* Specifies that the line end is anchored with a diamond-shaped line cap, which is a square turned at
|
||||
* 45 degrees. The center of the diamond MUST be located at the last point in the line.
|
||||
* The diamond SHOULD be wider than the line.
|
||||
*/
|
||||
DIAMOND_ANCHOR(0X00000013),
|
||||
/**
|
||||
* Specifies that the line end is anchored with an arrowhead shape. The arrowhead point MUST be located at
|
||||
* the last point in the line. The arrowhead SHOULD be wider than the line.
|
||||
*/
|
||||
ARROW_ANCHOR(0X00000014),
|
||||
/** Mask used to check whether a line cap is an anchor cap. */
|
||||
ANCHOR_MASK(0X000000F0),
|
||||
/** Specifies a custom line cap. */
|
||||
CUSTOM(0X000000FF)
|
||||
;
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusLineCapType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusLineCapType valueOf(int id) {
|
||||
for (EmfPlusLineCapType wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The LineJoinType enumeration defines ways to join two lines that are drawn by the same graphics
|
||||
* pen and whose ends meet.
|
||||
*/
|
||||
public enum EmfPlusLineJoin {
|
||||
MITER(0X00000000),
|
||||
BEVEL(0X00000001),
|
||||
ROUND(0X00000002),
|
||||
MITER_CLIPPED(0X00000003)
|
||||
;
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusLineJoin(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusLineJoin valueOf(int id) {
|
||||
for (EmfPlusLineJoin wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** The LineStyle enumeration defines styles of lines that are drawn with graphics pens. */
|
||||
public enum EmfPlusLineStyle {
|
||||
/** Specifies a solid line. */
|
||||
SOLID(0X00000000),
|
||||
/** Specifies a dashed line. */
|
||||
DASH(0X00000001),
|
||||
/** Specifies a dotted line. */
|
||||
DOT(0X00000002),
|
||||
/** Specifies an alternating dash-dot line. */
|
||||
DASH_DOT(0X00000003),
|
||||
/** Specifies an alternating dash-dot-dot line. */
|
||||
DASH_DOT_DOT(0X00000004),
|
||||
/** Specifies a user-defined, custom dashed line. */
|
||||
CUSTOM(0X00000005)
|
||||
;
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusLineStyle(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusLineStyle valueOf(int id) {
|
||||
for (EmfPlusLineStyle wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The DashedLineCapType enumeration defines types of line caps to use at the ends of dashed lines
|
||||
* that are drawn with graphics pens.
|
||||
*/
|
||||
public enum EmfPlusDashedLineCapType {
|
||||
/** Specifies a flat dashed line cap. */
|
||||
FLAT(0X00000000),
|
||||
/** Specifies a round dashed line cap. */
|
||||
ROUND(0X00000002),
|
||||
/** Specifies a triangular dashed line cap. */
|
||||
TRIANGLE(0X00000003)
|
||||
;
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusDashedLineCapType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusDashedLineCapType valueOf(int id) {
|
||||
for (EmfPlusDashedLineCapType wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The PenAlignment enumeration defines the distribution of the width of the pen with respect to the
|
||||
* line being drawn.
|
||||
*/
|
||||
public enum EmfPlusPenAlignment {
|
||||
/** Specifies that the EmfPlusPen object is centered over the theoretical line. */
|
||||
CENTER(0X00000000),
|
||||
/** Specifies that the pen is positioned on the inside of the theoretical line. */
|
||||
INSET(0X00000001),
|
||||
/** Specifies that the pen is positioned to the left of the theoretical line. */
|
||||
LEFT(0X00000002),
|
||||
/** Specifies that the pen is positioned on the outside of the theoretical line. */
|
||||
OUTSET(0X00000003),
|
||||
/** Specifies that the pen is positioned to the right of the theoretical line. */
|
||||
RIGHT(0X00000004)
|
||||
;
|
||||
|
||||
public final int id;
|
||||
|
||||
EmfPlusPenAlignment(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static EmfPlusPenAlignment valueOf(int id) {
|
||||
for (EmfPlusPenAlignment wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EmfPlusPen implements EmfPlusObjectData {
|
||||
|
||||
|
||||
/**
|
||||
* If set, a 2x3 transform matrix MUST be specified in the OptionalData field of an EmfPlusPenData object.
|
||||
*/
|
||||
private final static BitField TRANSFORM = BitFieldFactory.getInstance(0x00000001);
|
||||
/**
|
||||
* If set, the style of a starting line cap MUST be specified in the OptionalData field of an
|
||||
* EmfPlusPenData object.
|
||||
*/
|
||||
private final static BitField START_CAP = BitFieldFactory.getInstance(0x00000002);
|
||||
/**
|
||||
* Indicates whether the style of an ending line cap MUST be specified in the OptionalData field
|
||||
* of an EmfPlusPenData object.
|
||||
*/
|
||||
private final static BitField END_CAP = BitFieldFactory.getInstance(0x00000004);
|
||||
/**
|
||||
* Indicates whether a line join type MUST be specified in the OptionalData
|
||||
* field of an EmfPlusPenData object.
|
||||
*/
|
||||
private final static BitField JOIN = BitFieldFactory.getInstance(0x00000008);
|
||||
/**
|
||||
* Indicates whether a miter limit MUST be specified in the OptionalData field of an EmfPlusPenData object.
|
||||
*/
|
||||
private final static BitField MITER_LIMIT = BitFieldFactory.getInstance(0x00000010);
|
||||
/**
|
||||
* Indicates whether a line style MUST be specified in the OptionalData field of an EmfPlusPenData object.
|
||||
*/
|
||||
private final static BitField LINE_STYLE = BitFieldFactory.getInstance(0x00000020);
|
||||
/**
|
||||
* Indicates whether a dashed line cap MUST be specified in the OptionalData field of an EmfPlusPenData object.
|
||||
*/
|
||||
private final static BitField DASHED_LINE_CAP = BitFieldFactory.getInstance(0x00000040);
|
||||
/**
|
||||
* Indicates whether a dashed line offset MUST be specified in the OptionalData field of an EmfPlusPenData object.
|
||||
*/
|
||||
private final static BitField DASHED_LINE_OFFSET = BitFieldFactory.getInstance(0x00000080);
|
||||
/**
|
||||
* Indicates whether an EmfPlusDashedLineData object MUST be specified in the
|
||||
* OptionalData field of an EmfPlusPenData object.
|
||||
*/
|
||||
private final static BitField DASHED_LINE = BitFieldFactory.getInstance(0x00000100);
|
||||
/**
|
||||
* Indicates whether a pen alignment MUST be specified in the OptionalData field of an EmfPlusPenData object.
|
||||
*/
|
||||
private final static BitField NON_CENTER = BitFieldFactory.getInstance(0x00000200);
|
||||
/**
|
||||
* Indicates whether the length and content of a EmfPlusCompoundLineData object are present in the
|
||||
* OptionalData field of an EmfPlusPenData object.
|
||||
*/
|
||||
private final static BitField COMPOUND_LINE = BitFieldFactory.getInstance(0x00000400);
|
||||
/**
|
||||
* Indicates whether an EmfPlusCustomStartCapData object MUST be specified
|
||||
* in the OptionalData field of an EmfPlusPenData object.y
|
||||
*/
|
||||
private final static BitField CUSTOM_START_CAP = BitFieldFactory.getInstance(0x00000800);
|
||||
/**
|
||||
* Indicates whether an EmfPlusCustomEndCapData object MUST be specified in
|
||||
* the OptionalData field of an EmfPlusPenData object.
|
||||
*/
|
||||
private final static BitField CUSTOM_END_CAP = BitFieldFactory.getInstance(0x00001000);
|
||||
|
||||
private final EmfPlusGraphicsVersion graphicsVersion = new EmfPlusGraphicsVersion();
|
||||
|
||||
|
||||
private int type;
|
||||
private int penDataFlags;
|
||||
private HemfPlusDraw.EmfPlusUnitType unitType;
|
||||
private double penWidth;
|
||||
private AffineTransform trans;
|
||||
private EmfPlusLineCapType startCap, endCap;
|
||||
private EmfPlusLineJoin join;
|
||||
private Double mitterLimit;
|
||||
private EmfPlusLineStyle style;
|
||||
EmfPlusDashedLineCapType dashedLineCapType;
|
||||
private Double dashOffset;
|
||||
private double[] dashedLineData;
|
||||
private EmfPlusPenAlignment penAlignment;
|
||||
private double[] compoundLineData;
|
||||
private EmfPlusCustomLineCap customStartCap;
|
||||
private EmfPlusCustomLineCap customEndCap;
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long dataSize, EmfPlusObjectType objectType, int flags) throws IOException {
|
||||
// An EmfPlusGraphicsVersion object that specifies the version of operating system graphics that
|
||||
// was used to create this object.
|
||||
long size = graphicsVersion.init(leis);
|
||||
// This field MUST be set to zero.
|
||||
type = leis.readInt();
|
||||
// A 32-bit unsigned integer that specifies the data in the OptionalData field.
|
||||
// This value MUST be composed of PenData flags
|
||||
penDataFlags = leis.readInt();
|
||||
// A 32-bit unsigned integer that specifies the measuring units for the pen.
|
||||
// The value MUST be from the UnitType enumeration
|
||||
unitType = HemfPlusDraw.EmfPlusUnitType.valueOf(leis.readInt());
|
||||
// A 32-bit floating-point value that specifies the width of the line drawn by the pen in the units specified
|
||||
// by the PenUnit field. If a zero width is specified, a minimum value is used, which is determined by the units.
|
||||
penWidth = leis.readFloat();
|
||||
size += 4* LittleEndianConsts.INT_SIZE;
|
||||
|
||||
if (TRANSFORM.isSet(penDataFlags)) {
|
||||
// An optional EmfPlusTransformMatrix object that specifies a world space to device space transform for
|
||||
// the pen. This field MUST be present if the PenDataTransform flag is set in the PenDataFlags field of
|
||||
// the EmfPlusPenData object.
|
||||
trans = new AffineTransform();
|
||||
size += readXForm(leis, trans);
|
||||
}
|
||||
|
||||
if (START_CAP.isSet(penDataFlags)) {
|
||||
// An optional 32-bit signed integer that specifies the shape for the start of a line in the
|
||||
// CustomStartCapData field. This field MUST be present if the PenDataStartCap flag is set in the
|
||||
// PenDataFlags field of the EmfPlusPenData object, and the value MUST be defined in the LineCapType enumeration
|
||||
startCap = EmfPlusLineCapType.valueOf(leis.readInt());
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
if (END_CAP.isSet(penDataFlags)) {
|
||||
// An optional 32-bit signed integer that specifies the shape for the end of a line in the
|
||||
// CustomEndCapData field. This field MUST be present if the PenDataEndCap flag is set in the
|
||||
// PenDataFlags field of the EmfPlusPenData object, and the value MUST be defined in the LineCapType enumeration.
|
||||
endCap = EmfPlusLineCapType.valueOf(leis.readInt());
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
if (JOIN.isSet(penDataFlags)) {
|
||||
// An optional 32-bit signed integer that specifies how to join two lines that are drawn by the same pen
|
||||
// and whose ends meet. This field MUST be present if the PenDataJoin flag is set in the PenDataFlags
|
||||
// field of the EmfPlusPenData object, and the value MUST be defined in the LineJoinType enumeration
|
||||
join = EmfPlusLineJoin.valueOf(leis.readInt());
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
if (MITER_LIMIT.isSet(penDataFlags)) {
|
||||
// An optional 32-bit floating-point value that specifies the miter limit, which is the maximum allowed
|
||||
// ratio of miter length to line width. The miter length is the distance from the intersection of the
|
||||
// line walls on the inside the join to the intersection of the line walls outside the join. The miter
|
||||
// length can be large when the angle between two lines is small. This field MUST be present if the
|
||||
// PenDataMiterLimit flag is set in the PenDataFlags field of the EmfPlusPenData object.
|
||||
mitterLimit = (double)leis.readFloat();
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
if (LINE_STYLE.isSet(penDataFlags)) {
|
||||
// An optional 32-bit signed integer that specifies the style used for lines drawn with this pen object.
|
||||
// This field MUST be present if the PenDataLineStyle flag is set in the PenDataFlags field of the
|
||||
// EmfPlusPenData object, and the value MUST be defined in the LineStyle enumeration
|
||||
style = EmfPlusLineStyle.valueOf(leis.readInt());
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
if (DASHED_LINE_CAP.isSet(penDataFlags)) {
|
||||
// An optional 32-bit signed integer that specifies the shape for both ends of each dash in a dashed line.
|
||||
// This field MUST be present if the PenDataDashedLineCap flag is set in the PenDataFlags field of the
|
||||
// EmfPlusPenData object, and the value MUST be defined in the DashedLineCapType enumeration
|
||||
dashedLineCapType = EmfPlusDashedLineCapType.valueOf(leis.readInt());
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
if (DASHED_LINE_OFFSET.isSet(penDataFlags)) {
|
||||
// An optional 32-bit floating-point value that specifies the distance from the start of a line to the
|
||||
// start of the first space in a dashed line pattern. This field MUST be present if the
|
||||
// PenDataDashedLineOffset flag is set in the PenDataFlags field of the EmfPlusPenData object.
|
||||
dashOffset = (double)leis.readFloat();
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
if (DASHED_LINE.isSet(penDataFlags)) {
|
||||
// A 32-bit unsigned integer that specifies the number of elements in the DashedLineData field.
|
||||
int dashesSize = leis.readInt();
|
||||
if (dashesSize < 0 || dashesSize > 1000) {
|
||||
throw new RuntimeException("Invalid dash data size");
|
||||
}
|
||||
|
||||
// An array of DashedLineDataSize floating-point values that specify the lengths of the dashes and spaces in a dashed line.
|
||||
dashedLineData = new double[dashesSize];
|
||||
for (int i=0; i<dashesSize; i++) {
|
||||
dashedLineData[i] = leis.readFloat();
|
||||
}
|
||||
|
||||
size += LittleEndianConsts.INT_SIZE * (dashesSize+1);
|
||||
}
|
||||
|
||||
if (NON_CENTER.isSet(penDataFlags)) {
|
||||
// An optional 32-bit signed integer that specifies the distribution of the pen width with respect to
|
||||
// the coordinates of the line being drawn. This field MUST be present if the PenDataNonCenter flag is
|
||||
// set in the PenDataFlags field of the EmfPlusPenData object, and the value MUST be defined in the
|
||||
// PenAlignment enumeration
|
||||
penAlignment = EmfPlusPenAlignment.valueOf(leis.readInt());
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
if (COMPOUND_LINE.isSet(penDataFlags)) {
|
||||
// A 32-bit unsigned integer that specifies the number of elements in the CompoundLineData field.
|
||||
int compoundSize = leis.readInt();
|
||||
if (compoundSize < 0 || compoundSize > 1000) {
|
||||
throw new RuntimeException("Invalid compound line data size");
|
||||
}
|
||||
|
||||
// An array of CompoundLineDataSize floating-point values that specify the compound line of a pen.
|
||||
// The elements MUST be in increasing order, and their values MUST be between 0.0 and 1.0, inclusive.
|
||||
compoundLineData = new double[compoundSize];
|
||||
|
||||
for (int i=0; i<compoundSize; i++) {
|
||||
compoundLineData[i] = leis.readFloat();
|
||||
}
|
||||
size += LittleEndianConsts.INT_SIZE * (compoundSize+1);
|
||||
}
|
||||
|
||||
if (CUSTOM_START_CAP.isSet(penDataFlags)) {
|
||||
size += initCustomCap(c -> customStartCap = c, leis);
|
||||
}
|
||||
|
||||
if (CUSTOM_END_CAP.isSet(penDataFlags)) {
|
||||
size += initCustomCap(c -> customEndCap = c, leis);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
private long initCustomCap(Consumer<EmfPlusCustomLineCap> setter, LittleEndianInputStream leis) throws IOException {
|
||||
EmfPlusGraphicsVersion version = new EmfPlusGraphicsVersion();
|
||||
long size = version.init(leis);
|
||||
|
||||
boolean adjustableArrow = (leis.readInt() != 0);
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
|
||||
EmfPlusCustomLineCap cap = (adjustableArrow) ? new EmfPlusAdjustableArrowCap() : new EmfPlusPathArrowCap();
|
||||
size += cap.init(leis);
|
||||
|
||||
setter.accept(cap);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@Internal
|
||||
public interface EmfPlusCustomLineCap {
|
||||
long init(LittleEndianInputStream leis) throws IOException;
|
||||
}
|
||||
|
||||
public static class EmfPlusPathArrowCap implements EmfPlusCustomLineCap {
|
||||
/**
|
||||
* If set, an EmfPlusFillPath object MUST be specified in the OptionalData field of the
|
||||
* EmfPlusCustomLineCapData object for filling the custom line cap.
|
||||
*/
|
||||
private static final BitField FILL_PATH = BitFieldFactory.getInstance(0x00000001);
|
||||
/**
|
||||
* If set, an EmfPlusLinePath object MUST be specified in the OptionalData field of the
|
||||
* EmfPlusCustomLineCapData object for outlining the custom line cap.
|
||||
*/
|
||||
private static final BitField LINE_PATH = BitFieldFactory.getInstance(0x00000002);
|
||||
|
||||
|
||||
private int dataFlags;
|
||||
private EmfPlusLineCapType baseCap;
|
||||
private double baseInset;
|
||||
private EmfPlusLineCapType startCap;
|
||||
private EmfPlusLineCapType endCap;
|
||||
private EmfPlusLineJoin join;
|
||||
private double mitterLimit;
|
||||
private double widthScale;
|
||||
private final Point2D fillHotSpot = new Point2D.Double();
|
||||
private final Point2D lineHotSpot = new Point2D.Double();
|
||||
private EmfPlusPath fillPath;
|
||||
private EmfPlusPath outlinePath;
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis) throws IOException {
|
||||
// A 32-bit unsigned integer that specifies the data in the OptionalData field.
|
||||
// This value MUST be composed of CustomLineCapData flags
|
||||
dataFlags = leis.readInt();
|
||||
|
||||
// A 32-bit unsigned integer that specifies the value from the LineCap enumeration on which
|
||||
// the custom line cap is based.
|
||||
baseCap = EmfPlusLineCapType.valueOf(leis.readInt());
|
||||
|
||||
// A 32-bit floating-point value that specifies the distance between the
|
||||
// beginning of the line cap and the end of the line.
|
||||
baseInset = leis.readFloat();
|
||||
|
||||
// A 32-bit unsigned integer that specifies the value in the LineCap enumeration that indicates the line
|
||||
// cap used at the start/end of the line to be drawn.
|
||||
startCap = EmfPlusLineCapType.valueOf(leis.readInt());
|
||||
endCap = EmfPlusLineCapType.valueOf(leis.readInt());
|
||||
|
||||
// A 32-bit unsigned integer that specifies the value in the LineJoin enumeration, which specifies how
|
||||
// to join two lines that are drawn by the same pen and whose ends meet. At the intersection of the two
|
||||
// line ends, a line join makes the connection look more continuous.
|
||||
join = EmfPlusLineJoin.valueOf(leis.readInt());
|
||||
|
||||
// A 32-bit floating-point value that contains the limit of the thickness of the join on a mitered corner
|
||||
// by setting the maximum allowed ratio of miter length to line width.
|
||||
mitterLimit = leis.readFloat();
|
||||
|
||||
// A 32-bit floating-point value that specifies the amount by which to scale the custom line cap with
|
||||
// respect to the width of the EmfPlusPen object that is used to draw the lines.
|
||||
widthScale = leis.readFloat();
|
||||
|
||||
int size = 8* LittleEndianConsts.INT_SIZE;
|
||||
|
||||
// An EmfPlusPointF object that is not currently used. It MUST be set to {0.0, 0.0}.
|
||||
size += readPointF(leis, fillHotSpot);
|
||||
size += readPointF(leis, lineHotSpot);
|
||||
|
||||
if (FILL_PATH.isSet(dataFlags)) {
|
||||
fillPath = new EmfPlusPath();
|
||||
size += fillPath.init(leis, -1, null, -1);
|
||||
}
|
||||
|
||||
if (LINE_PATH.isSet(dataFlags)) {
|
||||
outlinePath = new EmfPlusPath();
|
||||
size += outlinePath.init(leis, -1, null, -1);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EmfPlusAdjustableArrowCap implements EmfPlusCustomLineCap {
|
||||
private double width;
|
||||
private double height;
|
||||
private double middleInset;
|
||||
private boolean isFilled;
|
||||
private EmfPlusLineCapType startCap;
|
||||
private EmfPlusLineCapType endCap;
|
||||
private EmfPlusLineJoin join;
|
||||
private double mitterLimit;
|
||||
private double widthScale;
|
||||
private final Point2D fillHotSpot = new Point2D.Double();
|
||||
private final Point2D lineHotSpot = new Point2D.Double();
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis) throws IOException {
|
||||
// A 32-bit floating-point value that specifies the width of the arrow cap.
|
||||
// The width of the arrow cap is scaled by the width of the EmfPlusPen object that is used to draw the
|
||||
// line being capped. For example, when drawing a capped line with a pen that has a width of 5 pixels,
|
||||
// and the adjustable arrow cap object has a width of 3, the actual arrow cap is drawn 15 pixels wide.
|
||||
width = leis.readFloat();
|
||||
|
||||
// A 32-bit floating-point value that specifies the height of the arrow cap.
|
||||
// The height of the arrow cap is scaled by the width of the EmfPlusPen object that is used to draw the
|
||||
// line being capped. For example, when drawing a capped line with a pen that has a width of 5 pixels,
|
||||
// and the adjustable arrow cap object has a height of 3, the actual arrow cap is drawn 15 pixels high.
|
||||
height = leis.readFloat();
|
||||
|
||||
// A 32-bit floating-point value that specifies the number of pixels between the outline of the arrow
|
||||
// cap and the fill of the arrow cap.
|
||||
middleInset = leis.readFloat();
|
||||
|
||||
// A 32-bit Boolean value that specifies whether the arrow cap is filled.
|
||||
// If the arrow cap is not filled, only the outline is drawn.
|
||||
isFilled = (leis.readInt() != 0);
|
||||
|
||||
// A 32-bit unsigned integer that specifies the value in the LineCap enumeration that indicates
|
||||
// the line cap to be used at the start/end of the line to be drawn.
|
||||
startCap = EmfPlusLineCapType.valueOf(leis.readInt());
|
||||
endCap = EmfPlusLineCapType.valueOf(leis.readInt());
|
||||
|
||||
// 32-bit unsigned integer that specifies the value in the LineJoin enumeration that specifies how to
|
||||
// join two lines that are drawn by the same pen and whose ends meet. At the intersection of the two
|
||||
// line ends, a line join makes the connection look more continuous.
|
||||
join = EmfPlusLineJoin.valueOf(leis.readInt());
|
||||
|
||||
// A 32-bit floating-point value that specifies the limit of the thickness of the join on a mitered
|
||||
// corner by setting the maximum allowed ratio of miter length to line width.
|
||||
mitterLimit = leis.readFloat();
|
||||
|
||||
// A 32-bit floating-point value that specifies the amount by which to scale an EmfPlusCustomLineCap
|
||||
// object with respect to the width of the graphics pen that is used to draw the lines.
|
||||
widthScale = leis.readFloat();
|
||||
|
||||
int size = 9 * LittleEndianConsts.INT_SIZE;
|
||||
|
||||
// An EmfPlusPointF object that is not currently used. It MUST be set to {0.0, 0.0}.
|
||||
size += readPointF(leis, fillHotSpot);
|
||||
|
||||
// An EmfPlusPointF object that is not currently used. It MUST be set to {0.0, 0.0}.
|
||||
size += readPointF(leis, lineHotSpot);
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
/* ====================================================================
|
||||
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.hemf.record.emfplus;
|
||||
|
||||
import static org.apache.poi.hemf.record.emfplus.HemfPlusDraw.readRectF;
|
||||
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.io.IOException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusHeader.EmfPlusGraphicsVersion;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectData;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectType;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusPath.EmfPlusPath;
|
||||
import org.apache.poi.util.LittleEndianConsts;
|
||||
import org.apache.poi.util.LittleEndianInputStream;
|
||||
|
||||
public class HemfPlusRegion {
|
||||
public enum EmfPlusRegionNodeDataType {
|
||||
/**
|
||||
* Specifies a region node with child nodes. A Boolean AND operation SHOULD be applied to the left and right
|
||||
* child nodes specified by an EmfPlusRegionNodeChildNodes object
|
||||
*/
|
||||
AND(0X00000001, EmfPlusRegionNode::new),
|
||||
/**
|
||||
* Specifies a region node with child nodes. A Boolean OR operation SHOULD be applied to the left and right
|
||||
* child nodes specified by an EmfPlusRegionNodeChildNodes object.
|
||||
*/
|
||||
OR(0X00000002, EmfPlusRegionNode::new),
|
||||
/**
|
||||
* Specifies a region node with child nodes. A Boolean XOR operation SHOULD be applied to the left and right
|
||||
* child nodes specified by an EmfPlusRegionNodeChildNodes object.
|
||||
*/
|
||||
XOR(0X00000003, EmfPlusRegionNode::new),
|
||||
/**
|
||||
* Specifies a region node with child nodes. A Boolean operation, defined as "the part of region 1 that is excluded
|
||||
* from region 2", SHOULD be applied to the left and right child nodes specified by an EmfPlusRegionNodeChildNodes object.
|
||||
*/
|
||||
EXCLUDE(0X00000004, EmfPlusRegionNode::new),
|
||||
/**
|
||||
* Specifies a region node with child nodes. A Boolean operation, defined as "the part of region 2 that is excluded
|
||||
* from region 1", SHOULD be applied to the left and right child nodes specified by an EmfPlusRegionNodeChildNodes object.
|
||||
*/
|
||||
COMPLEMENT(0X00000005, EmfPlusRegionNode::new),
|
||||
/**
|
||||
* Specifies a region node with no child nodes.
|
||||
* The RegionNodeData field SHOULD specify a boundary with an EmfPlusRectF object.
|
||||
*/
|
||||
RECT(0X10000000, EmfPlusRegionRect::new),
|
||||
/**
|
||||
* Specifies a region node with no child nodes.
|
||||
* The RegionNodeData field SHOULD specify a boundary with an EmfPlusRegionNodePath object
|
||||
*/
|
||||
PATH(0X10000001, EmfPlusRegionPath::new),
|
||||
/** Specifies a region node with no child nodes. The RegionNodeData field SHOULD NOT be present. */
|
||||
EMPTY(0X10000002, EmfPlusRegionEmpty::new),
|
||||
/** Specifies a region node with no child nodes, and its bounds are not defined. */
|
||||
INFINITE(0X10000003, EmfPlusRegionInfinite::new)
|
||||
;
|
||||
|
||||
public final int id;
|
||||
public final Supplier<EmfPlusRegionNodeData> constructor;
|
||||
|
||||
EmfPlusRegionNodeDataType(int id, Supplier<EmfPlusRegionNodeData> constructor) {
|
||||
this.id = id;
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
public static EmfPlusRegionNodeDataType valueOf(int id) {
|
||||
for (EmfPlusRegionNodeDataType wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EmfPlusRegion implements EmfPlusObjectData {
|
||||
|
||||
private final EmfPlusGraphicsVersion version = new EmfPlusGraphicsVersion();
|
||||
private EmfPlusRegionNodeData regionNode;
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long dataSize, EmfPlusObjectType objectType, int flags) throws IOException {
|
||||
long size = version.init(leis);
|
||||
|
||||
// A 32-bit unsigned integer that specifies the number of child nodes in the RegionNode field.
|
||||
int nodeCount = leis.readInt();
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
|
||||
// An array of RegionNodeCount+1 EmfPlusRegionNode objects. Regions are specified as a binary tree of
|
||||
// region nodes, and each node MUST either be a terminal node or specify one or two child nodes.
|
||||
// RegionNode MUST contain at least one element.
|
||||
size += readNode(leis, d -> regionNode = d);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public interface EmfPlusRegionNodeData {
|
||||
long init(LittleEndianInputStream leis) throws IOException;
|
||||
}
|
||||
|
||||
public static class EmfPlusRegionPath extends EmfPlusPath implements EmfPlusRegionNodeData {
|
||||
public long init(LittleEndianInputStream leis) throws IOException {
|
||||
int dataSize = leis.readInt();
|
||||
return super.init(leis, dataSize, EmfPlusObjectType.PATH, 0) + LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EmfPlusRegionInfinite implements EmfPlusRegionNodeData {
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis) throws IOException {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EmfPlusRegionEmpty implements EmfPlusRegionNodeData {
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis) throws IOException {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EmfPlusRegionRect implements EmfPlusRegionNodeData {
|
||||
private final Rectangle2D rect = new Rectangle2D.Double();
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis) {
|
||||
return readRectF(leis, rect);
|
||||
}
|
||||
}
|
||||
|
||||
public static class EmfPlusRegionNode implements EmfPlusRegionNodeData {
|
||||
private EmfPlusRegionNodeData left, right;
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis) throws IOException {
|
||||
long size = readNode(leis, n -> left = n);
|
||||
size += readNode(leis, n -> right = n);
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static long readNode(LittleEndianInputStream leis, Consumer<EmfPlusRegionNodeData> con) throws IOException {
|
||||
// A 32-bit unsigned integer that specifies the type of data in the RegionNodeData field.
|
||||
// This value MUST be defined in the RegionNodeDataType enumeration
|
||||
EmfPlusRegionNodeDataType type = EmfPlusRegionNodeDataType.valueOf(leis.readInt());
|
||||
assert(type != null);
|
||||
EmfPlusRegionNodeData nd = type.constructor.get();
|
||||
con.accept(nd);
|
||||
return LittleEndianConsts.INT_SIZE + nd.init(leis);
|
||||
}
|
||||
}
|
|
@ -36,7 +36,11 @@ import javax.imageio.ImageIO;
|
|||
|
||||
import org.apache.poi.hemf.record.emf.HemfComment;
|
||||
import org.apache.poi.hemf.record.emf.HemfRecord;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusImage.EmfPlusBitmapDataType;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusImage.EmfPlusImage;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusImage.EmfPlusPixelFormat;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusObject;
|
||||
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObject;
|
||||
import org.apache.poi.hwmf.record.HwmfBitmapDib;
|
||||
import org.apache.poi.hwmf.record.HwmfFill;
|
||||
import org.apache.poi.hwmf.usermodel.HwmfEmbedded;
|
||||
|
@ -105,7 +109,7 @@ public class HemfEmbeddedIterator implements Iterator<HwmfEmbedded> {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (obj instanceof HemfPlusObject.EmfPlusObject && ((HemfPlusObject.EmfPlusObject)obj).getObjectType() == HemfPlusObject.EmfPlusObjectType.IMAGE) {
|
||||
if (obj instanceof EmfPlusObject && ((EmfPlusObject)obj).getObjectType() == HemfPlusObject.EmfPlusObjectType.IMAGE) {
|
||||
current = obj;
|
||||
return true;
|
||||
}
|
||||
|
@ -196,13 +200,13 @@ public class HemfEmbeddedIterator implements Iterator<HwmfEmbedded> {
|
|||
}
|
||||
|
||||
private HwmfEmbedded checkEmfPlusObject() {
|
||||
if (!(current instanceof HemfPlusObject.EmfPlusObject)) {
|
||||
if (!(current instanceof EmfPlusObject)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HemfPlusObject.EmfPlusObject epo = (HemfPlusObject.EmfPlusObject)current;
|
||||
EmfPlusObject epo = (EmfPlusObject)current;
|
||||
assert(epo.getObjectType() == HemfPlusObject.EmfPlusObjectType.IMAGE);
|
||||
HemfPlusObject.EmfPlusImage img = epo.getObjectData();
|
||||
EmfPlusImage img = epo.getObjectData();
|
||||
assert(img.getImageDataType() != null);
|
||||
|
||||
HwmfEmbedded emb = getEmfPlusImageData();
|
||||
|
@ -210,7 +214,7 @@ public class HemfEmbeddedIterator implements Iterator<HwmfEmbedded> {
|
|||
HwmfEmbeddedType et;
|
||||
switch (img.getImageDataType()) {
|
||||
case BITMAP:
|
||||
if (img.getBitmapType() == HemfPlusObject.EmfPlusBitmapDataType.COMPRESSED) {
|
||||
if (img.getBitmapType() == EmfPlusBitmapDataType.COMPRESSED) {
|
||||
switch (FileMagic.valueOf(emb.getRawData())) {
|
||||
case JPEG:
|
||||
et = HwmfEmbeddedType.JPEG;
|
||||
|
@ -262,11 +266,11 @@ public class HemfEmbeddedIterator implements Iterator<HwmfEmbedded> {
|
|||
/**
|
||||
* Compress GDIs internal format to something useful
|
||||
*/
|
||||
private void compressGDIBitmap(HemfPlusObject.EmfPlusImage img, HwmfEmbedded emb, HwmfEmbeddedType et) {
|
||||
private void compressGDIBitmap(EmfPlusImage img, HwmfEmbedded emb, HwmfEmbeddedType et) {
|
||||
final int width = img.getBitmapWidth();
|
||||
final int height = img.getBitmapHeight();
|
||||
final int stride = img.getBitmapStride();
|
||||
final HemfPlusObject.EmfPlusPixelFormat pf = img.getPixelFormat();
|
||||
final EmfPlusPixelFormat pf = img.getPixelFormat();
|
||||
|
||||
int[] nBits, bOffs;
|
||||
switch (pf) {
|
||||
|
@ -306,14 +310,14 @@ public class HemfEmbeddedIterator implements Iterator<HwmfEmbedded> {
|
|||
|
||||
|
||||
private HwmfEmbedded getEmfPlusImageData() {
|
||||
HemfPlusObject.EmfPlusObject epo = (HemfPlusObject.EmfPlusObject)current;
|
||||
EmfPlusObject epo = (EmfPlusObject)current;
|
||||
assert(epo.getObjectType() == HemfPlusObject.EmfPlusObjectType.IMAGE);
|
||||
|
||||
final int objectId = epo.getObjectId();
|
||||
|
||||
HwmfEmbedded emb = new HwmfEmbedded();
|
||||
|
||||
HemfPlusObject.EmfPlusImage img = (HemfPlusObject.EmfPlusImage)epo.getObjectData();
|
||||
EmfPlusImage img = (EmfPlusImage)epo.getObjectData();
|
||||
assert(img.getImageDataType() != null);
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
|
@ -324,10 +328,10 @@ public class HemfEmbeddedIterator implements Iterator<HwmfEmbedded> {
|
|||
current = null;
|
||||
//noinspection ConstantConditions
|
||||
if (hasNext() &&
|
||||
(current instanceof HemfPlusObject.EmfPlusObject) &&
|
||||
((epo = (HemfPlusObject.EmfPlusObject) current).getObjectId() == objectId)
|
||||
(current instanceof EmfPlusObject) &&
|
||||
((epo = (EmfPlusObject) current).getObjectId() == objectId)
|
||||
) {
|
||||
img = (HemfPlusObject.EmfPlusImage)epo.getObjectData();
|
||||
img = (EmfPlusImage)epo.getObjectData();
|
||||
} else {
|
||||
return emb;
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ public class HemfPictureTest {
|
|||
|
||||
if (entry.isDirectory() || !etName.endsWith(".emf") || passed.contains(etName)) continue;
|
||||
|
||||
if (!etName.equals("emfs/commoncrawl2/2S/2SYMYPLNJURGCXJKLNZCJQGIBHVMQTRS_0.emf")) continue;
|
||||
// if (!etName.equals("emfs/commoncrawl2/2S/2SYMYPLNJURGCXJKLNZCJQGIBHVMQTRS_0.emf")) continue;
|
||||
|
||||
// emfs/commoncrawl2/ZJ/ZJT2BZPLQR7DKSKYLYL6GRDEUM2KIO5F_4.emf
|
||||
// emfs/govdocs1/005/005203.ppt_3.emf
|
||||
|
|
Loading…
Reference in New Issue