--- /dev/null
+/* ====================================================================
+ 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.common.usermodel.fonts;
+
+import java.nio.charset.Charset;
+import java.nio.charset.UnsupportedCharsetException;
+
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+
+/**
+ * Charset represents the basic set of characters associated with a font (that it can display), and
+ * corresponds to the ANSI codepage (8-bit or DBCS) of that character set used by a given language.
+ *
+ * @since POI 3.17-beta2
+ */
+public enum FontCharset {
+ /** Specifies the English character set. */
+ ANSI(0x00000000, "Cp1252"),
+ /**
+ * Specifies a character set based on the current system locale;
+ * for example, when the system locale is United States English,
+ * the default character set is ANSI_CHARSET.
+ */
+ DEFAULT(0x00000001, "Cp1252"),
+ /** Specifies a character set of symbols. */
+ SYMBOL(0x00000002, ""),
+ /** Specifies the Apple Macintosh character set. */
+ MAC(0x0000004D, "MacRoman"),
+ /** Specifies the Japanese character set. */
+ SHIFTJIS(0x00000080, "Shift_JIS"),
+ /** Also spelled "Hangeul". Specifies the Hangul Korean character set. */
+ HANGUL(0x00000081, "cp949"),
+ /** Also spelled "Johap". Specifies the Johab Korean character set. */
+ JOHAB(0x00000082, "x-Johab"),
+ /** Specifies the "simplified" Chinese character set for People's Republic of China. */
+ GB2312(0x00000086, "GB2312"),
+ /**
+ * Specifies the "traditional" Chinese character set, used mostly in
+ * Taiwan and in the Hong Kong and Macao Special Administrative Regions.
+ */
+ CHINESEBIG5(0x00000088, "Big5"),
+ /** Specifies the Greek character set. */
+ GREEK(0x000000A1, "Cp1253"),
+ /** Specifies the Turkish character set. */
+ TURKISH(0x000000A2, "Cp1254"),
+ /** Specifies the Vietnamese character set. */
+ VIETNAMESE(0x000000A3, "Cp1258"),
+ /** Specifies the Hebrew character set. */
+ HEBREW(0x000000B1, "Cp1255"),
+ /** Specifies the Arabic character set. */
+ ARABIC(0x000000B2, "Cp1256"),
+ /** Specifies the Baltic (Northeastern European) character set. */
+ BALTIC(0x000000BA, "Cp1257"),
+ /** Specifies the Russian Cyrillic character set. */
+ RUSSIAN(0x000000CC, "Cp1251"),
+ /** Specifies the Thai character set. */
+ THAI_(0x000000DE, "x-windows-874"),
+ /** Specifies a Eastern European character set. */
+ EASTEUROPE(0x000000EE, "Cp1250"),
+ /**
+ * Specifies a mapping to one of the OEM code pages,
+ * according to the current system locale setting.
+ */
+ OEM(0x000000FF, "Cp1252");
+
+ private static FontCharset[] _table = new FontCharset[256];
+
+ private int nativeId;
+ private Charset charset;
+
+
+ static {
+ for (FontCharset c : values()) {
+ _table[c.getNativeId()] = c;
+ }
+ }
+
+ FontCharset(int flag, String javaCharsetName) {
+ this.nativeId = flag;
+ if (javaCharsetName.length() > 0) {
+ try {
+ charset = Charset.forName(javaCharsetName);
+ return;
+ } catch (UnsupportedCharsetException e) {
+ POILogger logger = POILogFactory.getLogger(FontCharset.class);
+ logger.log(POILogger.WARN, "Unsupported charset: "+javaCharsetName);
+ }
+ }
+ charset = null;
+ }
+
+ /**
+ *
+ * @return charset for the font or <code>null</code> if there is no matching charset or
+ * if the charset is a "default"
+ */
+ public Charset getCharset() {
+ return charset;
+ }
+
+ public int getNativeId() {
+ return nativeId;
+ }
+
+ public static FontCharset valueOf(int value){
+ return (value < 0 || value >= _table.length) ? null :_table[value];
+ }
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+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.common.usermodel.fonts;
+
+/**
+ * A property of a font that describes its general appearance.
+ *
+ * @since POI 3.17-beta2
+ */
+public enum FontFamily {
+ /**
+ * The default font is specified, which is implementation-dependent.
+ */
+ FF_DONTCARE (0x00),
+ /**
+ * Fonts with variable stroke widths, which are proportional to the actual widths of
+ * the glyphs, and which have serifs. "MS Serif" is an example.
+ */
+ FF_ROMAN (0x01),
+ /**
+ * Fonts with variable stroke widths, which are proportional to the actual widths of the
+ * glyphs, and which do not have serifs. "MS Sans Serif" is an example.
+ */
+ FF_SWISS (0x02),
+ /**
+ * Fonts with constant stroke width, with or without serifs. Fixed-width fonts are
+ * usually modern. "Pica", "Elite", and "Courier New" are examples.
+ */
+ FF_MODERN (0x03),
+ /**
+ * Fonts designed to look like handwriting. "Script" and "Cursive" are examples.
+ */
+ FF_SCRIPT (0x04),
+ /**
+ * Novelty fonts. "Old English" is an example.
+ */
+ FF_DECORATIVE (0x05);
+
+ private int nativeId;
+ private FontFamily(int nativeId) {
+ this.nativeId = nativeId;
+ }
+
+ public int getFlag() {
+ return nativeId;
+ }
+
+ public static FontFamily valueOf(int nativeId) {
+ for (FontFamily ff : values()) {
+ if (ff.nativeId == nativeId) {
+ return ff;
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Get FontFamily from combined native id
+ */
+ public static FontFamily valueOfPitchFamily(byte pitchAndFamily) {
+ return valueOf(pitchAndFamily >>> 4);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+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.common.usermodel.fonts;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+/**
+ * Text runs can contain characters which will be handled (if configured) by a different font,
+ * because the default font (latin) doesn't contain corresponding glyphs.
+ *
+ * @since POI 3.17-beta2
+ *
+ * @see <a href="https://blogs.msdn.microsoft.com/officeinteroperability/2013/04/22/office-open-xml-themes-schemes-and-fonts/">Office Open XML Themes, Schemes, and Fonts</a>
+ */
+public enum FontGroup {
+ /** type for latin charset (default) - also used for unicode fonts like MS Arial Unicode */
+ LATIN,
+ /** type for east asian charsets - usually set as fallback for the latin font, e.g. something like MS Gothic or MS Mincho */
+ EAST_ASIAN,
+ /** type for symbol fonts */
+ SYMBOL,
+ /** type for complex scripts - see https://msdn.microsoft.com/en-us/library/windows/desktop/dd317698 */
+ COMPLEX_SCRIPT
+ ;
+
+
+ public static class FontGroupRange {
+ private int len;
+ private FontGroup fontGroup;
+ public int getLength() {
+ return len;
+ }
+ public FontGroup getFontGroup( ) {
+ return fontGroup;
+ }
+ }
+
+ private static class Range {
+ int upper;
+ FontGroup fontGroup;
+ Range(int upper, FontGroup fontGroup) {
+ this.upper = upper;
+ this.fontGroup = fontGroup;
+ }
+ }
+
+ private static NavigableMap<Integer,Range> UCS_RANGES;
+
+ static {
+ UCS_RANGES = new TreeMap<Integer,Range>();
+ UCS_RANGES.put(0x0000, new Range(0x007F, LATIN));
+ UCS_RANGES.put(0x0080, new Range(0x00A6, LATIN));
+ UCS_RANGES.put(0x00A9, new Range(0x00AF, LATIN));
+ UCS_RANGES.put(0x00B2, new Range(0x00B3, LATIN));
+ UCS_RANGES.put(0x00B5, new Range(0x00D6, LATIN));
+ UCS_RANGES.put(0x00D8, new Range(0x00F6, LATIN));
+ UCS_RANGES.put(0x00F8, new Range(0x058F, LATIN));
+ UCS_RANGES.put(0x0590, new Range(0x074F, COMPLEX_SCRIPT));
+ UCS_RANGES.put(0x0780, new Range(0x07BF, COMPLEX_SCRIPT));
+ UCS_RANGES.put(0x0900, new Range(0x109F, COMPLEX_SCRIPT));
+ UCS_RANGES.put(0x10A0, new Range(0x10FF, LATIN));
+ UCS_RANGES.put(0x1200, new Range(0x137F, LATIN));
+ UCS_RANGES.put(0x13A0, new Range(0x177F, LATIN));
+ UCS_RANGES.put(0x1D00, new Range(0x1D7F, LATIN));
+ UCS_RANGES.put(0x1E00, new Range(0x1FFF, LATIN));
+ UCS_RANGES.put(0x1780, new Range(0x18AF, COMPLEX_SCRIPT));
+ UCS_RANGES.put(0x2000, new Range(0x200B, LATIN));
+ UCS_RANGES.put(0x200C, new Range(0x200F, COMPLEX_SCRIPT));
+ // For the quote characters in the range U+2018 - U+201E, use the East Asian font
+ // if the text has one of the following language identifiers:
+ // ii-CN, ja-JP, ko-KR, zh-CN,zh-HK, zh-MO, zh-SG, zh-TW
+ UCS_RANGES.put(0x2010, new Range(0x2029, LATIN));
+ UCS_RANGES.put(0x202A, new Range(0x202F, COMPLEX_SCRIPT));
+ UCS_RANGES.put(0x2030, new Range(0x2046, LATIN));
+ UCS_RANGES.put(0x204A, new Range(0x245F, LATIN));
+ UCS_RANGES.put(0x2670, new Range(0x2671, COMPLEX_SCRIPT));
+ UCS_RANGES.put(0x27C0, new Range(0x2BFF, LATIN));
+ UCS_RANGES.put(0x3099, new Range(0x309A, EAST_ASIAN));
+ UCS_RANGES.put(0xD835, new Range(0xD835, LATIN));
+ UCS_RANGES.put(0xF000, new Range(0xF0FF, SYMBOL));
+ UCS_RANGES.put(0xFB00, new Range(0xFB17, LATIN));
+ UCS_RANGES.put(0xFB1D, new Range(0xFB4F, COMPLEX_SCRIPT));
+ UCS_RANGES.put(0xFE50, new Range(0xFE6F, LATIN));
+ // All others EAST_ASIAN
+ };
+
+
+ /**
+ * Try to guess the font group based on the codepoint
+ *
+ * @param runText the text which font groups are to be analyzed
+ * @return the FontGroup
+ */
+ public static List<FontGroupRange> getFontGroupRanges(String runText) {
+ List<FontGroupRange> ttrList = new ArrayList<FontGroupRange>();
+ FontGroupRange ttrLast = null;
+ final int rlen = (runText != null) ? runText.length() : 0;
+ for(int cp, i = 0, charCount; i < rlen; i += charCount) {
+ cp = runText.codePointAt(i);
+ charCount = Character.charCount(cp);
+
+ // don't switch the font group for a few default characters supposedly available in all fonts
+ FontGroup tt;
+ if (ttrLast != null && " \n\r".indexOf(cp) > -1) {
+ tt = ttrLast.fontGroup;
+ } else {
+ tt = lookup(cp);
+ }
+
+ if (ttrLast == null || ttrLast.fontGroup != tt) {
+ ttrLast = new FontGroupRange();
+ ttrLast.fontGroup = tt;
+ ttrList.add(ttrLast);
+ }
+ ttrLast.len += charCount;
+ }
+ return ttrList;
+ }
+
+ public static FontGroup getFontGroupFirst(String runText) {
+ return (runText == null || runText.isEmpty()) ? LATIN : lookup(runText.codePointAt(0));
+ }
+
+ private static FontGroup lookup(int codepoint) {
+ // Do a lookup for a match in UCS_RANGES
+ Map.Entry<Integer,Range> entry = UCS_RANGES.floorEntry(codepoint);
+ Range range = (entry != null) ? entry.getValue() : null;
+ return (range != null && codepoint <= range.upper) ? range.fontGroup : EAST_ASIAN;
+ }
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+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.common.usermodel.fonts;
+
+/**
+ * A FontInfo object holds information about a font configuration.
+ * It is roughly an equivalent to the LOGFONT structure in Windows GDI.<p>
+ *
+ * If an implementation doesn't provide a property, the getter will return {@code null} -
+ * if the value is unset, a default value will be returned.<p>
+ *
+ * Setting a unsupported property results in an {@link UnsupportedOperationException}.
+ *
+ * @since POI 3.17-beta2
+ *
+ * @see <a href="https://msdn.microsoft.com/en-us/library/dd145037.aspx">LOGFONT structure</a>
+ */
+public interface FontInfo {
+
+ /**
+ * Get the index within the collection of Font objects
+ * @return unique index number of the underlying record this Font represents
+ * (probably you don't care unless you're comparing which one is which)
+ */
+ Integer getIndex();
+
+ /**
+ * Sets the index within the collection of Font objects
+ *
+ * @param index the index within the collection of Font objects
+ *
+ * @throws UnsupportedOperationException if unsupported
+ */
+ void setIndex(int index);
+
+
+ /**
+ * @return the full name of the font, i.e. font family + type face
+ */
+ String getTypeface();
+
+ /**
+ * Sets the font name
+ *
+ * @param typeface the full name of the font, when {@code null} removes the font definition -
+ * removal is implementation specific
+ */
+ void setTypeface(String typeface);
+
+ /**
+ * @return the font charset
+ */
+ FontCharset getCharset();
+
+ /**
+ * Sets the charset
+ *
+ * @param charset the charset
+ */
+ void setCharset(FontCharset charset);
+
+ /**
+ * @return the family class
+ */
+ FontFamily getFamily();
+
+ /**
+ * Sets the font family class
+ *
+ * @param family the font family class
+ */
+ void setFamily(FontFamily family);
+
+ /**
+ * @return the font pitch or {@code null} if unsupported
+ */
+ FontPitch getPitch();
+
+ /**
+ * Set the font pitch
+ *
+ * @param pitch the font pitch
+ *
+ * @throws UnsupportedOperationException if unsupported
+ */
+ void setPitch(FontPitch pitch);
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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.common.usermodel.fonts;
+
+/**
+ * A property of a font that describes the pitch, of the characters.
+ *
+ * @since POI 3.17-beta2
+ */
+public enum FontPitch {
+ /**
+ * The default pitch, which is implementation-dependent.
+ */
+ DEFAULT (0x00),
+ /**
+ * A fixed pitch, which means that all the characters in the font occupy the same
+ * width when output in a string.
+ */
+ FIXED (0x01),
+ /**
+ * A variable pitch, which means that the characters in the font occupy widths
+ * that are proportional to the actual widths of the glyphs when output in a string. For example,
+ * the "i" and space characters usually have much smaller widths than a "W" or "O" character.
+ */
+ VARIABLE (0x02);
+
+ private int nativeId;
+ FontPitch(int nativeId) {
+ this.nativeId = nativeId;
+ }
+
+ public int getNativeId() {
+ return nativeId;
+ }
+
+ public static FontPitch valueOf(int flag) {
+ for (FontPitch fp : values()) {
+ if (fp.nativeId == flag) return fp;
+ }
+ return null;
+ }
+
+ /**
+ * Combine pitch and family to native id
+ *
+ * @see <a href="https://msdn.microsoft.com/en-us/library/dd145037.aspx">LOGFONT structure</a>
+ */
+ public static byte getNativeId(FontPitch pitch, FontFamily family) {
+ return (byte)(pitch.getNativeId() | (family.getFlag() << 4));
+ }
+
+ /**
+ * Get FontPitch from native id
+ */
+ public static FontPitch valueOfPitchFamily(byte pitchAndFamily) {
+ return valueOf(pitchAndFamily & 0x3);
+ }
+}
+
}
}
}
+
+ /**
+ * Return a FontManager, either registered beforehand or a default implementation
+ *
+ * @param graphics the graphics context holding potentially a font manager
+ * @return the font manager
+ */
+ public DrawFontManager getFontManager(Graphics2D graphics) {
+ DrawFontManager fontHandler = (DrawFontManager)graphics.getRenderingHint(Drawable.FONT_HANDLER);
+ return (fontHandler != null) ? fontHandler : new DrawFontManagerDefault();
+ }
}
\ No newline at end of file
--- /dev/null
+/*
+ * ====================================================================
+ * 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.sl.draw;
+
+import org.apache.poi.common.usermodel.fonts.FontCharset;
+import org.apache.poi.common.usermodel.fonts.FontFamily;
+import org.apache.poi.common.usermodel.fonts.FontInfo;
+import org.apache.poi.common.usermodel.fonts.FontPitch;
+import org.apache.poi.util.Internal;
+
+/**
+ * Convenience class to handle FontInfo mappings
+ */
+@Internal
+/* package */ class DrawFontInfo implements FontInfo {
+
+ private final String typeface;
+
+ DrawFontInfo(String typeface) {
+ this.typeface = typeface;
+ }
+
+ @Override
+ public Integer getIndex() {
+ return null;
+ }
+
+ @Override
+ public void setIndex(int index) {
+ throw new UnsupportedOperationException("DrawFontManagers FontInfo can't be changed.");
+ }
+
+ @Override
+ public String getTypeface() {
+ return typeface;
+ }
+
+ @Override
+ public void setTypeface(String typeface) {
+ throw new UnsupportedOperationException("DrawFontManagers FontInfo can't be changed.");
+ }
+
+ @Override
+ public FontCharset getCharset() {
+ return FontCharset.ANSI;
+ }
+
+ @Override
+ public void setCharset(FontCharset charset) {
+ throw new UnsupportedOperationException("DrawFontManagers FontInfo can't be changed.");
+ }
+
+ @Override
+ public FontFamily getFamily() {
+ return FontFamily.FF_SWISS;
+ }
+
+ @Override
+ public void setFamily(FontFamily family) {
+ throw new UnsupportedOperationException("DrawFontManagers FontInfo can't be changed.");
+ }
+
+ @Override
+ public FontPitch getPitch() {
+ return FontPitch.VARIABLE;
+ }
+
+ @Override
+ public void setPitch(FontPitch pitch) {
+ throw new UnsupportedOperationException("DrawFontManagers FontInfo can't be changed.");
+ }
+}
package org.apache.poi.sl.draw;
+import java.awt.Font;
+import java.awt.Graphics2D;
+
+import org.apache.poi.common.usermodel.fonts.FontInfo;
+import org.apache.poi.util.StringUtil;
+
/**
* Manages fonts when rendering slides.
*
/**
* select a font to be used to paint text
*
- * @param typeface the font family as defined in the .pptx file.
- * This can be unknown or missing in the graphic environment.
- * @param pitchFamily a pitch-and-family,
- * see {@link org.apache.poi.hwmf.record.HwmfFont#getFamily()} and
- * {@link org.apache.poi.hwmf.record.HwmfFont#getPitch()}
- * for how to calculate those (ancient) values
+ * @param graphics the graphics context to request additional rendering hints
+ * @param fontInfo the font info object corresponding to the text run font
*
* @return the font to be used to paint text
*/
- String getRendererableFont(String typeface, int pitchFamily);
+ FontInfo getMappedFont(Graphics2D graphics, FontInfo fontInfo);
/**
* In case the original font doesn't contain a glyph, use the
* returned fallback font as an alternative
*
- * @param typeface the font family as defined in the .pptx file.
- * @param pitchFamily a pitch-and-family,
- * see {@link org.apache.poi.hwmf.record.HwmfFont#getFamily()} and
- * {@link org.apache.poi.hwmf.record.HwmfFont#getPitch()}
- * for how to calculate those (ancient) values
+ * @param graphics the graphics context to request additional rendering hints
+ * @param fontInfo the font info object corresponding to the text run font
*
* @return the font to be used as a fallback for the original typeface
*/
- String getFallbackFont(String typeface, int pitchFamily);
+ FontInfo getFallbackFont(Graphics2D graphics, FontInfo fontInfo);
+
+ /**
+ * Map text charset depending on font family.<p>
+ *
+ * Currently this only maps for wingdings font (into unicode private use area)
+ *
+ * @param graphics the graphics context to request additional rendering hints
+ * @param fontInfo the font info object corresponding to the text run font
+ * @param text the raw text
+ *
+ * @return String with mapped codepoints
+ *
+ * @see <a href="http://stackoverflow.com/questions/8692095">Drawing exotic fonts in a java applet</a>
+ * @see StringUtil#mapMsCodepointString(String)
+ */
+ String mapFontCharset(Graphics2D graphics, FontInfo fontInfo, String text);
+
+ /**
+ * Create an AWT font object with the given attributes
+ *
+ * @param graphics the graphics context to request additional rendering hints
+ * @param fontInfo the font info object corresponding to the text run font
+ * @param size the font size in points
+ * @param bold {@code true} if the font is bold
+ * @param italic {@code true} if the font is italic
+ *
+ * @return the AWT font object
+ */
+ Font createAWTFont(Graphics2D graphics, FontInfo fontInfo, double size, boolean bold, boolean italic);
}
--- /dev/null
+/*
+ * ====================================================================
+ * 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.sl.draw;
+
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.util.Map;
+
+import org.apache.poi.common.usermodel.fonts.FontInfo;
+import org.apache.poi.sl.draw.Drawable.DrawableHint;
+
+/**
+ * Manages fonts when rendering slides.
+ *
+ * Use this class to handle unknown / missing fonts or to substitute fonts
+ */
+public class DrawFontManagerDefault implements DrawFontManager {
+
+ @Override
+ public FontInfo getMappedFont(Graphics2D graphics, FontInfo fontInfo) {
+ return getFontWithFallback(graphics, Drawable.FONT_MAP, fontInfo);
+ }
+
+ @Override
+ public FontInfo getFallbackFont(Graphics2D graphics, FontInfo fontInfo) {
+ FontInfo fi = getFontWithFallback(graphics, Drawable.FONT_FALLBACK, fontInfo);
+ if (fi == null) {
+ fi = new DrawFontInfo(Font.SANS_SERIF);
+ }
+ return fi;
+ }
+
+ public String mapFontCharset(Graphics2D graphics, FontInfo fontInfo, String text) {
+ // TODO: find a real charset mapping solution instead of hard coding for Wingdings
+ String attStr = text;
+ if (fontInfo != null && "Wingdings".equalsIgnoreCase(fontInfo.getTypeface())) {
+ // wingdings doesn't contain high-surrogates, so chars are ok
+ boolean changed = false;
+ char chrs[] = attStr.toCharArray();
+ for (int i=0; i<chrs.length; i++) {
+ // only change valid chars
+ if ((0x20 <= chrs[i] && chrs[i] <= 0x7f) ||
+ (0xa0 <= chrs[i] && chrs[i] <= 0xff)) {
+ chrs[i] |= 0xf000;
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ attStr = new String(chrs);
+ }
+ }
+ return attStr;
+ }
+
+ @Override
+ public Font createAWTFont(Graphics2D graphics, FontInfo fontInfo, double fontSize, boolean bold, boolean italic) {
+ int style = (bold ? Font.BOLD : 0) | (italic ? Font.ITALIC : 0);
+ Font font = new Font(fontInfo.getTypeface(), style, 12);
+ if (Font.DIALOG.equals(font.getFamily())) {
+ // SansSerif is a better choice than Dialog
+ font = new Font(Font.SANS_SERIF, style, 12);
+ }
+ return font.deriveFont((float)fontSize);
+ }
+
+ private FontInfo getFontWithFallback(Graphics2D graphics, DrawableHint hint, FontInfo fontInfo) {
+ @SuppressWarnings("unchecked")
+ Map<String,String> fontMap = (Map<String,String>)graphics.getRenderingHint(hint);
+ if (fontMap == null) {
+ return fontInfo;
+ }
+
+ String f = (fontInfo != null) ? fontInfo.getTypeface() : null;
+ String mappedTypeface = null;
+ if (fontMap.containsKey(f)) {
+ mappedTypeface = fontMap.get(f);
+ } else if (fontMap.containsKey("*")) {
+ mappedTypeface = fontMap.get("*");
+ }
+
+ return (mappedTypeface != null) ? new DrawFontInfo(mappedTypeface) : fontInfo;
+ }
+}
* @return full height of this text run which is sum of ascent, descent and leading
*/
public float getHeight(){
- double h = Math.ceil(layout.getAscent()) + Math.ceil(layout.getDescent()) + getLeading();
+ double h = layout.getAscent() + layout.getDescent() + getLeading();
return (float)h;
}
* @return the leading height before/after a text line
*/
public float getLeading() {
- // fix invalid leadings (leading == 0) by fallback to descent
+ // fix invalid leadings (leading == 0)
double l = layout.getLeading();
- return (float)(l == 0 ? layout.getDescent() : l);
+ if (l == 0) {
+ // see https://stackoverflow.com/questions/925147
+ // we use a 115% value instead of the 120% proposed one, as this seems to be closer to LO/OO
+ l = (layout.getAscent()+layout.getDescent())*0.15;
+ }
+ return (float)l;
}
/**
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
+import java.util.Locale;
+import org.apache.poi.common.usermodel.fonts.FontGroup;
+import org.apache.poi.common.usermodel.fonts.FontGroup.FontGroupRange;
+import org.apache.poi.common.usermodel.fonts.FontInfo;
import org.apache.poi.sl.usermodel.AutoNumberingScheme;
import org.apache.poi.sl.usermodel.Hyperlink;
import org.apache.poi.sl.usermodel.Insets2D;
import org.apache.poi.sl.usermodel.TextRun.TextCap;
import org.apache.poi.sl.usermodel.TextShape;
import org.apache.poi.sl.usermodel.TextShape.TextDirection;
-import org.apache.poi.util.StringUtil;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
import org.apache.poi.util.Units;
public class DrawTextParagraph implements Drawable {
+ private static final POILogger LOG = POILogFactory.getLogger(DrawTextParagraph.class);
+
/** Keys for passing hyperlinks to the graphics context */
public static final XlinkAttribute HYPERLINK_HREF = new XlinkAttribute("href");
public static final XlinkAttribute HYPERLINK_LABEL = new XlinkAttribute("label");
line.setPosition(penX, penY);
line.draw(graphics);
-
+
if(spacing > 0) {
// If linespacing >= 0, then linespacing is a percentage of normal line height.
penY += spacing*0.01* line.getHeight();
return null;
}
- String buFont = bulletStyle.getBulletFont();
- if (buFont == null) {
- buFont = paragraph.getDefaultFontFamily();
- }
- assert(buFont != null);
-
PlaceableShape<?,?> ps = getParagraphShape();
PaintStyle fgPaintStyle = bulletStyle.getBulletFontColor();
Paint fgPaint;
fontSize = (float)-buSz;
}
+ String buFontStr = bulletStyle.getBulletFont();
+ if (buFontStr == null) {
+ buFontStr = paragraph.getDefaultFontFamily();
+ }
+ assert(buFontStr != null);
+ FontInfo buFont = new DrawFontInfo(buFontStr);
+
+
+ DrawFontManager dfm = DrawFactory.getInstance(graphics).getFontManager(graphics);
+ // TODO: check font group defaulting to Symbol
+ buFont = dfm.getMappedFont(graphics, buFont);
- AttributedString str = new AttributedString(mapFontCharset(buCharacter,buFont));
+ AttributedString str = new AttributedString(dfm.mapFontCharset(graphics,buFont,buCharacter));
str.addAttribute(TextAttribute.FOREGROUND, fgPaint);
- str.addAttribute(TextAttribute.FAMILY, buFont);
+ str.addAttribute(TextAttribute.FAMILY, buFont.getTypeface());
str.addAttribute(TextAttribute.SIZE, fontSize);
TextLayout layout = new TextLayout(str.getIterator(), graphics.getFontRenderContext());
protected String getRenderableText(Graphics2D graphics, TextRun tr) {
if (tr.getFieldType() == FieldType.SLIDE_NUMBER) {
Slide<?,?> slide = (Slide<?,?>)graphics.getRenderingHint(Drawable.CURRENT_SLIDE);
- return (slide == null) ? "" : Integer.toString(slide.getSlideNumber());
+ return (slide == null) ? "" : Integer.toString(slide.getSlideNumber());
}
StringBuilder buf = new StringBuilder();
TextCap cap = tr.getTextCap();
}
PlaceableShape<?,?> ps = getParagraphShape();
- DrawFontManager fontHandler = (DrawFontManager)graphics.getRenderingHint(Drawable.FONT_HANDLER);
- @SuppressWarnings("unchecked")
- Map<String,String> fontMap = (Map<String,String>)graphics.getRenderingHint(Drawable.FONT_MAP);
- @SuppressWarnings("unchecked")
- Map<String,String> fallbackMap = (Map<String,String>)graphics.getRenderingHint(Drawable.FONT_FALLBACK);
+ DrawFontManager dfm = DrawFactory.getInstance(graphics).getFontManager(graphics);
+ assert(dfm != null);
for (TextRun run : paragraph){
String runText = getRenderableText(graphics, run);
}
// user can pass an custom object to convert fonts
- String mappedFont = run.getFontFamily();
- String fallbackFont = Font.SANS_SERIF;
- if (mappedFont == null) {
- mappedFont = paragraph.getDefaultFontFamily();
- }
- if (mappedFont == null) {
- mappedFont = Font.SANS_SERIF;
- }
- if (fontHandler != null) {
- String font = fontHandler.getRendererableFont(mappedFont, run.getPitchAndFamily());
- if (font != null) {
- mappedFont = font;
- }
- font = fontHandler.getFallbackFont(mappedFont, run.getPitchAndFamily());
- if (font != null) {
- fallbackFont = font;
- }
- } else {
- mappedFont = getFontWithFallback(fontMap, mappedFont);
- fallbackFont = getFontWithFallback(fallbackMap, mappedFont);
- }
-
- runText = mapFontCharset(runText,mappedFont);
+ runText = dfm.mapFontCharset(graphics, run.getFontInfo(null), runText);
int beginIndex = text.length();
text.append(runText);
int endIndex = text.length();
- attList.add(new AttributedStringData(TextAttribute.FAMILY, mappedFont, beginIndex, endIndex));
-
PaintStyle fgPaintStyle = run.getFontColor();
Paint fgPaint = new DrawPaint(ps).getPaint(graphics, fgPaintStyle);
attList.add(new AttributedStringData(TextAttribute.FOREGROUND, fgPaint, beginIndex, endIndex));
if(run.isSuperscript()) {
attList.add(new AttributedStringData(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, beginIndex, endIndex));
}
-
+
Hyperlink<?,?> hl = run.getHyperlink();
if (hl != null) {
attList.add(new AttributedStringData(HYPERLINK_HREF, hl.getAddress(), beginIndex, endIndex));
attList.add(new AttributedStringData(HYPERLINK_LABEL, hl.getLabel(), beginIndex, endIndex));
}
-
- int style = (run.isBold() ? Font.BOLD : 0) | (run.isItalic() ? Font.ITALIC : 0);
- Font f = new Font(mappedFont, style, (int)Math.rint(fontSz));
-
- // check for unsupported characters and add a fallback font for these
- char textChr[] = runText.toCharArray();
- int nextEnd = canDisplayUpTo(f, textChr, 0, textChr.length);
- int last = nextEnd;
- boolean isNextValid = (nextEnd == 0);
- while ( nextEnd != -1 && nextEnd <= textChr.length ) {
- if (isNextValid) {
- nextEnd = canDisplayUpTo(f, textChr, nextEnd, textChr.length);
- isNextValid = false;
- } else {
- if (nextEnd >= textChr.length || f.canDisplay(Character.codePointAt(textChr, nextEnd, textChr.length)) ) {
- attList.add(new AttributedStringData(TextAttribute.FAMILY, fallbackFont, beginIndex+last, beginIndex+Math.min(nextEnd,textChr.length)));
- if (nextEnd >= textChr.length) {
- break;
- }
- last = nextEnd;
- isNextValid = true;
- } else {
- boolean isHS = Character.isHighSurrogate(textChr[nextEnd]);
- nextEnd+=(isHS?2:1);
- }
- }
- }
+
+ processGlyphs(graphics, dfm, attList, beginIndex, run, runText);
}
// ensure that the paragraph contains at least one character
return string;
}
- private String getFontWithFallback(Map<String, String> fontMap, String mappedFont) {
- if (fontMap != null) {
- if (fontMap.containsKey(mappedFont)) {
- mappedFont = fontMap.get(mappedFont);
- } else if (fontMap.containsKey("*")) {
- mappedFont = fontMap.get("*");
- }
- }
- return mappedFont;
- }
-
- /**
- * @return {@code true} if the HSLF implementation is used
- */
- protected boolean isHSLF() {
- return DrawShape.isHSLF(paragraph.getParentShape());
- }
-
/**
- * Map text charset depending on font family.
- * Currently this only maps for wingdings font (into unicode private use area)
+ * Processing the glyphs is done in two steps.
+ * <li>determine the font group - a text run can have different font groups. Depending on the chars,
+ * the correct font group needs to be used
*
- * @param text the raw text
- * @param fontFamily the font family
- * @return AttributedString with mapped codepoints
+ * @param graphics
+ * @param dfm
+ * @param attList
+ * @param beginIndex
+ * @param run
+ * @param runText
*
- * @see <a href="http://stackoverflow.com/questions/8692095">Drawing exotic fonts in a java applet</a>
- * @see StringUtil#mapMsCodepointString(String)
+ * @see <a href="https://blogs.msdn.microsoft.com/officeinteroperability/2013/04/22/office-open-xml-themes-schemes-and-fonts/">Office Open XML Themes, Schemes, and Fonts</a>
*/
- protected String mapFontCharset(String text, String fontFamily) {
- // TODO: find a real charset mapping solution instead of hard coding for Wingdings
- String attStr = text;
- if ("Wingdings".equalsIgnoreCase(fontFamily)) {
- // wingdings doesn't contain high-surrogates, so chars are ok
- boolean changed = false;
- char chrs[] = attStr.toCharArray();
- for (int i=0; i<chrs.length; i++) {
- // only change valid chars
- if ((0x20 <= chrs[i] && chrs[i] <= 0x7f) ||
- (0xa0 <= chrs[i] && chrs[i] <= 0xff)) {
- chrs[i] |= 0xf000;
- changed = true;
- }
+ private void processGlyphs(Graphics2D graphics, DrawFontManager dfm, List<AttributedStringData> attList, final int beginIndex, TextRun run, String runText) {
+ // determine font group ranges of the textrun to focus the fallback handling only on that font group
+ List<FontGroupRange> ttrList = FontGroup.getFontGroupRanges(runText);
+ int rangeBegin = 0;
+ for (FontGroupRange ttr : ttrList) {
+ FontInfo fiRun = run.getFontInfo(ttr.getFontGroup());
+ if (fiRun == null) {
+ // if the font group specific font wasn't defined, fallback to LATIN
+ fiRun = run.getFontInfo(FontGroup.LATIN);
+ }
+ FontInfo fiMapped = dfm.getMappedFont(graphics, fiRun);
+ FontInfo fiFallback = dfm.getFallbackFont(graphics, fiRun);
+ assert(fiFallback != null);
+ if (fiMapped == null) {
+ fiMapped = dfm.getMappedFont(graphics, new DrawFontInfo(paragraph.getDefaultFontFamily()));
+ }
+ if (fiMapped == null) {
+ fiMapped = fiFallback;
}
- if (changed) {
- attStr = new String(chrs);
+ Font fontMapped = dfm.createAWTFont(graphics, fiMapped, 10, run.isBold(), run.isItalic());
+ Font fontFallback = dfm.createAWTFont(graphics, fiFallback, 10, run.isBold(), run.isItalic());
+
+ // check for unsupported characters and add a fallback font for these
+ final int rangeLen = ttr.getLength();
+ int partEnd = rangeBegin;
+ while (partEnd<rangeBegin+rangeLen) {
+ // start with the assumption that the font is able to display the chars
+ int partBegin = partEnd;
+ partEnd = nextPart(fontMapped, runText, partBegin, rangeBegin+rangeLen, true);
+
+ // Now we have 3 cases:
+ // (a) the first part couldn't be displayed,
+ // (b) only part of the text run could be displayed
+ // (c) or all chars can be displayed (default)
+
+ if (partBegin < partEnd) {
+ // handle (b) and (c)
+ attList.add(new AttributedStringData(TextAttribute.FAMILY, fontMapped.getFontName(Locale.ROOT), beginIndex+partBegin, beginIndex+partEnd));
+ if (LOG.check(POILogger.DEBUG)) {
+ LOG.log(POILogger.DEBUG, "mapped: ",fontMapped.getFontName(Locale.ROOT)," ",(beginIndex+partBegin)," ",(beginIndex+partEnd)," - ",runText.substring(beginIndex+partBegin, beginIndex+partEnd));
+ }
+ }
+
+ // fallback for unsupported glyphs
+ partBegin = partEnd;
+ partEnd = nextPart(fontMapped, runText, partBegin, rangeBegin+rangeLen, false);
+
+ if (partBegin < partEnd) {
+ // handle (a) and (b)
+ attList.add(new AttributedStringData(TextAttribute.FAMILY, fontFallback.getFontName(Locale.ROOT), beginIndex+partBegin, beginIndex+partEnd));
+ if (LOG.check(POILogger.DEBUG)) {
+ LOG.log(POILogger.DEBUG, "fallback: ",fontFallback.getFontName(Locale.ROOT)," ",(beginIndex+partBegin)," ",(beginIndex+partEnd)," - ",runText.substring(beginIndex+partBegin, beginIndex+partEnd));
+ }
+ }
}
+
+ rangeBegin += rangeLen;
}
- return attStr;
}
- /**
- * Indicates whether or not this {@code Font} can display the characters in the specified {@code text}
- * starting at {@code start} and ending at {@code limit}.<p>
- *
- * This is a workaround for the Java 6 implementation of {@link Font#canDisplayUpTo(char[], int, int)}
- *
- * @param font the font to inspect
- * @param text the specified array of {@code char} values
- * @param start the specified starting offset (in
- * {@code char}s) into the specified array of
- * {@code char} values
- * @param limit the specified ending offset (in
- * {@code char}s) into the specified array of
- * {@code char} values
- * @return an offset into {@code text} that points
- * to the first character in {@code text} that this
- * {@code Font} cannot display; or {@code -1} if
- * this {@code Font} can display all characters in
- * {@code text}.
- *
- * @see <a href="https://bugs.openjdk.java.net/browse/JDK-6623219">Font.canDisplayUpTo does not work with supplementary characters</a>
- */
- protected static int canDisplayUpTo(Font font, char[] text, int start, int limit) {
- for (int i = start; i < limit; i++) {
- char c = text[i];
- if (font.canDisplay(c)) {
- continue;
- }
- if (!Character.isHighSurrogate(c)) {
- return i;
- }
- if (!font.canDisplay(Character.codePointAt(text, i, limit))) {
- return i;
+ private static int nextPart(Font fontMapped, String runText, int beginPart, int endPart, boolean isDisplayed) {
+ int rIdx = beginPart;
+ while (rIdx < endPart) {
+ int codepoint = runText.codePointAt(rIdx);
+ if (fontMapped.canDisplay(codepoint) != isDisplayed) {
+ break;
}
- i++;
+ rIdx += Character.charCount(codepoint);
}
- return -1;
+ return rIdx;
+ }
+
+ /**
+ * @return {@code true} if the HSLF implementation is used
+ */
+ protected boolean isHSLF() {
+ return DrawShape.isHSLF(paragraph.getParentShape());
}
}
import java.awt.Color;
+import org.apache.poi.common.usermodel.fonts.FontGroup;
+import org.apache.poi.common.usermodel.fonts.FontInfo;
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
import org.apache.poi.util.Internal;
* Some text.
*/
public interface TextRun {
+ /**
+ * Type of text capitals
+ */
enum TextCap {
NONE,
SMALL,
ALL
}
+ /**
+ * Type of placeholder fields
+ */
enum FieldType {
SLIDE_NUMBER, DATE_TIME
}
void setFontSize(Double fontSize);
/**
+ * Get the font family - convenience method for {@link #getFontInfo(FontGroup)}
+ *
* @return font family or null if not set
*/
String getFontFamily();
/**
- * Specifies the typeface, or name of the font that is to be used for this text run.
+ * Get the font family - convenience method for {@link #getFontInfo(FontGroup)}
+ *
+ * @param fontGroup the font group, i.e. the range of glpyhs to be covered.
+ * if {@code null}, the font group matching the first character will be returned
+ *
+ * @return font family or null if not set
+ */
+ String getFontFamily(FontGroup fontGroup);
+
+ /**
+ * Specifies the typeface, or name of the font that is to be used for this text run -
+ * convenience method for calling {@link #setFontInfo(FontInfo, FontGroup)} with just a font name
*
* @param typeface the font to apply to this text run.
- * The value of <code>null</code> unsets the Typeface attrubute from the underlying xml.
+ * The value of {@code null} removes the run specific font setting, so the default setting is activated again.
*/
void setFontFamily(String typeface);
+ /**
+ * Specifies the typeface, or name of the font that is to be used for this text run -
+ * convenience method for calling {@link #setFontInfo(FontInfo, FontGroup)} with just a font name
+ *
+ * @param typeface the font to apply to this text run.
+ * The value of {@code null} removes the run specific font setting, so the default setting is activated again.
+ * @param fontGroup the font group, i.e. the range of glpyhs to be covered.
+ * if {@code null}, the font group matching the first character will be returned
+ */
+ void setFontFamily(String typeface, FontGroup fontGroup);
+
+ /**
+ * Get the font info for the given font group
+ *
+ * @param fontGroup the font group, i.e. the range of glpyhs to be covered.
+ * if {@code null}, the font group matching the first character will be returned
+ * @return font info or {@code null} if not set
+ *
+ * @since POI 3.17-beta2
+ */
+ FontInfo getFontInfo(FontGroup fontGroup);
+
+ /**
+ * Specifies the font to be used for this text run.
+ *
+ * @param fontInfo the font to apply to this text run.
+ * The value of {@code null} removes the run specific font setting, so the default setting is activated again.
+ * @param fontGroup the font group, i.e. the range of glpyhs to be covered. defaults to latin, if {@code null}.
+ *
+ * @since POI 3.17-beta2
+ */
+ void setFontInfo(FontInfo fontInfo, FontGroup fontGroup);
+
/**
* @return true, if text is bold
*/
package org.apache.poi.ss.usermodel;
+import org.apache.poi.util.Removal;
/**
* Charset represents the basic set of characters associated with a font (that it can display), and
* corresponds to the ANSI codepage (8-bit or DBCS) of that character set used by a given language.
*
- * @author Gisella Bronzetti
+ * @deprecated enum will be replaced by common version org.apache.poi.common.usermodel.FontCharset
*/
+@Removal(version="4.0")
+@Deprecated
public enum FontCharset {
ANSI(0),
import java.awt.Color;
+import org.apache.poi.common.usermodel.fonts.FontCharset;
+import org.apache.poi.common.usermodel.fonts.FontFamily;
+import org.apache.poi.common.usermodel.fonts.FontGroup;
+import org.apache.poi.common.usermodel.fonts.FontInfo;
+import org.apache.poi.common.usermodel.fonts.FontPitch;
import org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.sl.draw.DrawPaint;
import org.apache.poi.xslf.model.CharacterPropertyFetcher;
import org.apache.poi.xslf.usermodel.XSLFPropertiesDelegate.XSLFFillProperties;
import org.apache.xmlbeans.XmlObject;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTFontCollection;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTFontScheme;
import org.openxmlformats.schemas.drawingml.x2006.main.CTHyperlink;
import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;
} else if (_r instanceof CTTextLineBreak) {
return "\n";
}
-
-
+
+
String txt = ((CTRegularTextRun)_r).getT();
TextCap cap = getTextCap();
StringBuffer buf = new StringBuffer();
public void setFontColor(Color color) {
setFontColor(DrawPaint.createSolidPaint(color));
}
-
+
@Override
public void setFontColor(PaintStyle color) {
if (!(color instanceof SolidPaint)) {
}
SolidPaint sp = (SolidPaint)color;
Color c = DrawPaint.applyColorTransform(sp.getSolidColor());
-
+
CTTextCharacterProperties rPr = getRPr(true);
CTSolidColorFillProperties fill = rPr.isSetSolidFill() ? rPr.getSolidFill() : rPr.addNewSolidFill();
-
+
XSLFColor col = new XSLFColor(fill, getParentParagraph().getParentShape().getSheet().getTheme(), fill.getSchemeClr());
col.setColor(c);
}
if (props == null) {
return false;
}
-
+
XSLFShape shape = _p.getParentShape();
CTShapeStyle style = shape.getSpStyle();
CTSchemeColor phClr = null;
PackagePart pp = sheet.getPackagePart();
XSLFTheme theme = sheet.getTheme();
PaintStyle ps = XSLFShape.selectPaint(fp, phClr, pp, theme, hasPlaceholder);
-
+
if (ps != null) {
setValue(ps);
return true;
}
-
+
return false;
}
};
}
@Override
- public void setFontFamily(String typeface){
- setFontFamily(typeface, (byte)-1, (byte)-1, false);
+ public void setFontFamily(String typeface) {
+ FontGroup fg = FontGroup.getFontGroupFirst(getRawText());
+ new XSLFFontInfo(fg).setTypeface(typeface);
}
- public void setFontFamily(String typeface, byte charset, byte pictAndFamily, boolean isSymbol){
- CTTextCharacterProperties rPr = getRPr(true);
+ @Override
+ public void setFontFamily(String typeface, FontGroup fontGroup) {
+ new XSLFFontInfo(fontGroup).setTypeface(typeface);
+ }
- if(typeface == null){
- if(rPr.isSetLatin()) {
- rPr.unsetLatin();
- }
- if(rPr.isSetCs()) {
- rPr.unsetCs();
- }
- if(rPr.isSetSym()) {
- rPr.unsetSym();
- }
- } else {
- if(isSymbol){
- CTTextFont font = rPr.isSetSym() ? rPr.getSym() : rPr.addNewSym();
- font.setTypeface(typeface);
- } else {
- CTTextFont latin = rPr.isSetLatin() ? rPr.getLatin() : rPr.addNewLatin();
- latin.setTypeface(typeface);
- if(charset != -1) {
- latin.setCharset(charset);
- }
- if(pictAndFamily != -1) {
- latin.setPitchFamily(pictAndFamily);
- }
- }
- }
+ @Override
+ public void setFontInfo(FontInfo fontInfo, FontGroup fontGroup) {
+ new XSLFFontInfo(fontGroup).copyFrom(fontInfo);
}
@Override
- public String getFontFamily(){
- final XSLFTheme theme = _p.getParentShape().getSheet().getTheme();
+ public String getFontFamily() {
+ FontGroup fg = FontGroup.getFontGroupFirst(getRawText());
+ return new XSLFFontInfo(fg).getTypeface();
+ }
- CharacterPropertyFetcher<String> visitor = new CharacterPropertyFetcher<String>(_p.getIndentLevel()){
- @Override
- public boolean fetch(CTTextCharacterProperties props){
- if (props != null) {
- CTTextFont font = props.getLatin();
- if (font != null) {
- String typeface = font.getTypeface();
- if("+mj-lt".equals(typeface)) {
- typeface = theme.getMajorFont();
- } else if ("+mn-lt".equals(typeface)){
- typeface = theme.getMinorFont();
- }
- setValue(typeface);
- return true;
- }
- }
- return false;
- }
- };
- fetchCharacterProperty(visitor);
+ @Override
+ public String getFontFamily(FontGroup fontGroup) {
+ return new XSLFFontInfo(fontGroup).getTypeface();
+ }
- return visitor.getValue();
+ @Override
+ public FontInfo getFontInfo(FontGroup fontGroup) {
+ XSLFFontInfo fontInfo = new XSLFFontInfo(fontGroup);
+ return (fontInfo.getTypeface() != null) ? fontInfo : null;
}
@Override
public byte getPitchAndFamily(){
- // final XSLFTheme theme = _p.getParentShape().getSheet().getTheme();
-
- CharacterPropertyFetcher<Byte> visitor = new CharacterPropertyFetcher<Byte>(_p.getIndentLevel()){
- @Override
- public boolean fetch(CTTextCharacterProperties props){
- if (props != null) {
- CTTextFont font = props.getLatin();
- if (font != null) {
- setValue(font.getPitchFamily());
- return true;
- }
- }
- return false;
- }
- };
- fetchCharacterProperty(visitor);
-
- return visitor.getValue() == null ? 0 : visitor.getValue();
+ FontGroup fg = FontGroup.getFontGroupFirst(getRawText());
+ XSLFFontInfo fontInfo = new XSLFFontInfo(fg);
+ FontPitch pitch = fontInfo.getPitch();
+ if (pitch == null) {
+ pitch = FontPitch.VARIABLE;
+ }
+ FontFamily family = fontInfo.getFamily();
+ if (family == null) {
+ family = FontFamily.FF_SWISS;
+ }
+ return FontPitch.getNativeId(pitch, family);
}
@Override
@Override
public XSLFHyperlink getHyperlink(){
CTTextCharacterProperties rPr = getRPr(false);
- if (rPr == null) {
+ if (rPr == null) {
return null;
}
CTHyperlink hl = rPr.getHlinkClick();
if (rPr != null && fetcher.fetch(rPr)) {
return true;
}
-
+
if (shape.fetchShapeProperty(fetcher)) {
return true;
}
-
+
CTPlaceholder ph = shape.getCTPlaceholder();
if (ph == null){
// if it is a plain text box then take defaults from presentation.xml
setStrikethrough(strike);
}
}
-
-
+
+
@Override
public FieldType getFieldType() {
if (_r instanceof CTTextField) {
}
return null;
}
+
+
+ private class XSLFFontInfo implements FontInfo {
+ private final FontGroup fontGroup;
+
+ private XSLFFontInfo(FontGroup fontGroup) {
+ this.fontGroup = (fontGroup != null) ? fontGroup : FontGroup.getFontGroupFirst(getRawText());
+ }
+
+ public void copyFrom(FontInfo fontInfo) {
+ CTTextFont tf = getXmlObject(true);
+ setTypeface(fontInfo.getTypeface());
+ setCharset(fontInfo.getCharset());
+ FontPitch pitch = fontInfo.getPitch();
+ FontFamily family = fontInfo.getFamily();
+ if (pitch == null && family == null) {
+ if (tf.isSetPitchFamily()) {
+ tf.unsetPitchFamily();
+ }
+ } else {
+ setPitch(pitch);
+ setFamily(family);
+ }
+ }
+
+ @Override
+ public Integer getIndex() {
+ return null;
+ }
+
+ @Override
+ public void setIndex(int index) {
+ throw new UnsupportedOperationException("setIndex not supported by XSLFFontInfo.");
+ }
+
+ @Override
+ public String getTypeface() {
+ CTTextFont tf = getXmlObject(false);
+ return (tf != null && tf.isSetTypeface()) ? tf.getTypeface() : null;
+ }
+
+ @Override
+ public void setTypeface(String typeface) {
+ if (typeface != null) {
+ getXmlObject(true).setTypeface(typeface);
+ return;
+ }
+
+ CTTextCharacterProperties props = getRPr(false);
+ if (props == null) {
+ return;
+ }
+ FontGroup fg = FontGroup.getFontGroupFirst(getRawText());
+ switch (fg) {
+ default:
+ case LATIN:
+ if (props.isSetLatin()) {
+ props.unsetLatin();
+ }
+ break;
+ case EAST_ASIAN:
+ if (props.isSetEa()) {
+ props.unsetEa();
+ }
+ break;
+ case COMPLEX_SCRIPT:
+ if (props.isSetCs()) {
+ props.unsetCs();
+ }
+ break;
+ case SYMBOL:
+ if (props.isSetSym()) {
+ props.unsetSym();
+ }
+ break;
+ }
+ }
+
+ @Override
+ public FontCharset getCharset() {
+ CTTextFont tf = getXmlObject(false);
+ return (tf != null && tf.isSetCharset()) ? FontCharset.valueOf(tf.getCharset()&0xFF) : null;
+ }
+
+ @Override
+ public void setCharset(FontCharset charset) {
+ CTTextFont tf = getXmlObject(true);
+ if (charset != null) {
+ tf.setCharset((byte)charset.getNativeId());
+ } else {
+ if (tf.isSetCharset()) {
+ tf.unsetCharset();
+ }
+ }
+ }
+
+ @Override
+ public FontFamily getFamily() {
+ CTTextFont tf = getXmlObject(false);
+ return (tf != null && tf.isSetPitchFamily()) ? FontFamily.valueOfPitchFamily(tf.getPitchFamily()) : null;
+ }
+
+ @Override
+ public void setFamily(FontFamily family) {
+ CTTextFont tf = getXmlObject(true);
+ if (family == null && !tf.isSetPitchFamily()) {
+ return;
+ }
+ FontPitch pitch = (tf.isSetPitchFamily())
+ ? FontPitch.valueOfPitchFamily(tf.getPitchFamily())
+ : FontPitch.VARIABLE;
+ byte pitchFamily = FontPitch.getNativeId(pitch, family != null ? family : FontFamily.FF_SWISS);
+ tf.setPitchFamily(pitchFamily);
+ }
+
+ @Override
+ public FontPitch getPitch() {
+ CTTextFont tf = getXmlObject(false);
+ return (tf != null && tf.isSetPitchFamily()) ? FontPitch.valueOfPitchFamily(tf.getPitchFamily()) : null;
+ }
+
+ @Override
+ public void setPitch(FontPitch pitch) {
+ CTTextFont tf = getXmlObject(true);
+ if (pitch == null && !tf.isSetPitchFamily()) {
+ return;
+ }
+ FontFamily family = (tf.isSetPitchFamily())
+ ? FontFamily.valueOfPitchFamily(tf.getPitchFamily())
+ : FontFamily.FF_SWISS;
+ byte pitchFamily = FontPitch.getNativeId(pitch != null ? pitch : FontPitch.VARIABLE, family);
+ tf.setPitchFamily(pitchFamily);
+ }
+
+ private CTTextFont getXmlObject(boolean create) {
+ if (create) {
+ return getCTTextFont(getRPr(true), true);
+ }
+
+ CharacterPropertyFetcher<CTTextFont> visitor = new CharacterPropertyFetcher<CTTextFont>(_p.getIndentLevel()){
+ @Override
+ public boolean fetch(CTTextCharacterProperties props){
+ CTTextFont font = getCTTextFont(props, false);
+ if (font == null) {
+ return false;
+ }
+ setValue(font);
+ return true;
+ }
+ };
+ fetchCharacterProperty(visitor);
+
+ return visitor.getValue();
+ }
+
+ private CTTextFont getCTTextFont(CTTextCharacterProperties props, boolean create) {
+ if (props == null) {
+ return null;
+ }
+
+ CTTextFont font;
+ switch (fontGroup) {
+ default:
+ case LATIN:
+ font = props.getLatin();
+ if (font == null && create) {
+ font = props.addNewLatin();
+ }
+ break;
+ case EAST_ASIAN:
+ font = props.getEa();
+ if (font == null && create) {
+ font = props.addNewEa();
+ }
+ break;
+ case COMPLEX_SCRIPT:
+ font = props.getCs();
+ if (font == null && create) {
+ font = props.addNewCs();
+ }
+ break;
+ case SYMBOL:
+ font = props.getSym();
+ if (font == null && create) {
+ font = props.addNewSym();
+ }
+ break;
+ }
+
+ if (font == null) {
+ return null;
+ }
+
+ String typeface = font.isSetTypeface() ? font.getTypeface() : "";
+ if (typeface.startsWith("+mj-") || typeface.startsWith("+mn-")) {
+ // "+mj-lt".equals(typeface) || "+mn-lt".equals(typeface)
+ final XSLFTheme theme = _p.getParentShape().getSheet().getTheme();
+ CTFontScheme fontTheme = theme.getXmlObject().getThemeElements().getFontScheme();
+ CTFontCollection coll = typeface.startsWith("+mj-")
+ ? fontTheme.getMajorFont() : fontTheme.getMinorFont();
+ // TODO: handle LCID codes
+ // see https://blogs.msdn.microsoft.com/officeinteroperability/2013/04/22/office-open-xml-themes-schemes-and-fonts/
+ String fgStr = typeface.substring(4);
+ if ("ea".equals(fgStr)) {
+ font = coll.getEa();
+ } else if ("cs".equals(fgStr)) {
+ font = coll.getCs();
+ } else {
+ font = coll.getLatin();
+ }
+ // SYMBOL is missing
+
+ if (font != null || !font.isSetTypeface() || "".equals(font.getTypeface())) {
+ font = coll.getLatin();
+ }
+ }
+
+ return font;
+ }
+ }
}
import java.util.Map;
import org.apache.poi.POIDataSamples;
+import org.apache.poi.common.usermodel.fonts.FontGroup;
import org.apache.poi.hslf.usermodel.HSLFSlideShow;
import org.apache.poi.sl.draw.DrawFactory;
import org.apache.poi.sl.draw.Drawable;
import org.apache.poi.sl.usermodel.TextParagraph;
import org.apache.poi.sl.usermodel.TextRun;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
-import org.apache.poi.xslf.usermodel.XSLFTextRun;
import org.junit.BeforeClass;
import org.junit.Test;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;
/**
"\u9451\u3092\u4E00\u7DD2\u306B\u898B\u3066\u305F\u306E\u601D\u3044\u51FA\u3059\u301C\u3068\u3044";
private static final String INIT_FONTS[] = { "mona.ttf" };
-
+
// currently linux and mac return quite different values
private static final int[] expected_sizes = { 311, 312, 313,
362, // Windows 10, 13.3" 1080p high-dpi
398, 399,
406 // Ubuntu Trusty, 15", 1680x1050
};
-
+
@BeforeClass
public static void initGE() throws FontFormatException, IOException {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
ge.registerFont(font);
}
}
-
+
@Test
public void resizeToFitTextHSLF() throws IOException {
assumeFalse(xslfOnly());
SlideShow<?,?> ppt = new HSLFSlideShow();
- TextBox<?,?> tb = resizeToFitText(ppt);
- Rectangle2D anc = tb.getAnchor();
- // ignore font metrics differences on windows / linux (... hopefully ...)
- boolean found = Arrays.binarySearch(expected_sizes, (int)anc.getHeight()) > -1;
- assertTrue("Did not find height " + anc.getHeight() + " in expected sizes: " + Arrays.toString(expected_sizes),
- found);
-// setFont(tb, "Mona");
-// FileOutputStream fos = new FileOutputStream("bla-hslf.ppt");
-// ppt.write(fos);
-// fos.close();
+ resizeToFitText(ppt);
ppt.close();
}
@Test
public void resizeToFitTextXSLF() throws IOException {
SlideShow<?,?> ppt = new XMLSlideShow();
- TextBox<?,?> tb = resizeToFitText(ppt);
- Rectangle2D anc = tb.getAnchor();
- // ignore font metrics differences on windows / linux (... hopefully ...)
- boolean found = Arrays.binarySearch(expected_sizes, (int)anc.getHeight()) > -1;
- assertTrue("Did not find height " + anc.getHeight() + " in expected sizes: " + Arrays.toString(expected_sizes),
- found);
-// setFont(tb, "Mona");
-// FileOutputStream fos = new FileOutputStream("bla-xslf.ppt");
-// ppt.write(fos);
-// fos.close();
+ resizeToFitText(ppt);
ppt.close();
}
- private TextBox<?,?> resizeToFitText(SlideShow<?,?> slideshow) throws IOException {
+ private void resizeToFitText(SlideShow<?,?> slideshow) throws IOException {
Slide<?,?> sld = slideshow.createSlide();
TextBox<?,?> tb = sld.createTextBox();
tb.setAnchor(new Rectangle(50, 50, 200, 50));
tb.setStrokeStyle(Color.black, LineDash.SOLID, 3);
tb.setText(JPTEXT);
-
- setFont(tb, "NoSuchFont");
+
+ setFont(tb, "NoSuchFont", FontGroup.LATIN);
Dimension pgsize = slideshow.getPageSize();
int width = (int)pgsize.getWidth();
int height = (int)pgsize.getHeight();
-
+
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = img.createGraphics();
Map<String,String> fallbackMap = new HashMap<String,String>();
fallbackMap.put("NoSuchFont", "Mona");
+ // in XSLF the fonts default to the theme fonts (Calibri), if the font group is not overridden
+ // see XSLFTextRun.XSLFTextInfo.getCTTextFont
+ fallbackMap.put("Calibri", "Mona");
graphics.setRenderingHint(Drawable.FONT_FALLBACK, fallbackMap);
graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
-
+
DrawFactory.getInstance(graphics).fixFonts(graphics);
-
+
tb.resizeToFitText(graphics);
graphics.dispose();
-
- return tb;
+
+ Rectangle2D anc = tb.getAnchor();
+ // ignore font metrics differences on windows / linux (... hopefully ...)
+ int tbHeight = (int)anc.getHeight();
+ boolean found = Arrays.binarySearch(expected_sizes, tbHeight) > -1;
+ assertTrue(tbHeight+" wasn't within the expected sizes: "+Arrays.toString(expected_sizes), found);
}
-
- private void setFont(TextBox<?,?> tb, String fontFamily) {
+
+ private void setFont(TextBox<?,?> tb, String fontFamily, FontGroup fontGroup) {
// TODO: set east asian font family - MS Office uses "MS Mincho" or "MS Gothic" as a fallback
// see https://stackoverflow.com/questions/26063828 for good explanation about the font metrics
// differences on different environments
for (TextParagraph<?,?,?> p : tb.getTextParagraphs()) {
for (TextRun r : p.getTextRuns()) {
- r.setFontFamily(fontFamily);
- if (r instanceof XSLFTextRun) {
- // TODO: provide API for HSLF
- XSLFTextRun xr = (XSLFTextRun)r;
- CTRegularTextRun tr = (CTRegularTextRun)xr.getXmlObject();
- tr.getRPr().addNewEa().setTypeface(fontFamily);
-
- }
+ r.setFontFamily(fontFamily, fontGroup);
}
}
}
+++ /dev/null
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.hslf.model;
-
-import org.apache.poi.hslf.record.FontEntityAtom;
-
-/**
- * Represents a Font used in a presenation.
- * <p>
- * In PowerPoint Font is a shared resource and can be shared among text object in the presentation.
- * </p>
- * Some commonly used fonts are predefined in static constants.
- *
- * @author Yegor Kozlov
- */
-public final class PPFont {
- /**
- * ANSI character set
- */
- public final static byte ANSI_CHARSET = 0;
-
- /**
- * Default character set.
- */
- public final static byte DEFAULT_CHARSET = 1;
-
- /**
- * Symbol character set
- */
- public final static byte SYMBOL_CHARSET = 2;
-
-
- /**
- * Constants for the pitch and family of the font.
- * The two low-order bits specify the pitch of the font and can be one of the following values
- */
- public final static byte DEFAULT_PITCH = 0;
- public final static byte FIXED_PITCH = 1;
- public final static byte VARIABLE_PITCH = 2;
-
- /**
- * Don't care or don't know.
- */
- public final static byte FF_DONTCARE = 0;
- /**
- * Fonts with variable stroke width (proportional) and with serifs. Times New Roman is an example.
- */
- public final static byte FF_ROMAN = 16;
- /**
- * Fonts with variable stroke width (proportional) and without serifs. Arial is an example.
- */
- public final static byte FF_SWISS = 32;
- /**
- * Fonts designed to look like handwriting. Script and Cursive are examples.
- */
- public final static byte FF_SCRIPT = 64;
- /**
- * Fonts with constant stroke width (monospace), with or without serifs.
- * Monospace fonts are usually modern. CourierNew is an example
- */
- public final static byte FF_MODERN = 48;
- /**
- * Novelty fonts. Old English is an example
- */
- public final static byte FF_DECORATIVE = 80;
-
-
- private int charset;
- private int type;
- private int flags;
- private int pitch;
- private String name;
-
- /**
- * Creates a new instance of PPFont
- */
- public PPFont(){
-
- }
-
- /**
- * Creates a new instance of PPFont and initialize it from the supplied font atom
- */
- public PPFont(FontEntityAtom fontAtom){
- name = fontAtom.getFontName();
- charset = fontAtom.getCharSet();
- type = fontAtom.getFontType();
- flags = fontAtom.getFontFlags();
- pitch = fontAtom.getPitchAndFamily();
- }
-
- /**
- * set the name for the font (i.e. Arial)
- *
- * @param val String representing the name of the font to use
- */
- public void setFontName(String val){
- name = val;
- }
-
- /**
- * get the name for the font (i.e. Arial)
- *
- * @return String representing the name of the font to use
- */
- public String getFontName(){
- return name;
- }
-
- /**
- * set the character set
- *
- * @param val - characterset
- */
- public void setCharSet(int val){
- charset = val;
- }
-
- /**
- * get the character set
- *
- * @return charset - characterset
- */
- public int getCharSet(){
- return charset;
- }
-
- /**
- * set the font flags
- * Bit 1: If set, font is subsetted
- *
- * @param val - the font flags
- */
- public void setFontFlags(int val){
- flags = val;
- }
-
- /**
- * get the character set
- * Bit 1: If set, font is subsetted
- *
- * @return the font flags
- */
- public int getFontFlags(){
- return flags;
- }
-
- /**
- * set the font type
- * <p>
- * Bit 1: Raster Font
- * Bit 2: Device Font
- * Bit 3: TrueType Font
- * </p>
- *
- * @param val - the font type
- */
- public void setFontType(int val){
- type = val;
- }
-
- /**
- * get the font type
- * <p>
- * Bit 1: Raster Font
- * Bit 2: Device Font
- * Bit 3: TrueType Font
- * </p>
- *
- * @return the font type
- */
- public int getFontType(){
- return type;
- }
-
- /**
- * set lfPitchAndFamily
- *
- *
- * @param val - Corresponds to the lfPitchAndFamily field of the Win32 API LOGFONT structure
- */
- public void setPitchAndFamily(int val){
- pitch = val;
- }
-
- /**
- * get lfPitchAndFamily
- *
- * @return corresponds to the lfPitchAndFamily field of the Win32 API LOGFONT structure
- */
- public int getPitchAndFamily(){
- return pitch;
- }
-
- public static final PPFont ARIAL;
- public static final PPFont TIMES_NEW_ROMAN ;
- public static final PPFont COURIER_NEW;
- public static final PPFont WINGDINGS;
- static {
- ARIAL = new PPFont();
- ARIAL.setFontName("Arial");
- ARIAL.setCharSet(ANSI_CHARSET);
- ARIAL.setFontType(4);
- ARIAL.setFontFlags(0);
- ARIAL.setPitchAndFamily(VARIABLE_PITCH | FF_SWISS);
-
- TIMES_NEW_ROMAN = new PPFont();
- TIMES_NEW_ROMAN.setFontName("Times New Roman");
- TIMES_NEW_ROMAN.setCharSet(ANSI_CHARSET);
- TIMES_NEW_ROMAN.setFontType(4);
- TIMES_NEW_ROMAN.setFontFlags(0);
- TIMES_NEW_ROMAN.setPitchAndFamily(VARIABLE_PITCH | FF_ROMAN);
-
- COURIER_NEW = new PPFont();
- COURIER_NEW.setFontName("Courier New");
- COURIER_NEW.setCharSet(ANSI_CHARSET);
- COURIER_NEW.setFontType(4);
- COURIER_NEW.setFontFlags(0);
- COURIER_NEW.setPitchAndFamily(FIXED_PITCH | FF_MODERN);
-
- WINGDINGS = new PPFont();
- WINGDINGS.setFontName("Wingdings");
- WINGDINGS.setCharSet(SYMBOL_CHARSET);
- WINGDINGS.setFontType(4);
- WINGDINGS.setFontFlags(0);
- WINGDINGS.setPitchAndFamily(VARIABLE_PITCH | FF_DONTCARE);
- }
-}
"fehint", // 0x0020 A bit that specifies whether characters originated from double-byte input.
"unused2", // 0x0040 Undefined and MUST be ignored.
"kumi", // 0x0080 A bit that specifies whether Kumimoji are used for vertical text.
- "strikethrough", // 0x0100 Undefined and MUST be ignored.
+ "strikethrough", // 0x0100 aka "unused3" - sometimes contains the strikethrough flag
"emboss", // 0x0200 A bit that specifies whether the characters are embossed.
"pp9rt_1", // 0x0400 An unsigned integer that specifies the run grouping of additional text properties in StyleTextProp9Atom record.
"pp9rt_2", // 0x0800
package org.apache.poi.hslf.record;
-import org.apache.poi.hslf.model.PPFont;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.poi.common.usermodel.fonts.FontInfo;
+import org.apache.poi.hslf.usermodel.HSLFFontInfo;
+import org.apache.poi.hslf.usermodel.HSLFFontInfoPredefined;
import org.apache.poi.util.POILogger;
-import java.io.*;
-import java.util.*;
-
/**
- * <code>FontCollection</code> ia a container that holds information
+ * {@code FontCollection} ia a container that holds information
* about all the fonts in the presentation.
- *
- * @author Yegor Kozlov
*/
public final class FontCollection extends RecordContainer {
- private List<String> fonts;
+ private final Map<String,HSLFFontInfo> fonts = new LinkedHashMap<String,HSLFFontInfo>();
private byte[] _header;
protected FontCollection(byte[] source, int start, int len) {
- // Grab the header
_header = new byte[8];
System.arraycopy(source,start,_header,0,8);
_children = Record.findChildRecords(source,start+8,len-8);
- // Save font names into <code>List</code>
- fonts = new ArrayList<String>();
- for (int i = 0; i < _children.length; i++){
- if(_children[i] instanceof FontEntityAtom) {
- FontEntityAtom atom = (FontEntityAtom)_children[i];
- fonts.add(atom.getFontName());
+ for (Record r : _children){
+ if(r instanceof FontEntityAtom) {
+ HSLFFontInfo fi = new HSLFFontInfo((FontEntityAtom)r);
+ fonts.put(fi.getTypeface(), fi);
} else {
- logger.log(POILogger.WARN, "Warning: FontCollection child wasn't a FontEntityAtom, was " + _children[i]);
+ logger.log(POILogger.WARN, "Warning: FontCollection child wasn't a FontEntityAtom, was " + r.getClass().getSimpleName());
}
}
}
/**
* Return the type, which is 2005
*/
- public long getRecordType() {
+ @Override
+ public long getRecordType() {
return RecordTypes.FontCollection.typeID;
}
* Write the contents of the record back, so it can be written
* to disk
*/
- public void writeOut(OutputStream out) throws IOException {
+ @Override
+ public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],getRecordType(),_children,out);
}
/**
- * Add font with the specified name to the font collection.
- * If the font is already present return its index.
- * @param name of the font
- * @return zero based index of the font in the collection
+ * Add font with the given FontInfo configuration to the font collection.
+ * The returned FontInfo contains the HSLF specific details and the collection
+ * uniquely contains fonts based on their typeface, i.e. calling the method with FontInfo
+ * objects having the same name results in the same HSLFFontInfo reference.
+ *
+ * @param fontInfo the FontInfo configuration, can be a instance of {@link HSLFFontInfo},
+ * {@link HSLFFontInfoPredefined} or a custom implementation
+ * @return the register HSLFFontInfo object
*/
- public int addFont(String name) {
- int idx = getFontIndex(name);
- if (idx != -1) return idx;
-
- return addFont(name, 0, 0, 4, PPFont.FF_SWISS | PPFont.VARIABLE_PITCH);
- }
+ public HSLFFontInfo addFont(FontInfo fontInfo) {
+ HSLFFontInfo fi = getFontInfo(fontInfo.getTypeface());
+ if (fi != null) {
+ return fi;
+ }
- public int addFont(String name, int charset, int flags, int type, int pitch) {
- FontEntityAtom fnt = new FontEntityAtom();
- fnt.setFontIndex(fonts.size() << 4);
- fnt.setFontName(name);
- fnt.setCharSet(charset);
- fnt.setFontFlags(flags);
- fnt.setFontType(type);
- fnt.setPitchAndFamily(pitch);
- fonts.add(name);
+ fi = new HSLFFontInfo(fontInfo);
+ fi.setIndex(fonts.size());
+ fonts.put(fi.getTypeface(), fi);
+
+ FontEntityAtom fnt = fi.createRecord();
// Append new child to the end
- appendChildRecord(fnt);
+ appendChildRecord(fnt);
- return fonts.size()-1; //the added font is the last in the list
+ // the added font is the last in the list
+ return fi;
+ }
+
+
+ /**
+ * Lookup a FontInfo object by its typeface
+ *
+ * @param typeface the full font name
+ *
+ * @return the HSLFFontInfo for the given name or {@code null} if not found
+ */
+ public HSLFFontInfo getFontInfo(String typeface) {
+ return fonts.get(typeface);
}
/**
- * @return zero based index of the font in the collection or -1 if not found
+ * Lookup a FontInfo object by its internal font index
+ *
+ * @param index the internal font index
+ *
+ * @return the HSLFFontInfo for the given index or {@code null} if not found
*/
- public int getFontIndex(String name) {
- for (int i = 0; i < fonts.size(); i++) {
- if(fonts.get(i).equals(name)){
- //if the font is already present return its index
- return i;
+ public HSLFFontInfo getFontInfo(int index) {
+ for (HSLFFontInfo fi : fonts.values()) {
+ if (fi.getIndex() == index) {
+ return fi;
}
}
- return -1;
+ return null;
}
-
+
+ /**
+ * @return the number of registered fonts
+ */
public int getNumberOfFonts() {
return fonts.size();
}
-
- /**
- * Get the name of the font at the given ID, or null if there is
- * no font at that ID.
- * @param id
- */
- public String getFontWithId(int id) {
- if(id >= fonts.size()) {
- // No font with that id
- return null;
- }
- return fonts.get(id);
- }
}
import java.io.IOException;
import java.io.OutputStream;
+import java.util.Arrays;
import org.apache.poi.hslf.exceptions.HSLFException;
import org.apache.poi.util.LittleEndian;
* Will be converted to null-terminated if not already
* @param name of the font
*/
- public void setFontName(String name){
- // Add a null termination if required
- if(! name.endsWith("\u0000")) {
- name += '\u0000';
- }
-
- // Ensure it's not now too long
- if(name.length() > 32) {
- throw new HSLFException("The length of the font name, including null termination, must not exceed 32 characters");
- }
-
- // Everything's happy, so save the name
+ public void setFontName(String name) {
+ // Ensure it's not now too long
+ int nameLen = name.length() + (name.endsWith("\u0000") ? 0 : 1);
+ if (nameLen > 32) {
+ throw new HSLFException("The length of the font name, including null termination, must not exceed 32 characters");
+ }
+
+ // Everything's happy, so save the name
byte[] bytes = StringUtil.getToUnicodeLE(name);
System.arraycopy(bytes, 0, _recdata, 0, bytes.length);
+ // null the remaining bytes
+ Arrays.fill(_recdata, 64-bytes.length, 64, (byte)0);
}
public void setFontIndex(int idx){
package org.apache.poi.hslf.record;
-import org.apache.poi.util.ArrayUtil;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.hslf.util.MutableByteArrayOutputStream;
-
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
-import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
+import org.apache.poi.hslf.util.MutableByteArrayOutputStream;
+import org.apache.poi.util.ArrayUtil;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.Removal;
+
/**
* Abstract class which all container records will extend. Providers
* helpful methods for writing child records out to disk
- *
- * @author Nick Burch
*/
public abstract class RecordContainer extends Record
/**
* Return any children
*/
- public Record[] getChildRecords() { return _children; }
+ @Override
+ public Record[] getChildRecords() { return _children; }
/**
* We're not an atom
*/
- public boolean isAnAtom() { return false; }
+ @Override
+ public boolean isAnAtom() { return false; }
/* ===============================================================
/**
* Adds a child record, at the very end.
* @param newChild The child record to add
+ * @return the position of the added child
*/
- private void appendChild(Record newChild) {
+ private int appendChild(Record newChild) {
// Copy over, and pop the child in at the end
Record[] nc = new Record[(_children.length + 1)];
System.arraycopy(_children, 0, nc, 0, _children.length);
// Switch the arrays
nc[_children.length] = newChild;
_children = nc;
+ return _children.length;
}
/**
Record rm = null;
ArrayList<Record> lst = new ArrayList<Record>();
for(Record r : _children) {
- if(r != ch) lst.add(r);
- else rm = r;
+ if(r != ch) {
+ lst.add(r);
+ } else {
+ rm = r;
+ }
}
_children = lst.toArray(new Record[lst.size()]);
return rm;
/**
* Add a new child record onto a record's list of children.
+ *
+ * @param newChild the child record to be added
+ * @return the position of the added child within the list, i.e. the last index
*/
- public void appendChildRecord(Record newChild) {
- appendChild(newChild);
+ public int appendChildRecord(Record newChild) {
+ return appendChild(newChild);
}
/**
* Adds the given Child Record after the supplied record
* @param newChild
* @param after
+ * @return the position of the added child within the list
*/
- public void addChildAfter(Record newChild, Record after) {
+ public int addChildAfter(Record newChild, Record after) {
// Decide where we're going to put it
int loc = findChildLocation(after);
if(loc == -1) {
// Add one place after the supplied record
addChildAt(newChild, loc+1);
+ return loc+1;
}
/**
* Adds the given Child Record before the supplied record
* @param newChild
* @param before
+ * @return the position of the added child within the list
*/
- public void addChildBefore(Record newChild, Record before) {
+ public int addChildBefore(Record newChild, Record before) {
// Decide where we're going to put it
int loc = findChildLocation(before);
if(loc == -1) {
// Add at the place of the supplied record
addChildAt(newChild, loc);
+ return loc;
}
/**
* Moves the given Child Record to before the supplied record
- */
+ *
+ * @deprecated method is not used within POI and will be removed
+ */
+ @Removal(version="3.19")
+ @Deprecated
public void moveChildBefore(Record child, Record before) {
moveChildrenBefore(child, 1, before);
}
/**
* Moves the given Child Records to before the supplied record
- */
+ *
+ * @deprecated method is not used within POI and will be removed
+ */
+ @Removal(version="3.19")
+ @Deprecated
public void moveChildrenBefore(Record firstChild, int number, Record before) {
if(number < 1) { return; }
/**
* Moves the given Child Records to after the supplied record
+ *
+ * @param firstChild the first child to be moved
+ * @param number the number of records to move
+ * @param after the record after that the children are moved
+ *
+ * @deprecated method is not used within POI and will be removed
*/
+ @Removal(version="3.19")
+ @Deprecated
public void moveChildrenAfter(Record firstChild, int number, Record after) {
if(number < 1) { return; }
// Decide where we're going to put them
--- /dev/null
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hslf.usermodel;
+
+import org.apache.poi.common.usermodel.fonts.FontCharset;
+import org.apache.poi.common.usermodel.fonts.FontFamily;
+import org.apache.poi.common.usermodel.fonts.FontInfo;
+import org.apache.poi.common.usermodel.fonts.FontPitch;
+import org.apache.poi.hslf.record.FontEntityAtom;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
+
+/**
+ * Represents a Font used in a presentation.<p>
+ *
+ * In PowerPoint Font is a shared resource and can be shared among text object in the presentation.
+ *
+ * @since POI 3.17-beta2
+ */
+public class HSLFFontInfo implements FontInfo {
+
+ public enum FontRenderType {
+ raster, device, truetype;
+ }
+
+ /** A bit that specifies whether a subset of this font is embedded. */
+ private static final BitField FLAGS_EMBED_SUBSETTED = BitFieldFactory.getInstance(0x01);
+ /** Bits that specifies whether the font is a raster,device or truetype font. */
+ private static final BitField FLAGS_RENDER_FONTTYPE = BitFieldFactory.getInstance(0x07);
+ /** A bit that specifies whether font substitution logic is not applied for this font. */
+ private static final BitField FLAGS_NO_FONT_SUBSTITUTION = BitFieldFactory.getInstance(0x08);
+
+ private int index = -1;
+ private String typeface = "undefined";
+ private FontCharset charset = FontCharset.ANSI;
+ private FontRenderType renderType = FontRenderType.truetype;
+ private FontFamily family = FontFamily.FF_SWISS;
+ private FontPitch pitch = FontPitch.VARIABLE;
+ private boolean isSubsetted = false;
+ private boolean isSubstitutable = true;
+
+ /**
+ * Creates a new instance of HSLFFontInfo with more or sensible defaults.<p>
+ *
+ * If you don't use default fonts (see {@link HSLFFontInfoPredefined}) then the results
+ * of the font substitution will be better, if you also specify the other properties.
+ *
+ * @param typeface the font name
+ */
+ public HSLFFontInfo(String typeface){
+ setTypeface(typeface);
+ }
+
+ /**
+ * Creates a new instance of HSLFFontInfo and initialize it from the supplied font atom
+ */
+ public HSLFFontInfo(FontEntityAtom fontAtom){
+ setIndex(fontAtom.getFontIndex());
+ setTypeface(fontAtom.getFontName());
+ setCharset(FontCharset.valueOf(fontAtom.getCharSet()));
+ // assumption: the render type is exclusive
+ switch (FLAGS_RENDER_FONTTYPE.getValue(fontAtom.getFontType())) {
+ case 1:
+ setRenderType(FontRenderType.raster);
+ break;
+ case 2:
+ setRenderType(FontRenderType.device);
+ break;
+ default:
+ case 4:
+ setRenderType(FontRenderType.truetype);
+ break;
+ }
+
+ byte pitchAndFamily = (byte)fontAtom.getPitchAndFamily();
+ setPitch(FontPitch.valueOfPitchFamily(pitchAndFamily));
+ setFamily(FontFamily.valueOfPitchFamily(pitchAndFamily));
+ setEmbedSubsetted(FLAGS_EMBED_SUBSETTED.isSet(fontAtom.getFontFlags()));
+ setFontSubstitutable(!FLAGS_NO_FONT_SUBSTITUTION.isSet(fontAtom.getFontType()));
+ }
+
+ public HSLFFontInfo(FontInfo fontInfo) {
+ // don't copy font index on copy constructor - it depends on the FontCollection this record is in
+ setTypeface(fontInfo.getTypeface());
+ setCharset(fontInfo.getCharset());
+ setFamily(fontInfo.getFamily());
+ setPitch(fontInfo.getPitch());
+ if (fontInfo instanceof HSLFFontInfo) {
+ HSLFFontInfo hFontInfo = (HSLFFontInfo)fontInfo;
+ setRenderType(hFontInfo.getRenderType());
+ setEmbedSubsetted(hFontInfo.isEmbedSubsetted());
+ setFontSubstitutable(hFontInfo.isFontSubstitutable());
+ }
+ }
+
+ @Override
+ public Integer getIndex() {
+ return index;
+ }
+
+ @Override
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+ @Override
+ public String getTypeface(){
+ return typeface;
+ }
+
+ @Override
+ public void setTypeface(String typeface){
+ if (typeface == null || "".equals(typeface)) {
+ throw new IllegalArgumentException("typeface can't be null nor empty");
+ }
+ this.typeface = typeface;
+ }
+
+ @Override
+ public void setCharset(FontCharset charset){
+ this.charset = (charset == null) ? FontCharset.ANSI : charset;
+ }
+
+ @Override
+ public FontCharset getCharset(){
+ return charset;
+ }
+
+ @Override
+ public FontFamily getFamily() {
+ return family;
+ }
+
+ @Override
+ public void setFamily(FontFamily family) {
+ this.family = (family == null) ? FontFamily.FF_SWISS : family;
+ }
+
+ @Override
+ public FontPitch getPitch() {
+ return pitch;
+ }
+
+ @Override
+ public void setPitch(FontPitch pitch) {
+ this.pitch = (pitch == null) ? FontPitch.VARIABLE : pitch;
+
+ }
+
+ public FontRenderType getRenderType() {
+ return renderType;
+ }
+
+ public void setRenderType(FontRenderType renderType) {
+ this.renderType = (renderType == null) ? FontRenderType.truetype : renderType;
+ }
+
+ public boolean isEmbedSubsetted() {
+ return isSubsetted;
+ }
+
+ public void setEmbedSubsetted(boolean embedSubset) {
+ this.isSubsetted = embedSubset;
+ }
+
+ public boolean isFontSubstitutable() {
+ return this.isSubstitutable;
+ }
+
+ public void setFontSubstitutable(boolean isSubstitutable) {
+ this.isSubstitutable = isSubstitutable;
+ }
+
+ public FontEntityAtom createRecord() {
+ FontEntityAtom fnt = new FontEntityAtom();
+ fnt.setFontIndex(getIndex() << 4);
+ fnt.setFontName(getTypeface());
+ fnt.setCharSet(getCharset().getNativeId());
+ fnt.setFontFlags((byte)(isEmbedSubsetted() ? 1 : 0));
+
+ int typeFlag;
+ switch (renderType) {
+ case device:
+ typeFlag = FLAGS_RENDER_FONTTYPE.setValue(0, 1);
+ break;
+ case raster:
+ typeFlag = FLAGS_RENDER_FONTTYPE.setValue(0, 2);
+ break;
+ default:
+ case truetype:
+ typeFlag = FLAGS_RENDER_FONTTYPE.setValue(0, 4);
+ break;
+ }
+ typeFlag = FLAGS_NO_FONT_SUBSTITUTION.setBoolean(typeFlag, isFontSubstitutable());
+ fnt.setFontType(typeFlag);
+
+ fnt.setPitchAndFamily(FontPitch.getNativeId(pitch, family));
+ return fnt;
+ }
+}
--- /dev/null
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hslf.usermodel;
+
+import org.apache.poi.common.usermodel.fonts.FontCharset;
+import org.apache.poi.common.usermodel.fonts.FontFamily;
+import org.apache.poi.common.usermodel.fonts.FontInfo;
+import org.apache.poi.common.usermodel.fonts.FontPitch;
+
+/**
+ * Predefined fonts
+ *
+ * @since POI 3.17-beta2
+ */
+public enum HSLFFontInfoPredefined implements FontInfo {
+ ARIAL("Arial", FontCharset.ANSI, FontPitch.VARIABLE, FontFamily.FF_SWISS),
+ TIMES_NEW_ROMAN("Times New Roman", FontCharset.ANSI, FontPitch.VARIABLE, FontFamily.FF_ROMAN),
+ COURIER_NEW("Courier New", FontCharset.ANSI, FontPitch.FIXED, FontFamily.FF_MODERN),
+ WINGDINGS("Wingdings", FontCharset.SYMBOL, FontPitch.VARIABLE, FontFamily.FF_DONTCARE);
+
+ private String typeface;
+ private FontCharset charset;
+ private FontPitch pitch;
+ private FontFamily family;
+
+ HSLFFontInfoPredefined(String typeface, FontCharset charset, FontPitch pitch, FontFamily family) {
+ this.typeface = typeface;
+ this.charset = charset;
+ this.pitch = pitch;
+ this.family = family;
+ }
+
+ @Override
+ public Integer getIndex() {
+ return -1;
+ }
+
+ @Override
+ public void setIndex(int index) {
+ throw new UnsupportedOperationException("Predefined enum can't be changed.");
+ }
+
+ @Override
+ public String getTypeface() {
+ return typeface;
+ }
+
+ @Override
+ public void setTypeface(String typeface) {
+ throw new UnsupportedOperationException("Predefined enum can't be changed.");
+ }
+
+ @Override
+ public FontCharset getCharset() {
+ return charset;
+ }
+
+ @Override
+ public void setCharset(FontCharset charset) {
+ throw new UnsupportedOperationException("Predefined enum can't be changed.");
+ }
+
+ @Override
+ public FontFamily getFamily() {
+ return family;
+ }
+
+ @Override
+ public void setFamily(FontFamily family) {
+ throw new UnsupportedOperationException("Predefined enum can't be changed.");
+ }
+
+ @Override
+ public FontPitch getPitch() {
+ return pitch;
+ }
+
+ @Override
+ public void setPitch(FontPitch pitch) {
+ throw new UnsupportedOperationException("Predefined enum can't be changed.");
+ }
+}
import java.util.List;
import java.util.Map;
+import org.apache.poi.common.usermodel.fonts.FontInfo;
import org.apache.poi.ddf.EscherBSERecord;
import org.apache.poi.ddf.EscherContainerRecord;
import org.apache.poi.ddf.EscherOptRecord;
import org.apache.poi.hslf.exceptions.HSLFException;
import org.apache.poi.hslf.model.HeadersFooters;
import org.apache.poi.hslf.model.MovieShape;
-import org.apache.poi.hslf.model.PPFont;
-import org.apache.poi.hslf.record.*;
+import org.apache.poi.hslf.record.Document;
+import org.apache.poi.hslf.record.DocumentAtom;
+import org.apache.poi.hslf.record.ExAviMovie;
+import org.apache.poi.hslf.record.ExControl;
+import org.apache.poi.hslf.record.ExEmbed;
+import org.apache.poi.hslf.record.ExEmbedAtom;
+import org.apache.poi.hslf.record.ExMCIMovie;
+import org.apache.poi.hslf.record.ExObjList;
+import org.apache.poi.hslf.record.ExObjListAtom;
+import org.apache.poi.hslf.record.ExOleObjAtom;
+import org.apache.poi.hslf.record.ExOleObjStg;
+import org.apache.poi.hslf.record.ExVideoContainer;
+import org.apache.poi.hslf.record.FontCollection;
+import org.apache.poi.hslf.record.HeadersFootersContainer;
+import org.apache.poi.hslf.record.MainMaster;
+import org.apache.poi.hslf.record.Notes;
+import org.apache.poi.hslf.record.PersistPtrHolder;
+import org.apache.poi.hslf.record.PositionDependentRecord;
+import org.apache.poi.hslf.record.PositionDependentRecordContainer;
+import org.apache.poi.hslf.record.Record;
+import org.apache.poi.hslf.record.RecordContainer;
+import org.apache.poi.hslf.record.RecordTypes;
+import org.apache.poi.hslf.record.Slide;
+import org.apache.poi.hslf.record.SlideListWithText;
import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
+import org.apache.poi.hslf.record.SlidePersistAtom;
+import org.apache.poi.hslf.record.TxMasterStyleAtom;
+import org.apache.poi.hslf.record.UserEditAtom;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
/**
* Add a font in this presentation
*
- * @param font
- * the font to add
- * @return 0-based index of the font
+ * @param fontInfo the font to add
+ * @return the registered HSLFFontInfo - the font info object is unique based on the typeface
*/
- public int addFont(PPFont font) {
- FontCollection fonts = getDocumentRecord().getEnvironment().getFontCollection();
- int idx = fonts.getFontIndex(font.getFontName());
- if (idx == -1) {
- idx = fonts.addFont(font.getFontName(), font.getCharSet(), font.getFontFlags(), font
- .getFontType(), font.getPitchAndFamily());
- }
- return idx;
+ public HSLFFontInfo addFont(FontInfo fontInfo) {
+ return getDocumentRecord().getEnvironment().getFontCollection().addFont(fontInfo);
}
/**
* @return of an instance of <code>PPFont</code> or <code>null</code> if not
* found
*/
- public PPFont getFont(int idx) {
- FontCollection fonts = getDocumentRecord().getEnvironment().getFontCollection();
- for (Record ch : fonts.getChildRecords()) {
- if (ch instanceof FontEntityAtom) {
- FontEntityAtom atom = (FontEntityAtom) ch;
- if (atom.getFontIndex() == idx) {
- return new PPFont(atom);
- }
- }
- }
- return null;
+ public HSLFFontInfo getFont(int idx) {
+ return getDocumentRecord().getEnvironment().getFontCollection().getFontInfo(idx);
}
/**
import java.util.Iterator;
import java.util.List;
+import org.apache.poi.common.usermodel.fonts.FontGroup;
+import org.apache.poi.common.usermodel.fonts.FontInfo;
import org.apache.poi.hslf.exceptions.HSLFException;
-import org.apache.poi.hslf.model.PPFont;
import org.apache.poi.hslf.model.textproperties.BitMaskTextProp;
import org.apache.poi.hslf.model.textproperties.FontAlignmentProp;
import org.apache.poi.hslf.model.textproperties.IndentProp;
import org.apache.poi.hslf.model.textproperties.TextPropCollection.TextPropType;
import org.apache.poi.hslf.record.ColorSchemeAtom;
import org.apache.poi.hslf.record.EscherTextboxWrapper;
-import org.apache.poi.hslf.record.FontCollection;
import org.apache.poi.hslf.record.InteractiveInfo;
import org.apache.poi.hslf.record.MasterTextPropAtom;
import org.apache.poi.hslf.record.OutlineTextRefAtom;
public void setParagraphStyle(TextPropCollection paragraphStyle) {
_paragraphStyle.copy(paragraphStyle);
}
-
+
/**
* Setting a master style reference
*
@Override
public String getDefaultFontFamily() {
- String typeface = null;
+ FontInfo fontInfo = null;
if (!_runs.isEmpty()) {
- typeface = _runs.get(0).getFontFamily();
+ HSLFTextRun tr = _runs.get(0);
+ fontInfo = tr.getFontInfo(null);
+ // fallback to LATIN if the font for the font group wasn't defined
+ if (fontInfo == null) {
+ fontInfo = tr.getFontInfo(FontGroup.LATIN);
+ }
+ }
+ if (fontInfo == null) {
+ fontInfo = HSLFFontInfoPredefined.ARIAL;
}
- return (typeface != null) ? typeface : "Arial";
+
+ return fontInfo.getTypeface();
}
@Override
return;
}
- FontCollection fc = getSheet().getSlideShow().getFontCollection();
- int idx = fc.addFont(typeface);
+ HSLFFontInfo fi = new HSLFFontInfo(typeface);
+ fi = getSheet().getSlideShow().addFont(fi);
- setParagraphTextPropVal("bullet.font", idx);
+ setParagraphTextPropVal("bullet.font", fi.getIndex());
setFlag(ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, true);
}
if (tp == null || !hasFont) {
return getDefaultFontFamily();
}
- PPFont ppFont = getSheet().getSlideShow().getFont(tp.getValue());
+ HSLFFontInfo ppFont = getSheet().getSlideShow().getFont(tp.getValue());
assert(ppFont != null);
- return ppFont.getFontName();
+ return ppFont.getTypeface();
}
@Override
String propNames[] = propName.split(",");
for (String pn : propNames) {
TextProp prop = props.findByName(pn);
- if (prop == null) {
- continue;
- }
-
- // Font properties (maybe other too???) can have an index of -1
- // so we check the master for this font index then
- if (pn.contains("font") && prop.getValue() == -1) {
- return getMasterPropVal(props, masterProps, pn);
+ if (isValidProp(prop)) {
+ return prop;
}
-
- return prop;
}
-
+
return getMasterPropVal(props, masterProps, propName);
}
-
+
private TextProp getMasterPropVal(TextPropCollection props, TextPropCollection masterProps, String propName) {
boolean isChar = props.getTextPropType() == TextPropType.character;
}
String propNames[] = propName.split(",");
- if (masterProps == null) {
+ if (masterProps == null) {
HSLFSheet sheet = getSheet();
int txtype = getRunType();
HSLFMasterSheet master = sheet.getMasterSheet();
for (String pn : propNames) {
TextProp prop = master.getStyleAttribute(txtype, getIndentLevel(), pn, isChar);
- if (prop != null) {
+ if (isValidProp(prop)) {
return prop;
}
}
} else {
for (String pn : propNames) {
TextProp prop = masterProps.findByName(pn);
- if (prop != null) {
+ if (isValidProp(prop)) {
return prop;
}
}
}
-
+
+
return null;
}
+ private static boolean isValidProp(TextProp prop) {
+ // Font properties (maybe other too???) can have an index of -1
+ // so we check the master for this font index then
+ return prop != null && (!prop.getName().contains("font") || prop.getValue() != -1);
+ }
+
/**
* Returns the named TextProp, either by fetching it (if it exists) or
* adding it (if it didn't)
if (getSheet() instanceof MasterSheet && masterProps != null) {
pc = masterProps;
}
-
+
if (val == null) {
pc.removeByName(name);
return;
if (lastPTPC == null || lastRTPC == null || ptpc == null || rtpc == null) { // NOSONAR
throw new HSLFException("Not all TextPropCollection could be determined.");
}
-
+
ptpc.updateTextSize(ptpc.getCharactersCovered() + 1);
rtpc.updateTextSize(rtpc.getCharactersCovered() + 1);
lastPTPC.updateTextSize(lastPTPC.getCharactersCovered() + 1);
assert(info != null && txinfo != null);
_txtbox.appendChildRecord(info);
_txtbox.appendChildRecord(txinfo);
- }
+ }
}
-
+
/**
- * Writes the textbox records back to the document record
+ * Writes the textbox records back to the document record
*/
private static void refreshRecords(List<HSLFTextParagraph> paragraphs) {
TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom;
/**
* {@inheritDoc}
- *
+ *
* @see RoundTripHFPlaceholder12
*/
@Override
import java.awt.Color;
import java.util.List;
+import org.apache.poi.common.usermodel.fonts.FontGroup;
+import org.apache.poi.common.usermodel.fonts.FontInfo;
import org.apache.poi.hslf.exceptions.HSLFException;
import org.apache.poi.hslf.model.textproperties.BitMaskTextProp;
import org.apache.poi.hslf.model.textproperties.CharFlagsTextProp;
/** The TextRun we belong to */
private HSLFTextParagraph parentParagraph;
private String _runText = "";
- private String _fontFamily;
+ /** Caches the font info objects until the text runs are attached to the container */
+ private HSLFFontInfo[] cachedFontInfo;
private HSLFHyperlink link;
-
+
/**
* Our paragraph and character style.
* Note - we may share these styles with other RichTextRuns
*/
private TextPropCollection characterStyle = new TextPropCollection(1, TextPropType.character);
-
+
private TextPropCollection masterStyle;
/**
public HSLFTextRun(HSLFTextParagraph parentParagraph) {
this.parentParagraph = parentParagraph;
}
-
+
public TextPropCollection getCharacterStyle() {
return characterStyle;
}
this.characterStyle.copy(characterStyle);
this.characterStyle.updateTextSize(_runText.length());
}
-
+
/**
* Setting a master style reference
*
* @param characterStyle the master style reference
- *
+ *
* @since POI 3.14-Beta1
*/
@Internal
/* package */ void setMasterStyleReference(TextPropCollection masterStyle) {
this.masterStyle = masterStyle;
}
-
-
+
+
/**
* Supply the SlideShow we belong to
*/
public void updateSheet() {
- if (_fontFamily != null) {
- setFontFamily(_fontFamily);
- _fontFamily = null;
+ if (cachedFontInfo != null) {
+ for (FontGroup tt : FontGroup.values()) {
+ setFontInfo(cachedFontInfo[tt.ordinal()], tt);
+ }
+ cachedFontInfo = null;
}
}
}
@Override
- public void setFontFamily(String fontFamily) {
- HSLFSheet sheet = parentParagraph.getSheet();
- @SuppressWarnings("resource")
+ public void setFontFamily(String typeface) {
+ setFontInfo(new HSLFFontInfo(typeface), FontGroup.LATIN);
+ }
+
+ @Override
+ public void setFontFamily(String typeface, FontGroup fontGroup) {
+ setFontInfo(new HSLFFontInfo(typeface), fontGroup);
+ }
+
+ @Override
+ public void setFontInfo(FontInfo fontInfo, FontGroup fontGroup) {
+ FontGroup fg = safeFontGroup(fontGroup);
+
+ HSLFSheet sheet = parentParagraph.getSheet();
+ @SuppressWarnings("resource")
HSLFSlideShow slideShow = (sheet == null) ? null : sheet.getSlideShow();
- if (sheet == null || slideShow == null) {
- //we can't set font since slideshow is not assigned yet
- _fontFamily = fontFamily;
- return;
- }
- // Get the index for this font (adding if needed)
- Integer fontIdx = (fontFamily == null) ? null : slideShow.getFontCollection().addFont(fontFamily);
- setCharTextPropVal("font.index", fontIdx);
+ if (sheet == null || slideShow == null) {
+ // we can't set font since slideshow is not assigned yet
+ if (cachedFontInfo == null) {
+ cachedFontInfo = new HSLFFontInfo[FontGroup.values().length];
+ }
+ cachedFontInfo[fg.ordinal()] = (fontInfo != null) ? new HSLFFontInfo(fontInfo) : null;
+ return;
+ }
+
+ String propName;
+ switch (fg) {
+ default:
+ case LATIN:
+ propName = "font.index";
+ break;
+ case COMPLEX_SCRIPT:
+ // TODO: implement TextCFException10 structure
+ case EAST_ASIAN:
+ propName = "asian.font.index";
+ break;
+ case SYMBOL:
+ propName = "symbol.font.index";
+ break;
+ }
+
+
+ // Get the index for this font, if it is not to be removed (typeface == null)
+ Integer fontIdx = null;
+ if (fontInfo != null) {
+ fontIdx = slideShow.addFont(fontInfo).getIndex();
+ }
+
+
+ setCharTextPropVal(propName, fontIdx);
+ }
+
+ @Override
+ public String getFontFamily() {
+ return getFontFamily(null);
}
+ @Override
+ public String getFontFamily(FontGroup fontGroup) {
+ HSLFFontInfo fi = getFontInfo(fontGroup);
+ return (fi != null) ? fi.getTypeface() : null;
+ }
+
@Override
- public String getFontFamily() {
- HSLFSheet sheet = parentParagraph.getSheet();
+ public HSLFFontInfo getFontInfo(final FontGroup fontGroup) {
+ FontGroup fg = safeFontGroup(fontGroup);
+
+ HSLFSheet sheet = parentParagraph.getSheet();
@SuppressWarnings("resource")
HSLFSlideShow slideShow = (sheet == null) ? null : sheet.getSlideShow();
if (sheet == null || slideShow == null) {
- return _fontFamily;
+ return (cachedFontInfo != null) ? cachedFontInfo[fg.ordinal()] : null;
}
- TextProp tp = getTextParagraph().getPropVal(characterStyle, masterStyle, "font.index,asian.font.index,ansi.font.index,symbol.font.index");
- if (tp == null) { return null; }
- return slideShow.getFontCollection().getFontWithId(tp.getValue());
+
+ String propName;
+ switch (fg) {
+ default:
+ case LATIN:
+ propName = "font.index,ansi.font.index";
+ break;
+ case COMPLEX_SCRIPT:
+ case EAST_ASIAN:
+ propName = "asian.font.index";
+ break;
+ case SYMBOL:
+ propName = "symbol.font.index";
+ break;
+ }
+
+ TextProp tp = getTextParagraph().getPropVal(characterStyle, masterStyle, propName);
+ return (tp != null) ? slideShow.getFont(tp.getValue()) : null;
}
/**
public void setFontColor(Color color) {
setFontColor(DrawPaint.createSolidPaint(color));
}
-
+
@Override
public void setFontColor(PaintStyle color) {
if (!(color instanceof SolidPaint)) {
public HSLFTextParagraph getTextParagraph() {
return parentParagraph;
}
-
+
@Override
public TextCap getTextCap() {
return TextCap.NONE;
protected void setHyperlink(HSLFHyperlink link) {
this.link = link;
}
-
+
@Override
public HSLFHyperlink getHyperlink() {
return link;
}
-
+
@Override
public HSLFHyperlink createHyperlink() {
if (link == null) {
}
return link;
}
-
+
@Override
public FieldType getFieldType() {
HSLFTextShape ts = getTextParagraph().getParentShape();
Placeholder ph = ts.getPlaceholder();
-
+
if (ph != null) {
switch (ph) {
case SLIDE_NUMBER:
}
return trList.get(0).getFieldType();
}
-
+
return null;
}
+
+ private FontGroup safeFontGroup(FontGroup fontGroup) {
+ return (fontGroup != null) ? fontGroup : FontGroup.getFontGroupFirst(getRawText());
+ }
}
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
-import java.util.Map;
import java.util.NoSuchElementException;
+import org.apache.poi.common.usermodel.fonts.FontInfo;
import org.apache.poi.hwmf.record.HwmfBrushStyle;
import org.apache.poi.hwmf.record.HwmfFont;
import org.apache.poi.hwmf.record.HwmfHatchStyle;
import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash;
import org.apache.poi.sl.draw.DrawFactory;
import org.apache.poi.sl.draw.DrawFontManager;
-import org.apache.poi.sl.draw.Drawable;
import org.apache.poi.util.LocaleUtil;
public class HwmfGraphics {
// TODO: another approx. ...
double fontW = fontH/1.8;
- int len = text.length;
- Charset charset = (font.getCharSet().getCharset() == null)?
- DEFAULT_CHARSET : font.getCharSet().getCharset();
+ Charset charset = (font.getCharset().getCharset() == null)?
+ DEFAULT_CHARSET : font.getCharset().getCharset();
String textString = new String(text, charset);
AttributedString as = new AttributedString(textString);
if (dx == null || dx.length == 0) {
}
private void addAttributes(AttributedString as, HwmfFont font) {
- DrawFontManager fontHandler = (DrawFontManager)graphicsCtx.getRenderingHint(Drawable.FONT_HANDLER);
- String fontFamily = null;
- @SuppressWarnings("unchecked")
- Map<String,String> fontMap = (Map<String,String>)graphicsCtx.getRenderingHint(Drawable.FONT_MAP);
- if (fontMap != null && fontMap.containsKey(font.getFacename())) {
- fontFamily = fontMap.get(font.getFacename());
- }
- if (fontHandler != null) {
- fontFamily = fontHandler.getRendererableFont(font.getFacename(), font.getPitchAndFamily());
- }
- if (fontFamily == null) {
- fontFamily = font.getFacename();
- }
+ DrawFontManager fontHandler = DrawFactory.getInstance(graphicsCtx).getFontManager(graphicsCtx);
+ FontInfo fontInfo = fontHandler.getMappedFont(graphicsCtx, font);
- as.addAttribute(TextAttribute.FAMILY, fontFamily);
+ as.addAttribute(TextAttribute.FAMILY, fontInfo.getTypeface());
as.addAttribute(TextAttribute.SIZE, getFontHeight(font));
as.addAttribute(TextAttribute.STRIKETHROUGH, font.isStrikeOut());
if (font.isUnderline()) {
import java.io.IOException;
import java.nio.charset.Charset;
-import java.nio.charset.UnsupportedCharsetException;
+import org.apache.poi.common.usermodel.fonts.FontCharset;
+import org.apache.poi.common.usermodel.fonts.FontFamily;
+import org.apache.poi.common.usermodel.fonts.FontInfo;
+import org.apache.poi.common.usermodel.fonts.FontPitch;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
-import org.apache.poi.util.POILogFactory;
-import org.apache.poi.util.POILogger;
/**
* The Font object specifies the attributes of a logical font
*/
-public class HwmfFont {
-
- private static final POILogger logger = POILogFactory.getLogger(HwmfFont.class);
-
- public enum WmfCharset {
- /** Specifies the English character set. */
- ANSI_CHARSET(0x00000000, "Cp1252"),
- /**
- * Specifies a character set based on the current system locale;
- * for example, when the system locale is United States English,
- * the default character set is ANSI_CHARSET.
- */
- DEFAULT_CHARSET(0x00000001, "Cp1252"),
- /** Specifies a character set of symbols. */
- SYMBOL_CHARSET(0x00000002, ""),
- /** Specifies the Apple Macintosh character set. */
- MAC_CHARSET(0x0000004D, "MacRoman"),
- /** Specifies the Japanese character set. */
- SHIFTJIS_CHARSET(0x00000080, "Shift_JIS"),
- /** Also spelled "Hangeul". Specifies the Hangul Korean character set. */
- HANGUL_CHARSET(0x00000081, "cp949"),
- /** Also spelled "Johap". Specifies the Johab Korean character set. */
- JOHAB_CHARSET(0x00000082, "x-Johab"),
- /** Specifies the "simplified" Chinese character set for People's Republic of China. */
- GB2312_CHARSET(0x00000086, "GB2312"),
- /**
- * Specifies the "traditional" Chinese character set, used mostly in
- * Taiwan and in the Hong Kong and Macao Special Administrative Regions.
- */
- CHINESEBIG5_CHARSET(0x00000088, "Big5"),
- /** Specifies the Greek character set. */
- GREEK_CHARSET(0x000000A1, "Cp1253"),
- /** Specifies the Turkish character set. */
- TURKISH_CHARSET(0x000000A2, "Cp1254"),
- /** Specifies the Vietnamese character set. */
- VIETNAMESE_CHARSET(0x000000A3, "Cp1258"),
- /** Specifies the Hebrew character set. */
- HEBREW_CHARSET(0x000000B1, "Cp1255"),
- /** Specifies the Arabic character set. */
- ARABIC_CHARSET(0x000000B2, "Cp1256"),
- /** Specifies the Baltic (Northeastern European) character set. */
- BALTIC_CHARSET(0x000000BA, "Cp1257"),
- /** Specifies the Russian Cyrillic character set. */
- RUSSIAN_CHARSET(0x000000CC, "Cp1251"),
- /** Specifies the Thai character set. */
- THAI_CHARSET(0x000000DE, "x-windows-874"),
- /** Specifies a Eastern European character set. */
- EASTEUROPE_CHARSET(0x000000EE, "Cp1250"),
- /**
- * Specifies a mapping to one of the OEM code pages,
- * according to the current system locale setting.
- */
- OEM_CHARSET(0x000000FF, "Cp1252");
-
- int flag;
- Charset charset;
-
- WmfCharset(int flag, String javaCharsetName) {
- this.flag = flag;
- if (javaCharsetName.length() > 0) {
- try {
- charset = Charset.forName(javaCharsetName);
- return;
- } catch (UnsupportedCharsetException e) {
- logger.log(POILogger.WARN, "Unsupported charset: "+javaCharsetName);
- }
- }
- charset = null;
- }
-
- /**
- *
- * @return charset for the font or <code>null</code> if there is no matching charset or
- * if the charset is a "default"
- */
- public Charset getCharset() {
- return charset;
- }
-
- public static WmfCharset valueOf(int flag) {
- for (WmfCharset cs : values()) {
- if (cs.flag == flag) return cs;
- }
- return null;
- }
- }
+public class HwmfFont implements FontInfo {
/**
* The output precision defines how closely the output must match the requested font's height,
static WmfOutPrecision valueOf(int flag) {
for (WmfOutPrecision op : values()) {
- if (op.flag == flag) return op;
+ if (op.flag == flag) {
+ return op;
+ }
}
return null;
}
static WmfClipPrecision valueOf(int flag) {
for (WmfClipPrecision cp : values()) {
- if (cp.flag == flag) return cp;
+ if (cp.flag == flag) {
+ return cp;
+ }
}
return null;
}
static WmfFontQuality valueOf(int flag) {
for (WmfFontQuality fq : values()) {
- if (fq.flag == flag) return fq;
+ if (fq.flag == flag) {
+ return fq;
+ }
}
return null;
}
}
- /**
- * A property of a font that describes its general appearance.
- */
- public enum WmfFontFamilyClass {
- /**
- * The default font is specified, which is implementation-dependent.
- */
- FF_DONTCARE (0x00),
- /**
- * Fonts with variable stroke widths, which are proportional to the actual widths of
- * the glyphs, and which have serifs. "MS Serif" is an example.
- */
- FF_ROMAN (0x01),
- /**
- * Fonts with variable stroke widths, which are proportional to the actual widths of the
- * glyphs, and which do not have serifs. "MS Sans Serif" is an example.
- */
- FF_SWISS (0x02),
- /**
- * Fonts with constant stroke width, with or without serifs. Fixed-width fonts are
- * usually modern. "Pica", "Elite", and "Courier New" are examples.
- */
- FF_MODERN (0x03),
- /**
- * Fonts designed to look like handwriting. "Script" and "Cursive" are examples.
- */
- FF_SCRIPT (0x04),
- /**
- * Novelty fonts. "Old English" is an example.
- */
- FF_DECORATIVE (0x05);
-
- int flag;
- WmfFontFamilyClass(int flag) {
- this.flag = flag;
- }
-
- static WmfFontFamilyClass valueOf(int flag) {
- for (WmfFontFamilyClass ff : values()) {
- if (ff.flag == flag) return ff;
- }
- return null;
- }
- }
- /**
- * A property of a font that describes the pitch, of the characters.
- */
- public enum WmfFontPitch {
- /**
- * The default pitch, which is implementation-dependent.
- */
- DEFAULT_PITCH (0x00),
- /**
- * A fixed pitch, which means that all the characters in the font occupy the same
- * width when output in a string.
- */
- FIXED_PITCH (0x01),
- /**
- * A variable pitch, which means that the characters in the font occupy widths
- * that are proportional to the actual widths of the glyphs when output in a string. For example,
- * the "i" and space characters usually have much smaller widths than a "W" or "O" character.
- */
- VARIABLE_PITCH (0x02);
-
- int flag;
- WmfFontPitch(int flag) {
- this.flag = flag;
- }
-
- static WmfFontPitch valueOf(int flag) {
- for (WmfFontPitch fp : values()) {
- if (fp.flag == flag) return fp;
- }
- 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
* If a typeface name in the FaceName field is specified, the CharSet value MUST match the
* character set of that typeface.
*/
- WmfCharset charSet;
+ FontCharset charSet;
/**
* An 8-bit unsigned integer that defines the output precision.
* intended for specifying fonts when the exact typeface wanted is not available.
* (LSB 4 bits)
*/
- WmfFontFamilyClass family;
+ FontFamily family;
/**
* A property of a font that describes the pitch (MSB 2 bits)
*/
- WmfFontPitch pitch;
+ FontPitch pitch;
/**
* A null-terminated string of 8-bit Latin-1 [ISO/IEC-8859-1] ANSI
italic = leis.readByte() != 0;
underline = leis.readByte() != 0;
strikeOut = leis.readByte() != 0;
- charSet = WmfCharset.valueOf(leis.readUByte());
+ charSet = FontCharset.valueOf(leis.readUByte());
outPrecision = WmfOutPrecision.valueOf(leis.readUByte());
clipPrecision = WmfClipPrecision.valueOf(leis.readUByte());
quality = WmfFontQuality.valueOf(leis.readUByte());
return strikeOut;
}
- public WmfCharset getCharSet() {
- return charSet;
- }
-
public WmfOutPrecision getOutPrecision() {
return outPrecision;
}
return pitchAndFamily;
}
- public WmfFontFamilyClass getFamily() {
- return WmfFontFamilyClass.valueOf(pitchAndFamily & 0xF);
+ @Override
+ public FontFamily getFamily() {
+ return FontFamily.valueOf(pitchAndFamily & 0xF);
}
- public WmfFontPitch getPitch() {
- return WmfFontPitch.valueOf((pitchAndFamily >>> 6) & 3);
+ @Override
+ public void setFamily(FontFamily family) {
+ throw new UnsupportedOperationException("setCharset not supported by HwmfFont.");
}
- public String getFacename() {
+ @Override
+ public FontPitch getPitch() {
+ return FontPitch.valueOf((pitchAndFamily >>> 6) & 3);
+ }
+
+ @Override
+ public void setPitch(FontPitch pitch) {
+ throw new UnsupportedOperationException("setPitch not supported by HwmfFont.");
+ }
+
+ @Override
+ public Integer getIndex() {
+ return null;
+ }
+
+ @Override
+ public void setIndex(int index) {
+ throw new UnsupportedOperationException("setIndex not supported by HwmfFont.");
+ }
+
+ @Override
+ public String getTypeface() {
return facename;
}
+
+ @Override
+ public void setTypeface(String typeface) {
+ throw new UnsupportedOperationException("setTypeface not supported by HwmfFont.");
+ }
+
+ @Override
+ public FontCharset getCharset() {
+ return charSet;
+ }
+
+ @Override
+ public void setCharset(FontCharset charset) {
+ throw new UnsupportedOperationException("setCharset not supported by HwmfFont.");
+ }
}
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
-import org.apache.poi.hpsf.CustomProperties;
-import org.apache.poi.hpsf.DocumentSummaryInformation;
-import org.apache.poi.hpsf.Section;
-import org.apache.poi.hwmf.record.HwmfFont;
+import org.apache.poi.common.usermodel.fonts.FontCharset;
import org.apache.poi.hwpf.model.ComplexFileTable;
import org.apache.poi.hwpf.model.FontTable;
import org.apache.poi.hwpf.model.OldCHPBinTable;
private Charset guessCodePage(OldFontTable fontTable) {
// pick the first non-default, non-symbol charset
for (OldFfn oldFfn : fontTable.getFontNames()) {
- HwmfFont.WmfCharset wmfCharset = HwmfFont.WmfCharset.valueOf(oldFfn.getChs()& 0xff);
+ FontCharset wmfCharset = FontCharset.valueOf(oldFfn.getChs()& 0xff);
if (wmfCharset != null &&
- wmfCharset != HwmfFont.WmfCharset.ANSI_CHARSET &&
- wmfCharset != HwmfFont.WmfCharset.DEFAULT_CHARSET &&
- wmfCharset != HwmfFont.WmfCharset.SYMBOL_CHARSET ) {
+ wmfCharset != FontCharset.ANSI &&
+ wmfCharset != FontCharset.DEFAULT &&
+ wmfCharset != FontCharset.SYMBOL ) {
return wmfCharset.getCharset();
}
}
import java.nio.charset.Charset;
-import org.apache.poi.hwmf.record.HwmfFont;
+import org.apache.poi.common.usermodel.fonts.FontCharset;
import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.POILogFactory;
return null;
}
//first byte
- short fontDescriptionLength = (short) buf[offset];
+ short fontDescriptionLength = buf[offset];
offset += 1;
if (offset + fontDescriptionLength > fontTableEnd) {
logger.log(POILogger.WARN, "Asked to read beyond font table end. Skipping font");
offset += 3;
byte chs = buf[offset];
Charset charset = null;
- HwmfFont.WmfCharset wmfCharset = HwmfFont.WmfCharset.valueOf(chs & 0xff);
+ FontCharset wmfCharset = FontCharset.valueOf(chs & 0xff);
if (wmfCharset == null) {
logger.log(POILogger.WARN, "Couldn't find font for type: " + (chs & 0xff));
} else {
import java.io.IOException;
+import org.apache.poi.common.usermodel.fonts.FontCharset;
+import org.apache.poi.common.usermodel.fonts.FontPitch;
+import org.apache.poi.hslf.usermodel.HSLFFontInfo;
+import org.apache.poi.hslf.usermodel.HSLFFontInfoPredefined;
import org.apache.poi.hslf.usermodel.HSLFSlideShow;
import org.junit.Test;
/**
- * Test adding fonts to the presenataion resources
+ * Test adding fonts to the presentation resources
*/
public final class TestPPFont {
public void testCreate() throws IOException {
HSLFSlideShow ppt = new HSLFSlideShow();
assertEquals(1, ppt.getNumberOfFonts());
- assertEquals("Arial", ppt.getFont(0).getFontName());
+ assertEquals("Arial", ppt.getFont(0).getTypeface());
//adding the same font twice
- assertEquals(0, ppt.addFont(PPFont.ARIAL));
+ assertEquals(0, (int)ppt.addFont(HSLFFontInfoPredefined.ARIAL).getIndex());
assertEquals(1, ppt.getNumberOfFonts());
- assertEquals(1, ppt.addFont(PPFont.TIMES_NEW_ROMAN));
- assertEquals(2, ppt.addFont(PPFont.COURIER_NEW));
- assertEquals(3, ppt.addFont(PPFont.WINGDINGS));
+ assertEquals(1, (int)ppt.addFont(HSLFFontInfoPredefined.TIMES_NEW_ROMAN).getIndex());
+ assertEquals(2, (int)ppt.addFont(HSLFFontInfoPredefined.COURIER_NEW).getIndex());
+ assertEquals(3, (int)ppt.addFont(HSLFFontInfoPredefined.WINGDINGS).getIndex());
assertEquals(4, ppt.getNumberOfFonts());
- assertEquals(PPFont.TIMES_NEW_ROMAN.getFontName(), ppt.getFont(1).getFontName());
- assertEquals(PPFont.COURIER_NEW.getFontName(), ppt.getFont(2).getFontName());
+ assertEquals(HSLFFontInfoPredefined.TIMES_NEW_ROMAN.getTypeface(), ppt.getFont(1).getTypeface());
+ assertEquals(HSLFFontInfoPredefined.COURIER_NEW.getTypeface(), ppt.getFont(2).getTypeface());
- PPFont font3 = ppt.getFont(3);
- assertEquals(PPFont.WINGDINGS.getFontName(), font3.getFontName());
- assertEquals(PPFont.SYMBOL_CHARSET, font3.getCharSet());
- assertEquals(PPFont.VARIABLE_PITCH, font3.getPitchAndFamily());
+ HSLFFontInfo font3 = ppt.getFont(3);
+ assertEquals(HSLFFontInfoPredefined.WINGDINGS.getTypeface(), font3.getTypeface());
+ assertEquals(FontCharset.SYMBOL, font3.getCharset());
+ assertEquals(FontPitch.VARIABLE, font3.getPitch());
ppt.close();
}
int font1 = master.get(0).getStyleAttribute(TextHeaderAtom.TITLE_TYPE, 0, "font.index", true).getValue();
int font2 = master.get(1).getStyleAttribute(TextHeaderAtom.TITLE_TYPE, 0, "font.index", true).getValue();
- assertEquals("Arial", env.getFontCollection().getFontWithId(font1));
- assertEquals("Georgia", env.getFontCollection().getFontWithId(font2));
+ assertEquals("Arial", env.getFontCollection().getFontInfo(font1).getTypeface());
+ assertEquals("Georgia", env.getFontCollection().getFontInfo(font2).getTypeface());
CharFlagsTextProp prop1 = (CharFlagsTextProp)master.get(0).getStyleAttribute(TextHeaderAtom.TITLE_TYPE, 0, "char_flags", true);
assertEquals(false, prop1.getSubValue(CharFlagsTextProp.BOLD_IDX));
int b1 = master.get(0).getStyleAttribute(TextHeaderAtom.BODY_TYPE, 0, "bullet.font", false).getValue();
int b2 = master.get(1).getStyleAttribute(TextHeaderAtom.BODY_TYPE, 0, "bullet.font", false).getValue();
- assertEquals("Arial", env.getFontCollection().getFontWithId(b1));
- assertEquals("Georgia", env.getFontCollection().getFontWithId(b2));
+ assertEquals("Arial", env.getFontCollection().getFontInfo(b1).getTypeface());
+ assertEquals("Georgia", env.getFontCollection().getFontInfo(b2).getTypeface());
ppt.close();
}
package org.apache.poi.hslf.record;
import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
-import junit.framework.TestCase;
+import org.apache.poi.hslf.usermodel.HSLFFontInfo;
+import org.apache.poi.hslf.usermodel.HSLFFontInfoPredefined;
+import org.apache.poi.poifs.storage.RawDataUtil;
+import org.junit.BeforeClass;
+import org.junit.Test;
/**
- * Tests <code>FontCollection</code> and <code>FontEntityAtom</code> records
- *
- * @author Yegor Kozlov
+ * Tests {@code FontCollection} and {@code FontEntityAtom} records
*/
-public final class TestFontCollection extends TestCase {
+public final class TestFontCollection {
// From a real file
- private final byte[] data = new byte[] {
- 0x0F, 0x00, 0xD5-256, 0x07, 0x4C, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xB7-256, 0x0F, 0x44, 0x00, 0x00, 0x00,
- 0x54, 0x00, 0x69, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x73, 0x00,
- 0x20, 0x00, 0x4E, 0x00, 0x65, 0x00, 0x77, 0x00, 0x20, 0x00,
- 0x52, 0x00, 0x6F, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x6E, 0x00,
- 0x00, 0x00, 0x74, 0x34, 0xB8-256, 0x00, 0x7C, 0xDA-256, 0x12, 0x00,
- 0x64, 0xDA-256, 0x12, 0x00, 0x76, 0xC7-256, 0x0B, 0x30, 0x08, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xDA-256, 0x12, 0x00,
- 0x28, 0xDD-256, 0x0D, 0x30, 0x00, 0x00, 0x04, 0x00 };
+ private static byte[] data;
+
+ @BeforeClass
+ public static void init() throws IOException {
+ data = RawDataUtil.decompress(
+ "H4sIAAAAAAAAAONnuMruwwAC2/ldgGQIQyZDLkMqQzGDAoMfkC4H0kEM+U"+
+ "CxRIY8oHyJyQ6GmltCDClAXHac24CDAQJAYhp3eQ0YGFgYAAusGftUAAAA"
+ );
+ }
+ @Test
public void testFonts() {
FontCollection fonts = new FontCollection(data, 0, data.length);
Record[] child = fonts.getChildRecords();
assertEquals(fnt.getFontName(), "Times New Roman");
}
+ @Test
public void testAddFont() {
FontCollection fonts = new FontCollection(data, 0, data.length);
- int idx = fonts.addFont("Times New Roman");
- assertEquals(idx, 0);
- idx = fonts.addFont("Helvetica");
- assertEquals(idx, 1);
- idx = fonts.addFont("Arial");
- assertEquals(idx, 2);
- idx = fonts.addFont("Arial"); //the font being added twice
- assertEquals(idx, 2);
+ HSLFFontInfo fi = fonts.addFont(HSLFFontInfoPredefined.TIMES_NEW_ROMAN);
+ assertEquals((int)fi.getIndex(), 0);
+ fi = fonts.addFont(new HSLFFontInfo("Helvetica"));
+ assertEquals((int)fi.getIndex(), 1);
+ fi = fonts.addFont(HSLFFontInfoPredefined.ARIAL);
+ assertEquals((int)fi.getIndex(), 2);
+ //the font being added twice
+ fi = fonts.addFont(HSLFFontInfoPredefined.ARIAL);
+ assertEquals((int)fi.getIndex(), 2);
// Font collection should contain 3 fonts
Record[] child = fonts.getChildRecords();
assertEquals(child.length, 3);
// Check we get the right font name for the indicies
- assertEquals("Times New Roman", fonts.getFontWithId(0));
- assertEquals("Helvetica", fonts.getFontWithId(1));
- assertEquals("Arial", fonts.getFontWithId(2));
- assertNull(fonts.getFontWithId(3));
+ assertEquals("Times New Roman", fonts.getFontInfo(0).getTypeface());
+ assertEquals("Helvetica", fonts.getFontInfo(1).getTypeface());
+ assertEquals("Arial", fonts.getFontInfo(2).getTypeface());
+ assertNull(fonts.getFontInfo(3));
}
+ @Test
public void testWrite() throws Exception {
FontCollection fonts = new FontCollection(data, 0, data.length);
ByteArrayOutputStream out = new ByteArrayOutputStream();
import java.util.Set;
import org.apache.poi.POIDataSamples;
+import org.apache.poi.common.usermodel.fonts.FontGroup;
import org.apache.poi.ddf.AbstractEscherOptRecord;
import org.apache.poi.ddf.EscherArrayProperty;
import org.apache.poi.ddf.EscherColorRef;
for (List<HSLFTextParagraph> paraList : sl.getTextParagraphs()) {
for (HSLFTextParagraph htp : paraList) {
for (HSLFTextRun htr : htp) {
- String actFamily = htr.getFontFamily();
+ String actFamily = htr.getFontFamily(FontGroup.EAST_ASIAN);
assertEquals(expFamily, actFamily);
}
}
package org.apache.poi.hwmf;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import static org.apache.poi.POITestCase.assertContains;
+import static org.junit.Assert.assertEquals;
-import javax.imageio.ImageIO;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
+import javax.imageio.ImageIO;
+
import org.apache.poi.POIDataSamples;
import org.apache.poi.hwmf.record.HwmfFill.HwmfImageRecord;
import org.apache.poi.hwmf.record.HwmfFont;
SlideShow<?,?> ss = SlideShowFactory.create(fis);
int wmfIdx = 1;
for (PictureData pd : ss.getPictureData()) {
- if (pd.getType() != PictureType.WMF) continue;
+ if (pd.getType() != PictureType.WMF) {
+ continue;
+ }
byte wmfData[] = pd.getData();
String filename = String.format(Locale.ROOT, "%s-%04d.wmf", basename, wmfIdx);
FileOutputStream fos = new FileOutputStream(new File(outdir, filename));
for (HwmfRecord r : wmf.getRecords()) {
if (r.getRecordType().equals(HwmfRecordType.createFontIndirect)) {
HwmfFont font = ((HwmfText.WmfCreateFontIndirect)r).getFont();
- charset = (font.getCharSet().getCharset() == null) ? LocaleUtil.CHARSET_1252 : font.getCharSet().getCharset();
+ charset = (font.getCharset().getCharset() == null) ? LocaleUtil.CHARSET_1252 : font.getCharset().getCharset();
}
if (r.getRecordType().equals(HwmfRecordType.extTextOut)) {
HwmfText.WmfExtTextOut textOut = (HwmfText.WmfExtTextOut)r;
for (HwmfRecord r : wmf.getRecords()) {
if (r.getRecordType().equals(HwmfRecordType.createFontIndirect)) {
HwmfFont font = ((HwmfText.WmfCreateFontIndirect)r).getFont();
- charset = (font.getCharSet().getCharset() == null) ? LocaleUtil.CHARSET_1252 : font.getCharSet().getCharset();
+ charset = (font.getCharset().getCharset() == null) ? LocaleUtil.CHARSET_1252 : font.getCharset().getCharset();
}
if (r.getRecordType().equals(HwmfRecordType.extTextOut)) {
HwmfText.WmfExtTextOut textOut = (HwmfText.WmfExtTextOut)r;
import java.nio.charset.Charset;
import org.apache.poi.OldFileFormatException;
-import org.apache.poi.hwmf.record.HwmfFont;
+import org.apache.poi.common.usermodel.fonts.FontCharset;
import org.apache.poi.hwpf.HWPFOldDocument;
import org.apache.poi.hwpf.HWPFTestCase;
import org.apache.poi.hwpf.HWPFTestDataSamples;
OldFontTable oldFontTable = doc.getOldFontTable();
assertEquals(5, oldFontTable.getFontNames().length);
assertEquals("\u7D30\u660E\u9AD4", oldFontTable.getFontNames()[0].getMainFontName());
- assertEquals(HwmfFont.WmfCharset.CHINESEBIG5_CHARSET.getCharset(), Charset.forName("Big5"));
+ assertEquals(FontCharset.CHINESEBIG5.getCharset(), Charset.forName("Big5"));
assertEquals("Times New Roman", oldFontTable.getFontNames()[1].getMainFontName());
doc.close();