| /* ==================================================================== |
| 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.hwmf.record; |
| |
| import java.io.IOException; |
| import java.nio.charset.Charset; |
| import java.nio.charset.StandardCharsets; |
| import java.util.Collections; |
| import java.util.LinkedHashMap; |
| import java.util.Map; |
| import java.util.function.Supplier; |
| |
| import org.apache.poi.common.usermodel.GenericRecord; |
| import org.apache.poi.common.usermodel.fonts.FontCharset; |
| import org.apache.poi.common.usermodel.fonts.FontFamily; |
| import org.apache.poi.common.usermodel.fonts.FontHeader; |
| import org.apache.poi.common.usermodel.fonts.FontInfo; |
| import org.apache.poi.common.usermodel.fonts.FontPitch; |
| import org.apache.poi.util.BitField; |
| import org.apache.poi.util.BitFieldFactory; |
| import org.apache.poi.util.GenericRecordJsonWriter; |
| import org.apache.poi.util.GenericRecordUtil; |
| import org.apache.poi.util.LittleEndianConsts; |
| import org.apache.poi.util.LittleEndianInputStream; |
| |
| /** |
| * The Font object specifies the attributes of a logical font |
| */ |
| @SuppressWarnings({"unused", "Duplicates"}) |
| public class HwmfFont implements FontInfo, GenericRecord { |
| |
| /** |
| * The output precision defines how closely the output must match the requested font's height, |
| * width, character orientation, escapement, pitch, and font type. |
| */ |
| public enum WmfOutPrecision { |
| /** |
| * A value that specifies default behavior. |
| */ |
| OUT_DEFAULT_PRECIS(0x00000000), |
| /** |
| * A value that is returned when rasterized fonts are enumerated. |
| */ |
| OUT_STRING_PRECIS(0x00000001), |
| /** |
| * A value that is returned when TrueType and other outline fonts, and |
| * vector fonts are enumerated. |
| */ |
| OUT_STROKE_PRECIS(0x00000003), |
| /** |
| * A value that specifies the choice of a TrueType font when the system |
| * contains multiple fonts with the same name. |
| */ |
| OUT_TT_PRECIS(0x00000004), |
| /** |
| * A value that specifies the choice of a device font when the system |
| * contains multiple fonts with the same name. |
| */ |
| OUT_DEVICE_PRECIS(0x00000005), |
| /** |
| * A value that specifies the choice of a rasterized font when the system |
| * contains multiple fonts with the same name. |
| */ |
| OUT_RASTER_PRECIS(0x00000006), |
| /** |
| * A value that specifies the requirement for only TrueType fonts. If |
| * there are no TrueType fonts installed in the system, default behavior is specified. |
| */ |
| OUT_TT_ONLY_PRECIS(0x00000007), |
| /** |
| * A value that specifies the requirement for TrueType and other outline fonts. |
| */ |
| OUT_OUTLINE_PRECIS (0x00000008), |
| /** |
| * A value that specifies a preference for TrueType and other outline fonts. |
| */ |
| OUT_SCREEN_OUTLINE_PRECIS (0x00000009), |
| /** |
| * A value that specifies a requirement for only PostScript fonts. If there |
| * are no PostScript fonts installed in the system, default behavior is specified. |
| */ |
| OUT_PS_ONLY_PRECIS (0x0000000A); |
| |
| |
| int flag; |
| WmfOutPrecision(int flag) { |
| this.flag = flag; |
| } |
| |
| public static WmfOutPrecision valueOf(int flag) { |
| for (WmfOutPrecision op : values()) { |
| if (op.flag == flag) { |
| return op; |
| } |
| } |
| return null; |
| } |
| } |
| |
| /** |
| * ClipPrecision Flags specify clipping precision, which defines how to clip characters that are |
| * partially outside a clipping region. These flags can be combined to specify multiple options. |
| */ |
| public static class WmfClipPrecision implements GenericRecord { |
| |
| /** Specifies that default clipping MUST be used. */ |
| private static final BitField DEFAULT_PRECIS = BitFieldFactory.getInstance(0x0003); |
| |
| |
| /** This value SHOULD NOT be used. */ |
| private static final BitField CHARACTER_PRECIS = BitFieldFactory.getInstance(0x0001); |
| |
| /** This value MAY be returned when enumerating rasterized, TrueType and vector fonts. */ |
| private static final BitField STROKE_PRECIS = BitFieldFactory.getInstance(0x0002); |
| |
| /** |
| * This value is used to control font rotation, as follows: |
| * If set, the rotation for all fonts SHOULD be determined by the orientation of the coordinate system; |
| * that is, whether the orientation is left-handed or right-handed. |
| * |
| * If clear, device fonts SHOULD rotate counterclockwise, but the rotation of other fonts |
| * SHOULD be determined by the orientation of the coordinate system. |
| */ |
| private static final BitField LH_ANGLES = BitFieldFactory.getInstance(0x0010); |
| |
| /** This value SHOULD NOT be used. */ |
| private static final BitField TT_ALWAYS = BitFieldFactory.getInstance(0x0020); |
| |
| /** This value specifies that font association SHOULD be turned off. */ |
| private static final BitField DFA_DISABLE = BitFieldFactory.getInstance(0x0040); |
| |
| /** |
| * This value specifies that font embedding MUST be used to render document content; |
| * embedded fonts are read-only. |
| */ |
| private static final BitField EMBEDDED = BitFieldFactory.getInstance(0x0080); |
| |
| private static final int[] FLAG_MASKS = { |
| 0x0001, 0x0002, 0x0010, 0x0020, 0x0040, 0x0080 |
| }; |
| |
| private static final String[] FLAG_NAMES = { |
| "CHARACTER_PRECIS", |
| "STROKE_PRECIS", |
| "LH_ANGLES", |
| "TT_ALWAYS", |
| "DFA_DISABLE", |
| "EMBEDDED" |
| }; |
| |
| private int flag; |
| |
| public int init(LittleEndianInputStream leis) { |
| flag = leis.readUByte(); |
| return LittleEndianConsts.BYTE_SIZE; |
| } |
| |
| public boolean isDefaultPrecision() { |
| return !DEFAULT_PRECIS.isSet(flag); |
| } |
| |
| public boolean isCharacterPrecision() { |
| return CHARACTER_PRECIS.isSet(flag); |
| } |
| |
| public boolean isStrokePrecision() { |
| return STROKE_PRECIS.isSet(flag); |
| } |
| |
| public boolean isLeftHandAngles() { |
| return LH_ANGLES.isSet(flag); |
| } |
| |
| public boolean isTrueTypeAlways() { |
| return TT_ALWAYS.isSet(flag); |
| } |
| |
| public boolean isFontAssociated() { |
| return !DFA_DISABLE.isSet(flag); |
| } |
| |
| public boolean useEmbeddedFont() { |
| return EMBEDDED.isSet(flag); |
| } |
| |
| @Override |
| public String toString() { |
| return GenericRecordJsonWriter.marshal(this); |
| } |
| |
| @Override |
| public Map<String, Supplier<?>> getGenericProperties() { |
| return GenericRecordUtil.getGenericProperties( |
| "isDefaultPrecision", this::isDefaultPrecision, |
| "flag", GenericRecordUtil.getBitsAsString(() -> flag, FLAG_MASKS, FLAG_NAMES) |
| ); |
| } |
| } |
| |
| /** |
| * The output quality defines how carefully to attempt to match the logical font attributes to those of an actual |
| * physical font. |
| */ |
| public enum WmfFontQuality { |
| /** |
| * Specifies that the character quality of the font does not matter, so DRAFT_QUALITY can be used. |
| */ |
| DEFAULT_QUALITY (0x00), |
| |
| /** |
| * Specifies that the character quality of the font is less important than the |
| * matching of logical attribuetes. For rasterized fonts, scaling SHOULD be enabled, which |
| * means that more font sizes are available. |
| */ |
| DRAFT_QUALITY (0x01), |
| |
| /** |
| * Specifies that the character quality of the font is more important than the |
| * matching of logical attributes. For rasterized fonts, scaling SHOULD be disabled, and the font |
| * closest in size SHOULD be chosen. |
| */ |
| PROOF_QUALITY (0x02), |
| |
| /** |
| * Specifies that anti-aliasing SHOULD NOT be used when rendering text. |
| */ |
| NONANTIALIASED_QUALITY (0x03), |
| |
| /** |
| * Specifies that anti-aliasing SHOULD be used when rendering text, if the font supports it. |
| */ |
| ANTIALIASED_QUALITY (0x04), |
| |
| /** |
| * Specifies that ClearType anti-aliasing SHOULD be used when rendering text, if the font supports it. |
| * |
| * Fonts that do not support ClearType anti-aliasing include type 1 fonts, PostScript fonts, |
| * OpenType fonts without TrueType outlines, rasterized fonts, vector fonts, and device fonts. |
| */ |
| CLEARTYPE_QUALITY (0x05); |
| |
| int flag; |
| WmfFontQuality(int flag) { |
| this.flag = flag; |
| } |
| |
| public static WmfFontQuality valueOf(int flag) { |
| for (WmfFontQuality fq : values()) { |
| if (fq.flag == flag) { |
| return fq; |
| } |
| } |
| return null; |
| } |
| } |
| |
| |
| /** |
| * A 16-bit signed integer that specifies the height, in logical units, of the font's |
| * character cell. The character height is computed as the character cell height minus the |
| * internal leading. The font mapper SHOULD interpret the height as follows. |
| * |
| * negative value: |
| * The font mapper SHOULD transform this value into device units and match its |
| * absolute value against the character height of available fonts. |
| * |
| * zero value: |
| * A default height value MUST be used when creating a physical font. |
| * |
| * positive value: |
| * The font mapper SHOULD transform this value into device units and match it |
| * against the cell height of available fonts. |
| * |
| * For all height comparisons, the font mapper SHOULD find the largest physical |
| * font that does not exceed the requested size. |
| */ |
| protected double height; |
| |
| /** |
| * A 16-bit signed integer that defines the average width, in logical units, of |
| * characters in the font. If Width is 0x0000, the aspect ratio of the device SHOULD be matched |
| * against the digitization aspect ratio of the available fonts to find the closest match, |
| * determined by the absolute value of the difference. |
| */ |
| protected int width; |
| |
| /** |
| * A 16-bit signed integer that defines the angle, in tenths of degrees, between the |
| * escapement vector and the x-axis of the device. The escapement vector is parallel |
| * to the base line of a row of text. |
| */ |
| protected int escapement; |
| |
| /** |
| * A 16-bit signed integer that defines the angle, in tenths of degrees, |
| * between each character's base line and the x-axis of the device. |
| */ |
| protected int orientation; |
| |
| /** |
| * A 16-bit signed integer that defines the weight of the font in the range 0 |
| * through 1000. For example, 400 is normal and 700 is bold. If this value is 0x0000, |
| * a default weight SHOULD be used. |
| */ |
| protected int weight; |
| |
| /** |
| * A 8-bit Boolean value that specifies the italic attribute of the font. |
| * 0 = not italic / 1 = italic. |
| */ |
| protected boolean italic; |
| |
| /** |
| * An 8-bit Boolean value that specifies the underline attribute of the font. |
| * 0 = not underlined / 1 = underlined |
| */ |
| protected boolean underline; |
| |
| /** |
| * An 8-bit Boolean value that specifies the strike out attribute of the font. |
| * 0 = not striked out / 1 = striked out |
| */ |
| protected boolean strikeOut; |
| |
| /** |
| * An 8-bit unsigned integer that defines the character set. |
| * It SHOULD be set to a value in the {@link FontCharset} Enumeration. |
| * |
| * The DEFAULT_CHARSET value MAY be used to allow the name and size of a font to fully |
| * describe the logical font. If the specified font name does not exist, a font in another character |
| * set MAY be substituted. The DEFAULT_CHARSET value is set to a value based on the current |
| * system locale. For example, when the system locale is United States, it is set to ANSI_CHARSET. |
| * If a typeface name in the FaceName field is specified, the CharSet value MUST match the |
| * character set of that typeface. |
| */ |
| protected FontCharset charSet; |
| |
| /** |
| * An 8-bit unsigned integer that defines the output precision. |
| */ |
| protected WmfOutPrecision outPrecision; |
| |
| /** |
| * An 8-bit unsigned integer that defines the clipping precision. |
| * These flags can be combined to specify multiple options. |
| * |
| * @see WmfClipPrecision |
| */ |
| protected final WmfClipPrecision clipPrecision = new WmfClipPrecision(); |
| |
| /** |
| * An 8-bit unsigned integer that defines the output quality. |
| */ |
| protected WmfFontQuality quality; |
| |
| /** |
| * A PitchAndFamily object that defines the pitch and the family of the font. |
| * Font families specify the look of fonts in a general way and are intended for |
| * specifying fonts when the exact typeface wanted is not available. |
| */ |
| protected int pitchAndFamily; |
| |
| /** |
| * Font families specify the look of fonts in a general way and are |
| * intended for specifying fonts when the exact typeface wanted is not available. |
| * (LSB 4 bits) |
| */ |
| protected FontFamily family; |
| |
| /** |
| * A property of a font that describes the pitch (MSB 2 bits) |
| */ |
| protected FontPitch pitch; |
| |
| /** |
| * A null-terminated string of 8-bit Latin-1 [ISO/IEC-8859-1] ANSI |
| * characters that specifies the typeface name of the font. The length of this string MUST NOT |
| * exceed 32 8-bit characters, including the terminating null. |
| */ |
| protected String facename; |
| |
| public int init(LittleEndianInputStream leis, long recordSize) throws IOException { |
| height = leis.readShort(); |
| width = leis.readShort(); |
| escapement = leis.readShort(); |
| orientation = leis.readShort(); |
| weight = leis.readShort(); |
| italic = leis.readByte() != 0; |
| underline = leis.readByte() != 0; |
| strikeOut = leis.readByte() != 0; |
| charSet = FontCharset.valueOf(leis.readUByte()); |
| outPrecision = WmfOutPrecision.valueOf(leis.readUByte()); |
| clipPrecision.init(leis); |
| quality = WmfFontQuality.valueOf(leis.readUByte()); |
| pitchAndFamily = leis.readUByte(); |
| |
| StringBuilder sb = new StringBuilder(); |
| int readBytes = readString(leis, sb, 32, charSet.getCharset()); |
| if (readBytes == -1) { |
| throw new IOException("Font facename can't be determined."); |
| } |
| facename = sb.toString(); |
| |
| return 5*LittleEndianConsts.SHORT_SIZE+8*LittleEndianConsts.BYTE_SIZE+readBytes; |
| } |
| |
| public void initDefaults() { |
| height = -12; |
| width = 0; |
| escapement = 0; |
| weight = FontHeader.REGULAR_WEIGHT; |
| italic = false; |
| underline = false; |
| strikeOut = false; |
| charSet = FontCharset.ANSI; |
| outPrecision = WmfOutPrecision.OUT_DEFAULT_PRECIS; |
| quality = WmfFontQuality.ANTIALIASED_QUALITY; |
| pitchAndFamily = FontFamily.FF_DONTCARE.getFlag() | (FontPitch.DEFAULT.getNativeId() << 6); |
| facename = "SansSerif"; |
| } |
| |
| public double getHeight() { |
| return height; |
| } |
| |
| public int getWidth() { |
| return width; |
| } |
| |
| public int getEscapement() { |
| return escapement; |
| } |
| |
| public int getOrientation() { |
| return orientation; |
| } |
| |
| public int getWeight() { |
| return weight; |
| } |
| |
| public boolean isItalic() { |
| return italic; |
| } |
| |
| public boolean isUnderline() { |
| return underline; |
| } |
| |
| public boolean isStrikeOut() { |
| return strikeOut; |
| } |
| |
| public WmfOutPrecision getOutPrecision() { |
| return outPrecision; |
| } |
| |
| public WmfClipPrecision getClipPrecision() { |
| return clipPrecision; |
| } |
| |
| public WmfFontQuality getQuality() { |
| return quality; |
| } |
| |
| public int getPitchAndFamily() { |
| return pitchAndFamily; |
| } |
| |
| @Override |
| public FontFamily getFamily() { |
| return FontFamily.valueOf(pitchAndFamily & 0xF); |
| } |
| |
| @Override |
| public FontPitch getPitch() { |
| return FontPitch.valueOf((pitchAndFamily >>> 6) & 3); |
| } |
| |
| @Override |
| public String getTypeface() { |
| return facename; |
| } |
| |
| @Override |
| public FontCharset getCharset() { |
| return charSet; |
| } |
| |
| @Override |
| public String toString() { |
| return GenericRecordJsonWriter.marshal(this); |
| } |
| |
| protected int readString(LittleEndianInputStream leis, StringBuilder sb, int limit) { |
| return readString(leis, sb, limit, StandardCharsets.ISO_8859_1); |
| } |
| |
| protected int readString(LittleEndianInputStream leis, StringBuilder sb, int limit, Charset charset) { |
| byte[] buf = new byte[limit]; |
| byte b; |
| byte readBytes = 0; |
| do { |
| if (readBytes == limit) { |
| return -1; |
| } |
| |
| buf[readBytes++] = b = leis.readByte(); |
| } while (b != 0 && b != -1 && readBytes <= limit); |
| |
| sb.append(new String(buf, 0, readBytes-1, charset == null ? StandardCharsets.ISO_8859_1 : charset)); |
| |
| return readBytes; |
| } |
| |
| @Override |
| public Map<String, Supplier<?>> getGenericProperties() { |
| final Map<String,Supplier<?>> m = new LinkedHashMap<>(); |
| m.put("height", this::getHeight); |
| m.put("width", this::getWidth); |
| m.put("escapment", this::getEscapement); |
| m.put("weight", this::getWeight); |
| m.put("italic", this::isItalic); |
| m.put("underline", this::isUnderline); |
| m.put("strikeOut", this::isStrikeOut); |
| m.put("charset", this::getCharset); |
| m.put("outPrecision", this::getOutPrecision); |
| m.put("clipPrecision", this::getClipPrecision); |
| m.put("quality", this::getQuality); |
| m.put("pitch", this::getPitch); |
| m.put("family", this::getFamily); |
| m.put("typeface", this::getTypeface); |
| return Collections.unmodifiableMap(m); |
| } |
| } |