From 2325cdb9e6dff1496eb6b9ed482571206cbca8dd Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Sun, 23 Jul 2017 22:45:47 +0000 Subject: [PATCH] Bug 61331 - Font group handling / common font interface git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1802741 13f79535-47bb-0310-9956-ffa450edef68 --- .../common/usermodel/fonts/FontCharset.java | 124 ++++++ .../common/usermodel/fonts/FontFamily.java | 80 ++++ .../poi/common/usermodel/fonts/FontGroup.java | 149 +++++++ .../poi/common/usermodel/fonts/FontInfo.java | 102 +++++ .../poi/common/usermodel/fonts/FontPitch.java | 74 ++++ .../org/apache/poi/sl/draw/DrawFactory.java | 11 + .../org/apache/poi/sl/draw/DrawFontInfo.java | 89 +++++ .../apache/poi/sl/draw/DrawFontManager.java | 54 ++- .../poi/sl/draw/DrawFontManagerDefault.java | 101 +++++ .../apache/poi/sl/draw/DrawTextFragment.java | 11 +- .../apache/poi/sl/draw/DrawTextParagraph.java | 262 ++++++------- .../org/apache/poi/sl/usermodel/TextRun.java | 58 ++- .../apache/poi/ss/usermodel/FontCharset.java | 5 +- .../poi/xslf/usermodel/XSLFTextRun.java | 362 +++++++++++++----- .../org/apache/poi/sl/TestFonts.java | 67 ++-- .../src/org/apache/poi/hslf/model/PPFont.java | 243 ------------ .../textproperties/CharFlagsTextProp.java | 2 +- .../poi/hslf/record/FontCollection.java | 126 +++--- .../poi/hslf/record/FontEntityAtom.java | 21 +- .../poi/hslf/record/RecordContainer.java | 65 +++- .../poi/hslf/usermodel/HSLFFontInfo.java | 215 +++++++++++ .../usermodel/HSLFFontInfoPredefined.java | 97 +++++ .../poi/hslf/usermodel/HSLFSlideShow.java | 58 +-- .../poi/hslf/usermodel/HSLFTextParagraph.java | 74 ++-- .../poi/hslf/usermodel/HSLFTextRun.java | 147 +++++-- .../apache/poi/hwmf/draw/HwmfGraphics.java | 25 +- .../org/apache/poi/hwmf/record/HwmfFont.java | 243 +++--------- .../org/apache/poi/hwpf/HWPFOldDocument.java | 14 +- .../src/org/apache/poi/hwpf/model/OldFfn.java | 6 +- .../org/apache/poi/hslf/model/TestPPFont.java | 28 +- .../poi/hslf/model/TestSlideMaster.java | 8 +- .../poi/hslf/record/TestFontCollection.java | 62 +-- .../apache/poi/hslf/usermodel/TestBugs.java | 3 +- .../org/apache/poi/hwmf/TestHwmfParsing.java | 14 +- .../hwpf/usermodel/TestHWPFOldDocument.java | 4 +- 35 files changed, 2015 insertions(+), 989 deletions(-) create mode 100644 src/java/org/apache/poi/common/usermodel/fonts/FontCharset.java create mode 100644 src/java/org/apache/poi/common/usermodel/fonts/FontFamily.java create mode 100644 src/java/org/apache/poi/common/usermodel/fonts/FontGroup.java create mode 100644 src/java/org/apache/poi/common/usermodel/fonts/FontInfo.java create mode 100644 src/java/org/apache/poi/common/usermodel/fonts/FontPitch.java create mode 100644 src/java/org/apache/poi/sl/draw/DrawFontInfo.java create mode 100644 src/java/org/apache/poi/sl/draw/DrawFontManagerDefault.java delete mode 100644 src/scratchpad/src/org/apache/poi/hslf/model/PPFont.java create mode 100644 src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfo.java create mode 100644 src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfoPredefined.java diff --git a/src/java/org/apache/poi/common/usermodel/fonts/FontCharset.java b/src/java/org/apache/poi/common/usermodel/fonts/FontCharset.java new file mode 100644 index 000000000..aeeca9284 --- /dev/null +++ b/src/java/org/apache/poi/common/usermodel/fonts/FontCharset.java @@ -0,0 +1,124 @@ +/* ==================================================================== + 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 null 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 diff --git a/src/java/org/apache/poi/common/usermodel/fonts/FontFamily.java b/src/java/org/apache/poi/common/usermodel/fonts/FontFamily.java new file mode 100644 index 000000000..8faa788f5 --- /dev/null +++ b/src/java/org/apache/poi/common/usermodel/fonts/FontFamily.java @@ -0,0 +1,80 @@ +/* ==================================================================== +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 diff --git a/src/java/org/apache/poi/common/usermodel/fonts/FontGroup.java b/src/java/org/apache/poi/common/usermodel/fonts/FontGroup.java new file mode 100644 index 000000000..b1c294588 --- /dev/null +++ b/src/java/org/apache/poi/common/usermodel/fonts/FontGroup.java @@ -0,0 +1,149 @@ +/* ==================================================================== +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 Office Open XML Themes, Schemes, and Fonts + */ +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 UCS_RANGES; + + static { + UCS_RANGES = new TreeMap(); + 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 getFontGroupRanges(String runText) { + List ttrList = new ArrayList(); + 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 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 diff --git a/src/java/org/apache/poi/common/usermodel/fonts/FontInfo.java b/src/java/org/apache/poi/common/usermodel/fonts/FontInfo.java new file mode 100644 index 000000000..ecb5a6968 --- /dev/null +++ b/src/java/org/apache/poi/common/usermodel/fonts/FontInfo.java @@ -0,0 +1,102 @@ +/* ==================================================================== +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.

+ * + * 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.

+ * + * Setting a unsupported property results in an {@link UnsupportedOperationException}. + * + * @since POI 3.17-beta2 + * + * @see LOGFONT structure + */ +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 diff --git a/src/java/org/apache/poi/common/usermodel/fonts/FontPitch.java b/src/java/org/apache/poi/common/usermodel/fonts/FontPitch.java new file mode 100644 index 000000000..78c653394 --- /dev/null +++ b/src/java/org/apache/poi/common/usermodel/fonts/FontPitch.java @@ -0,0 +1,74 @@ +/* ==================================================================== + 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 LOGFONT structure + */ + 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); + } +} + diff --git a/src/java/org/apache/poi/sl/draw/DrawFactory.java b/src/java/org/apache/poi/sl/draw/DrawFactory.java index 53b2bcba7..376a8daab 100644 --- a/src/java/org/apache/poi/sl/draw/DrawFactory.java +++ b/src/java/org/apache/poi/sl/draw/DrawFactory.java @@ -236,4 +236,15 @@ public class DrawFactory { } } } + + /** + * 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 diff --git a/src/java/org/apache/poi/sl/draw/DrawFontInfo.java b/src/java/org/apache/poi/sl/draw/DrawFontInfo.java new file mode 100644 index 000000000..dc7afb4e2 --- /dev/null +++ b/src/java/org/apache/poi/sl/draw/DrawFontInfo.java @@ -0,0 +1,89 @@ +/* + * ==================================================================== + * 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."); + } +} diff --git a/src/java/org/apache/poi/sl/draw/DrawFontManager.java b/src/java/org/apache/poi/sl/draw/DrawFontManager.java index 5b74f8400..0c16dd994 100644 --- a/src/java/org/apache/poi/sl/draw/DrawFontManager.java +++ b/src/java/org/apache/poi/sl/draw/DrawFontManager.java @@ -19,6 +19,12 @@ 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. * @@ -29,28 +35,50 @@ public interface DrawFontManager { /** * 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.

+ * + * 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 Drawing exotic fonts in a java applet + * @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); } diff --git a/src/java/org/apache/poi/sl/draw/DrawFontManagerDefault.java b/src/java/org/apache/poi/sl/draw/DrawFontManagerDefault.java new file mode 100644 index 000000000..c439fc926 --- /dev/null +++ b/src/java/org/apache/poi/sl/draw/DrawFontManagerDefault.java @@ -0,0 +1,101 @@ +/* + * ==================================================================== + * 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 fontMap = (Map)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; + } +} diff --git a/src/java/org/apache/poi/sl/draw/DrawTextFragment.java b/src/java/org/apache/poi/sl/draw/DrawTextFragment.java index 0eceb9364..898ac51bd 100644 --- a/src/java/org/apache/poi/sl/draw/DrawTextFragment.java +++ b/src/java/org/apache/poi/sl/draw/DrawTextFragment.java @@ -70,7 +70,7 @@ public class DrawTextFragment implements Drawable { * @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; } @@ -78,9 +78,14 @@ public class DrawTextFragment implements Drawable { * @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; } /** diff --git a/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java b/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java index 10d4edfd6..7f708d124 100644 --- a/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java +++ b/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java @@ -32,8 +32,11 @@ import java.text.AttributedCharacterIterator.Attribute; 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; @@ -50,11 +53,14 @@ import org.apache.poi.sl.usermodel.TextRun.FieldType; 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"); @@ -200,7 +206,7 @@ public class DrawTextParagraph implements Drawable { 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(); @@ -325,12 +331,6 @@ public class DrawTextParagraph implements Drawable { return null; } - String buFont = bulletStyle.getBulletFont(); - if (buFont == null) { - buFont = paragraph.getDefaultFontFamily(); - } - assert(buFont != null); - PlaceableShape ps = getParagraphShape(); PaintStyle fgPaintStyle = bulletStyle.getBulletFontColor(); Paint fgPaint; @@ -351,10 +351,21 @@ public class DrawTextParagraph implements Drawable { fontSize = (float)-buSz; } + String buFontStr = bulletStyle.getBulletFont(); + if (buFontStr == null) { + buFontStr = paragraph.getDefaultFontFamily(); + } + assert(buFontStr != null); + FontInfo buFont = new DrawFontInfo(buFontStr); - AttributedString str = new AttributedString(mapFontCharset(buCharacter,buFont)); + + DrawFontManager dfm = DrawFactory.getInstance(graphics).getFontManager(graphics); + // TODO: check font group defaulting to Symbol + buFont = dfm.getMappedFont(graphics, 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()); @@ -365,7 +376,7 @@ public class DrawTextParagraph implements Drawable { 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(); @@ -557,11 +568,8 @@ public class DrawTextParagraph implements Drawable { } PlaceableShape ps = getParagraphShape(); - DrawFontManager fontHandler = (DrawFontManager)graphics.getRenderingHint(Drawable.FONT_HANDLER); - @SuppressWarnings("unchecked") - Map fontMap = (Map)graphics.getRenderingHint(Drawable.FONT_MAP); - @SuppressWarnings("unchecked") - Map fallbackMap = (Map)graphics.getRenderingHint(Drawable.FONT_FALLBACK); + DrawFontManager dfm = DrawFactory.getInstance(graphics).getFontManager(graphics); + assert(dfm != null); for (TextRun run : paragraph){ String runText = getRenderableText(graphics, run); @@ -571,36 +579,12 @@ public class DrawTextParagraph implements Drawable { } // 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)); @@ -630,39 +614,14 @@ public class DrawTextParagraph implements Drawable { 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 @@ -681,15 +640,91 @@ public class DrawTextParagraph implements Drawable { return string; } - private String getFontWithFallback(Map fontMap, String mappedFont) { - if (fontMap != null) { - if (fontMap.containsKey(mappedFont)) { - mappedFont = fontMap.get(mappedFont); - } else if (fontMap.containsKey("*")) { - mappedFont = fontMap.get("*"); + /** + * Processing the glyphs is done in two steps. + *

  • 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 graphics + * @param dfm + * @param attList + * @param beginIndex + * @param run + * @param runText + * + * @see Office Open XML Themes, Schemes, and Fonts + */ + private void processGlyphs(Graphics2D graphics, DrawFontManager dfm, List 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 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; + } + + 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 (partEndDrawing exotic fonts in a java applet - * @see StringUtil#mapMsCodepointString(String) - */ - 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 - * - * 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 Font.canDisplayUpTo does not work with supplementary characters - */ - 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; - } - i++; - } - return -1; - } } diff --git a/src/java/org/apache/poi/sl/usermodel/TextRun.java b/src/java/org/apache/poi/sl/usermodel/TextRun.java index 32b9c9933..394166071 100644 --- a/src/java/org/apache/poi/sl/usermodel/TextRun.java +++ b/src/java/org/apache/poi/sl/usermodel/TextRun.java @@ -19,6 +19,8 @@ package org.apache.poi.sl.usermodel; 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; @@ -26,12 +28,18 @@ 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 } @@ -87,18 +95,64 @@ public interface TextRun { 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 null 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 */ diff --git a/src/java/org/apache/poi/ss/usermodel/FontCharset.java b/src/java/org/apache/poi/ss/usermodel/FontCharset.java index 90e4038b3..64953675f 100644 --- a/src/java/org/apache/poi/ss/usermodel/FontCharset.java +++ b/src/java/org/apache/poi/ss/usermodel/FontCharset.java @@ -17,13 +17,16 @@ 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), diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java index 353bbbf2b..d5d57312f 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java @@ -18,6 +18,11 @@ package org.apache.poi.xslf.usermodel; 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; @@ -28,6 +33,8 @@ import org.apache.poi.util.Beta; 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; @@ -85,8 +92,8 @@ public class XSLFTextRun implements TextRun { } else if (_r instanceof CTTextLineBreak) { return "\n"; } - - + + String txt = ((CTRegularTextRun)_r).getT(); TextCap cap = getTextCap(); StringBuffer buf = new StringBuffer(); @@ -139,7 +146,7 @@ public class XSLFTextRun implements TextRun { public void setFontColor(Color color) { setFontColor(DrawPaint.createSolidPaint(color)); } - + @Override public void setFontColor(PaintStyle color) { if (!(color instanceof SolidPaint)) { @@ -147,10 +154,10 @@ public class XSLFTextRun implements TextRun { } 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); } @@ -164,7 +171,7 @@ public class XSLFTextRun implements TextRun { if (props == null) { return false; } - + XSLFShape shape = _p.getParentShape(); CTShapeStyle style = shape.getSpStyle(); CTSchemeColor phClr = null; @@ -177,12 +184,12 @@ public class XSLFTextRun implements TextRun { 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; } }; @@ -270,88 +277,51 @@ public class XSLFTextRun implements TextRun { } @Override - public void setFontFamily(String typeface){ - setFontFamily(typeface, (byte)-1, (byte)-1, false); - } - - public void setFontFamily(String typeface, byte charset, byte pictAndFamily, boolean isSymbol){ - CTTextCharacterProperties rPr = getRPr(true); - - 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); - } - } - } + public void setFontFamily(String typeface) { + FontGroup fg = FontGroup.getFontGroupFirst(getRawText()); + new XSLFFontInfo(fg).setTypeface(typeface); } @Override - public String getFontFamily(){ - final XSLFTheme theme = _p.getParentShape().getSheet().getTheme(); + public void setFontFamily(String typeface, FontGroup fontGroup) { + new XSLFFontInfo(fontGroup).setTypeface(typeface); + } - CharacterPropertyFetcher visitor = new CharacterPropertyFetcher(_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 void setFontInfo(FontInfo fontInfo, FontGroup fontGroup) { + new XSLFFontInfo(fontGroup).copyFrom(fontInfo); + } - return visitor.getValue(); + @Override + public String getFontFamily() { + FontGroup fg = FontGroup.getFontGroupFirst(getRawText()); + return new XSLFFontInfo(fg).getTypeface(); + } + + @Override + public String getFontFamily(FontGroup fontGroup) { + return new XSLFFontInfo(fontGroup).getTypeface(); + } + + @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 visitor = new CharacterPropertyFetcher(_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 @@ -574,7 +544,7 @@ public class XSLFTextRun implements TextRun { @Override public XSLFHyperlink getHyperlink(){ CTTextCharacterProperties rPr = getRPr(false); - if (rPr == null) { + if (rPr == null) { return null; } CTHyperlink hl = rPr.getHlinkClick(); @@ -592,11 +562,11 @@ public class XSLFTextRun implements TextRun { 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 @@ -654,8 +624,8 @@ public class XSLFTextRun implements TextRun { setStrikethrough(strike); } } - - + + @Override public FieldType getFieldType() { if (_r instanceof CTTextField) { @@ -666,4 +636,224 @@ public class XSLFTextRun implements TextRun { } 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 visitor = new CharacterPropertyFetcher(_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; + } + } } diff --git a/src/ooxml/testcases/org/apache/poi/sl/TestFonts.java b/src/ooxml/testcases/org/apache/poi/sl/TestFonts.java index 411c1929e..8b5afc5b8 100644 --- a/src/ooxml/testcases/org/apache/poi/sl/TestFonts.java +++ b/src/ooxml/testcases/org/apache/poi/sl/TestFonts.java @@ -37,6 +37,7 @@ import java.util.HashMap; 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; @@ -47,10 +48,8 @@ import org.apache.poi.sl.usermodel.TextBox; 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; /** @@ -71,14 +70,14 @@ public class TestFonts { "\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(); @@ -87,83 +86,65 @@ public class TestFonts { 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 fallbackMap = new HashMap(); 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); } } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/PPFont.java b/src/scratchpad/src/org/apache/poi/hslf/model/PPFont.java deleted file mode 100644 index 7de68dc76..000000000 --- a/src/scratchpad/src/org/apache/poi/hslf/model/PPFont.java +++ /dev/null @@ -1,243 +0,0 @@ -/* ==================================================================== - 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. - *

    - * In PowerPoint Font is a shared resource and can be shared among text object in the presentation. - *

    - * 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 - *

    - * Bit 1: Raster Font - * Bit 2: Device Font - * Bit 3: TrueType Font - *

    - * - * @param val - the font type - */ - public void setFontType(int val){ - type = val; - } - - /** - * get the font type - *

    - * Bit 1: Raster Font - * Bit 2: Device Font - * Bit 3: TrueType Font - *

    - * - * @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); - } -} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/CharFlagsTextProp.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/CharFlagsTextProp.java index f7cd03701..021d1c4a8 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/CharFlagsTextProp.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/CharFlagsTextProp.java @@ -43,7 +43,7 @@ public class CharFlagsTextProp extends BitMaskTextProp { "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 diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java b/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java index b90c698dd..6f138fa08 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java @@ -17,38 +17,37 @@ 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.*; - /** - * FontCollection 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 fonts; + private final Map fonts = new LinkedHashMap(); 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 List - fonts = new ArrayList(); - 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()); } } } @@ -56,7 +55,8 @@ public final class FontCollection extends RecordContainer { /** * Return the type, which is 2005 */ - public long getRecordType() { + @Override + public long getRecordType() { return RecordTypes.FontCollection.typeID; } @@ -64,66 +64,72 @@ public final class FontCollection extends RecordContainer { * 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; + public HSLFFontInfo addFont(FontInfo fontInfo) { + HSLFFontInfo fi = getFontInfo(fontInfo.getTypeface()); + if (fi != null) { + return fi; + } - return addFont(name, 0, 0, 4, PPFont.FF_SWISS | PPFont.VARIABLE_PITCH); - } - - 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); - } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/FontEntityAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/FontEntityAtom.java index 80126fdb9..c0be88507 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/FontEntityAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/FontEntityAtom.java @@ -19,6 +19,7 @@ package org.apache.poi.hslf.record; 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; @@ -97,20 +98,18 @@ public final class FontEntityAtom extends RecordAtom { * 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'; - } + 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"); + } - // 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 + // 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){ diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java b/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java index 3ad7b086c..c3fc1f1d2 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java @@ -17,20 +17,19 @@ 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 @@ -40,12 +39,14 @@ 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; } /* =============================================================== @@ -70,14 +71,16 @@ public abstract class RecordContainer extends Record /** * 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; } /** @@ -138,8 +141,11 @@ public abstract class RecordContainer extends Record Record rm = null; ArrayList lst = new ArrayList(); 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; @@ -152,17 +158,21 @@ public abstract class RecordContainer extends Record /** * 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) { @@ -171,14 +181,16 @@ public abstract class RecordContainer extends Record // 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) { @@ -187,18 +199,27 @@ public abstract class RecordContainer extends Record // 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; } @@ -220,7 +241,15 @@ public abstract class RecordContainer extends Record /** * 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 diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfo.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfo.java new file mode 100644 index 000000000..b56b55381 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfo.java @@ -0,0 +1,215 @@ +/* ==================================================================== + 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.

    + * + * 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.

    + * + * 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; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfoPredefined.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfoPredefined.java new file mode 100644 index 000000000..1f016f99e --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfoPredefined.java @@ -0,0 +1,97 @@ +/* ==================================================================== + 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."); + } +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java index 4cfa72d33..c79816de3 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java @@ -33,6 +33,7 @@ import java.util.HashMap; 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; @@ -41,9 +42,34 @@ import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException; 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; @@ -874,18 +900,11 @@ public final class HSLFSlideShow implements SlideShowPPFont or null 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); } /** diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java index 9fa73ac5d..a3c75678b 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java @@ -25,8 +25,9 @@ import java.util.ArrayList; 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; @@ -38,7 +39,6 @@ import org.apache.poi.hslf.model.textproperties.TextPropCollection; 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; @@ -159,7 +159,7 @@ public final class HSLFTextParagraph implements TextParagraph paragraphs) { TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom; @@ -1624,7 +1632,7 @@ public final class HSLFTextParagraph implements TextParagraph fontMap = (Map)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()) { diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java index f6e256381..ac667f73f 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java @@ -19,102 +19,18 @@ package org.apache.poi.hwmf.record; 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 null 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, @@ -176,7 +92,9 @@ public class HwmfFont { static WmfOutPrecision valueOf(int flag) { for (WmfOutPrecision op : values()) { - if (op.flag == flag) return op; + if (op.flag == flag) { + return op; + } } return null; } @@ -237,7 +155,9 @@ public class HwmfFont { static WmfClipPrecision valueOf(int flag) { for (WmfClipPrecision cp : values()) { - if (cp.flag == flag) return cp; + if (cp.flag == flag) { + return cp; + } } return null; } @@ -292,90 +212,15 @@ public class HwmfFont { 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 @@ -454,7 +299,7 @@ public class HwmfFont { * 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. @@ -486,12 +331,12 @@ public class HwmfFont { * 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 @@ -509,7 +354,7 @@ public class HwmfFont { 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()); @@ -561,10 +406,6 @@ public class HwmfFont { return strikeOut; } - public WmfCharset getCharSet() { - return charSet; - } - public WmfOutPrecision getOutPrecision() { return outPrecision; } @@ -581,15 +422,53 @@ public class HwmfFont { 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."); + } } diff --git a/src/scratchpad/src/org/apache/poi/hwpf/HWPFOldDocument.java b/src/scratchpad/src/org/apache/poi/hwpf/HWPFOldDocument.java index 063d8b7b0..d0da433f2 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/HWPFOldDocument.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/HWPFOldDocument.java @@ -19,13 +19,9 @@ package org.apache.poi.hwpf; 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; @@ -200,11 +196,11 @@ public class HWPFOldDocument extends HWPFDocumentCore { 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(); } } diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/OldFfn.java b/src/scratchpad/src/org/apache/poi/hwpf/model/OldFfn.java index d50ac4ec0..ebbc17e38 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/OldFfn.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/OldFfn.java @@ -19,7 +19,7 @@ package org.apache.poi.hwpf.model; 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; @@ -56,7 +56,7 @@ public final class OldFfn { 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"); @@ -67,7 +67,7 @@ public final class OldFfn { 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 { diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestPPFont.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestPPFont.java index 2a1a6afa7..bc4c61c14 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestPPFont.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestPPFont.java @@ -21,12 +21,16 @@ import static org.junit.Assert.assertEquals; 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 { @@ -34,25 +38,25 @@ 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(); } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java index b18683ddd..80772d3a1 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java @@ -64,8 +64,8 @@ public final class TestSlideMaster { 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)); @@ -83,8 +83,8 @@ public final class TestSlideMaster { 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(); } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestFontCollection.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestFontCollection.java index 2d595f332..7ca07bd82 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestFontCollection.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestFontCollection.java @@ -18,29 +18,34 @@ 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 FontCollection and FontEntityAtom 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(); @@ -50,28 +55,31 @@ public final class TestFontCollection extends TestCase { 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(); diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java index 462b77ed2..024650187 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java @@ -49,6 +49,7 @@ import java.util.Map; 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; @@ -848,7 +849,7 @@ public final class TestBugs { for (List 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); } } diff --git a/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java b/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java index 210ae95cb..8aac3b29b 100644 --- a/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java +++ b/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java @@ -17,11 +17,9 @@ 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; @@ -40,6 +38,8 @@ import java.util.Locale; 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; @@ -118,7 +118,9 @@ public class TestHwmfParsing { 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)); @@ -211,7 +213,7 @@ public class TestHwmfParsing { 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; @@ -239,7 +241,7 @@ public class TestHwmfParsing { 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; diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestHWPFOldDocument.java b/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestHWPFOldDocument.java index 9d53f67b6..50ddda329 100644 --- a/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestHWPFOldDocument.java +++ b/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestHWPFOldDocument.java @@ -24,7 +24,7 @@ import java.io.IOException; 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; @@ -201,7 +201,7 @@ public final class TestHWPFOldDocument extends HWPFTestCase { 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();