]> source.dussan.org Git - poi.git/commitdiff
Bug 61331 - Font group handling / common font interface
authorAndreas Beeker <kiwiwings@apache.org>
Sun, 23 Jul 2017 22:45:47 +0000 (22:45 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Sun, 23 Jul 2017 22:45:47 +0000 (22:45 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1802741 13f79535-47bb-0310-9956-ffa450edef68

35 files changed:
src/java/org/apache/poi/common/usermodel/fonts/FontCharset.java [new file with mode: 0644]
src/java/org/apache/poi/common/usermodel/fonts/FontFamily.java [new file with mode: 0644]
src/java/org/apache/poi/common/usermodel/fonts/FontGroup.java [new file with mode: 0644]
src/java/org/apache/poi/common/usermodel/fonts/FontInfo.java [new file with mode: 0644]
src/java/org/apache/poi/common/usermodel/fonts/FontPitch.java [new file with mode: 0644]
src/java/org/apache/poi/sl/draw/DrawFactory.java
src/java/org/apache/poi/sl/draw/DrawFontInfo.java [new file with mode: 0644]
src/java/org/apache/poi/sl/draw/DrawFontManager.java
src/java/org/apache/poi/sl/draw/DrawFontManagerDefault.java [new file with mode: 0644]
src/java/org/apache/poi/sl/draw/DrawTextFragment.java
src/java/org/apache/poi/sl/draw/DrawTextParagraph.java
src/java/org/apache/poi/sl/usermodel/TextRun.java
src/java/org/apache/poi/ss/usermodel/FontCharset.java
src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
src/ooxml/testcases/org/apache/poi/sl/TestFonts.java
src/scratchpad/src/org/apache/poi/hslf/model/PPFont.java [deleted file]
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/CharFlagsTextProp.java
src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java
src/scratchpad/src/org/apache/poi/hslf/record/FontEntityAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfo.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfoPredefined.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java
src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java
src/scratchpad/src/org/apache/poi/hwpf/HWPFOldDocument.java
src/scratchpad/src/org/apache/poi/hwpf/model/OldFfn.java
src/scratchpad/testcases/org/apache/poi/hslf/model/TestPPFont.java
src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java
src/scratchpad/testcases/org/apache/poi/hslf/record/TestFontCollection.java
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java
src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java
src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestHWPFOldDocument.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 (file)
index 0000000..aeeca92
--- /dev/null
@@ -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 <code>null</code> if there is no matching charset or
+     *         if the charset is a &quot;default&quot;
+     */
+    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 (file)
index 0000000..8faa788
--- /dev/null
@@ -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 (file)
index 0000000..b1c2945
--- /dev/null
@@ -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 <a href="https://blogs.msdn.microsoft.com/officeinteroperability/2013/04/22/office-open-xml-themes-schemes-and-fonts/">Office Open XML Themes, Schemes, and Fonts</a>
+ */
+public enum FontGroup {
+    /** type for latin charset (default) - also used for unicode fonts like MS Arial Unicode */
+    LATIN,
+    /** type for east asian charsets - usually set as fallback for the latin font, e.g. something like MS Gothic or MS Mincho */
+    EAST_ASIAN,
+    /** type for symbol fonts */
+    SYMBOL,
+    /** type for complex scripts - see https://msdn.microsoft.com/en-us/library/windows/desktop/dd317698 */
+    COMPLEX_SCRIPT
+    ;
+
+
+    public static class FontGroupRange {
+        private int len;
+        private FontGroup fontGroup;
+        public int getLength() {
+            return len;
+        }
+        public FontGroup getFontGroup( ) {
+            return fontGroup;
+        }
+    }
+
+    private static class Range {
+        int upper;
+        FontGroup fontGroup;
+        Range(int upper, FontGroup fontGroup) {
+            this.upper = upper;
+            this.fontGroup = fontGroup;
+        }
+    }
+
+    private static NavigableMap<Integer,Range> UCS_RANGES;
+
+    static {
+        UCS_RANGES = new TreeMap<Integer,Range>();
+        UCS_RANGES.put(0x0000,  new Range(0x007F, LATIN));
+        UCS_RANGES.put(0x0080,  new Range(0x00A6, LATIN));
+        UCS_RANGES.put(0x00A9,  new Range(0x00AF, LATIN));
+        UCS_RANGES.put(0x00B2,  new Range(0x00B3, LATIN));
+        UCS_RANGES.put(0x00B5,  new Range(0x00D6, LATIN));
+        UCS_RANGES.put(0x00D8,  new Range(0x00F6, LATIN));
+        UCS_RANGES.put(0x00F8,  new Range(0x058F, LATIN));
+        UCS_RANGES.put(0x0590,  new Range(0x074F, COMPLEX_SCRIPT));
+        UCS_RANGES.put(0x0780,  new Range(0x07BF, COMPLEX_SCRIPT));
+        UCS_RANGES.put(0x0900,  new Range(0x109F, COMPLEX_SCRIPT));
+        UCS_RANGES.put(0x10A0,  new Range(0x10FF, LATIN));
+        UCS_RANGES.put(0x1200,  new Range(0x137F, LATIN));
+        UCS_RANGES.put(0x13A0,  new Range(0x177F, LATIN));
+        UCS_RANGES.put(0x1D00,  new Range(0x1D7F, LATIN));
+        UCS_RANGES.put(0x1E00,  new Range(0x1FFF, LATIN));
+        UCS_RANGES.put(0x1780,  new Range(0x18AF, COMPLEX_SCRIPT));
+        UCS_RANGES.put(0x2000,  new Range(0x200B, LATIN));
+        UCS_RANGES.put(0x200C,  new Range(0x200F, COMPLEX_SCRIPT));
+        // For the quote characters in the range U+2018 - U+201E, use the East Asian font
+        // if the text has one of the following language identifiers:
+        // ii-CN, ja-JP, ko-KR, zh-CN,zh-HK, zh-MO, zh-SG, zh-TW
+        UCS_RANGES.put(0x2010,  new Range(0x2029, LATIN));
+        UCS_RANGES.put(0x202A,  new Range(0x202F, COMPLEX_SCRIPT));
+        UCS_RANGES.put(0x2030,  new Range(0x2046, LATIN));
+        UCS_RANGES.put(0x204A,  new Range(0x245F, LATIN));
+        UCS_RANGES.put(0x2670,  new Range(0x2671, COMPLEX_SCRIPT));
+        UCS_RANGES.put(0x27C0,  new Range(0x2BFF, LATIN));
+        UCS_RANGES.put(0x3099,  new Range(0x309A, EAST_ASIAN));
+        UCS_RANGES.put(0xD835,  new Range(0xD835, LATIN));
+        UCS_RANGES.put(0xF000,  new Range(0xF0FF, SYMBOL));
+        UCS_RANGES.put(0xFB00,  new Range(0xFB17, LATIN));
+        UCS_RANGES.put(0xFB1D,  new Range(0xFB4F, COMPLEX_SCRIPT));
+        UCS_RANGES.put(0xFE50,  new Range(0xFE6F, LATIN));
+        // All others EAST_ASIAN
+    };
+
+
+    /**
+     * Try to guess the font group based on the codepoint
+     *
+     * @param runText the text which font groups are to be analyzed
+     * @return the FontGroup
+     */
+    public static List<FontGroupRange> getFontGroupRanges(String runText) {
+        List<FontGroupRange> ttrList = new ArrayList<FontGroupRange>();
+        FontGroupRange ttrLast = null;
+        final int rlen = (runText != null) ? runText.length() : 0;
+        for(int cp, i = 0, charCount; i < rlen; i += charCount) {
+            cp = runText.codePointAt(i);
+            charCount = Character.charCount(cp);
+
+            // don't switch the font group for a few default characters supposedly available in all fonts
+            FontGroup tt;
+            if (ttrLast != null && " \n\r".indexOf(cp) > -1) {
+                tt = ttrLast.fontGroup;
+            } else {
+                tt = lookup(cp);
+            }
+
+            if (ttrLast == null || ttrLast.fontGroup != tt) {
+                ttrLast = new FontGroupRange();
+                ttrLast.fontGroup = tt;
+                ttrList.add(ttrLast);
+            }
+            ttrLast.len += charCount;
+        }
+        return ttrList;
+    }
+
+    public static FontGroup getFontGroupFirst(String runText) {
+        return (runText == null || runText.isEmpty()) ? LATIN : lookup(runText.codePointAt(0));
+    }
+
+    private static FontGroup lookup(int codepoint) {
+        // Do a lookup for a match in UCS_RANGES
+        Map.Entry<Integer,Range> entry = UCS_RANGES.floorEntry(codepoint);
+        Range range = (entry != null) ? entry.getValue() : null;
+        return (range != null && codepoint <= range.upper) ? range.fontGroup : EAST_ASIAN;
+    }
+}
\ No newline at end of file
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 (file)
index 0000000..ecb5a69
--- /dev/null
@@ -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.<p>
+ *
+ * If an implementation doesn't provide a property, the getter will return {@code null} -
+ * if the value is unset, a default value will be returned.<p>
+ *
+ * Setting a unsupported property results in an {@link UnsupportedOperationException}.
+ *
+ * @since POI 3.17-beta2
+ *
+ * @see <a href="https://msdn.microsoft.com/en-us/library/dd145037.aspx">LOGFONT structure</a>
+ */
+public interface FontInfo {
+
+    /**
+     * Get the index within the collection of Font objects
+     * @return unique index number of the underlying record this Font represents
+     *   (probably you don't care unless you're comparing which one is which)
+     */
+    Integer getIndex();
+
+    /**
+     * Sets the index within the collection of Font objects
+     *
+     * @param index the index within the collection of Font objects
+     *
+     * @throws UnsupportedOperationException if unsupported
+     */
+    void setIndex(int index);
+    
+    
+    /**
+     * @return the full name of the font, i.e. font family + type face
+     */
+    String getTypeface();
+
+    /**
+     * Sets the font name
+     *
+     * @param typeface the full name of the font, when {@code null} removes the font definition -
+     *    removal is implementation specific
+     */
+    void setTypeface(String typeface);
+
+    /**
+     * @return the font charset
+     */
+    FontCharset getCharset();
+
+    /**
+     * Sets the charset
+     *
+     * @param charset the charset
+     */
+    void setCharset(FontCharset charset);
+
+    /**
+     * @return the family class
+     */
+    FontFamily getFamily();
+
+    /**
+     * Sets the font family class
+     *
+     * @param family the font family class
+     */
+    void setFamily(FontFamily family);
+
+    /**
+     * @return the font pitch or {@code null} if unsupported
+     */
+    FontPitch getPitch();
+
+    /**
+     * Set the font pitch
+     *
+     * @param pitch the font pitch
+     *
+     * @throws UnsupportedOperationException if unsupported
+     */
+    void setPitch(FontPitch pitch);
+}
\ No newline at end of file
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 (file)
index 0000000..78c6533
--- /dev/null
@@ -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 <a href="https://msdn.microsoft.com/en-us/library/dd145037.aspx">LOGFONT structure</a>
+     */
+    public static byte getNativeId(FontPitch pitch, FontFamily family) {
+        return (byte)(pitch.getNativeId() | (family.getFlag() << 4));
+    }
+
+    /**
+     * Get FontPitch from native id
+     */
+    public static FontPitch valueOfPitchFamily(byte pitchAndFamily) {
+        return valueOf(pitchAndFamily & 0x3);
+    }
+}
+
index 53b2bcba7830764d471356aa90fd8e1ab6d92a77..376a8daab828356efba6a2b5e437e4b139609b75 100644 (file)
@@ -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 (file)
index 0000000..dc7afb4
--- /dev/null
@@ -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.");
+    }
+}
index 5b74f8400c8b67eeabbf55fc9457fe1ddc1bf732..0c16dd994ad49ffa1052d1e4bd9cf6972f169e9d 100644 (file)
 
 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.<p>
+     * 
+     * Currently this only maps for wingdings font (into unicode private use area)
+     *
+     * @param graphics the graphics context to request additional rendering hints
+     * @param fontInfo the font info object corresponding to the text run font
+     * @param text the raw text
+     * 
+     * @return String with mapped codepoints
+     *
+     * @see <a href="http://stackoverflow.com/questions/8692095">Drawing exotic fonts in a java applet</a>
+     * @see StringUtil#mapMsCodepointString(String)
+     */
+    String mapFontCharset(Graphics2D graphics, FontInfo fontInfo, String text);
+
+    /**
+     * Create an AWT font object with the given attributes
+     *
+     * @param graphics the graphics context to request additional rendering hints
+     * @param fontInfo the font info object corresponding to the text run font
+     * @param size the font size in points
+     * @param bold {@code true} if the font is bold
+     * @param italic {@code true} if the font is italic
+     * 
+     * @return the AWT font object
+     */
+    Font createAWTFont(Graphics2D graphics, FontInfo fontInfo, double size, boolean bold, boolean italic);
 }
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 (file)
index 0000000..c439fc9
--- /dev/null
@@ -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<chrs.length; i++) {
+                // only change valid chars
+                if ((0x20 <= chrs[i] && chrs[i] <= 0x7f) ||
+                    (0xa0 <= chrs[i] && chrs[i] <= 0xff)) {
+                    chrs[i] |= 0xf000;
+                    changed = true;
+                }
+            }
+
+            if (changed) {
+                attStr = new String(chrs);
+            }
+        }
+        return attStr;
+    }
+
+    @Override
+    public Font createAWTFont(Graphics2D graphics, FontInfo fontInfo, double fontSize, boolean bold, boolean italic) {
+        int style = (bold ? Font.BOLD : 0) | (italic ? Font.ITALIC : 0);
+        Font font = new Font(fontInfo.getTypeface(), style, 12);
+        if (Font.DIALOG.equals(font.getFamily())) {
+            // SansSerif is a better choice than Dialog
+            font = new Font(Font.SANS_SERIF, style, 12);
+        }
+        return font.deriveFont((float)fontSize);
+    }
+
+    private FontInfo getFontWithFallback(Graphics2D graphics, DrawableHint hint, FontInfo fontInfo) {
+        @SuppressWarnings("unchecked")
+        Map<String,String> fontMap = (Map<String,String>)graphics.getRenderingHint(hint);
+        if (fontMap == null) {
+            return fontInfo;
+        }
+        
+        String f = (fontInfo != null) ? fontInfo.getTypeface() : null;
+        String mappedTypeface = null;
+        if (fontMap.containsKey(f)) {
+            mappedTypeface = fontMap.get(f);
+        } else if (fontMap.containsKey("*")) {
+            mappedTypeface = fontMap.get("*");
+        }
+
+        return (mappedTypeface != null) ? new DrawFontInfo(mappedTypeface) : fontInfo;
+    }
+}
index 0eceb93643c3844d7d87a524fbf686afc45bd9eb..898ac51bd419f43b7275a73c352391e96307e6c4 100644 (file)
@@ -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;
     }
     
     /**
index 10d4edfd6283f29ebe6c9e541bf5090e7db06e84..7f708d1249d4b7531823b4e4737d4b9bd0a339c6 100644 (file)
@@ -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);
+
+
+        DrawFontManager dfm = DrawFactory.getInstance(graphics).getFontManager(graphics);
+        // TODO: check font group defaulting to Symbol
+        buFont = dfm.getMappedFont(graphics, buFont);
 
-        AttributedString str = new AttributedString(mapFontCharset(buCharacter,buFont));
+        AttributedString str = new AttributedString(dfm.mapFontCharset(graphics,buFont,buCharacter));
         str.addAttribute(TextAttribute.FOREGROUND, fgPaint);
-        str.addAttribute(TextAttribute.FAMILY, buFont);
+        str.addAttribute(TextAttribute.FAMILY, buFont.getTypeface());
         str.addAttribute(TextAttribute.SIZE, fontSize);
 
         TextLayout layout = new TextLayout(str.getIterator(), graphics.getFontRenderContext());
@@ -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<String,String> fontMap = (Map<String,String>)graphics.getRenderingHint(Drawable.FONT_MAP);
-        @SuppressWarnings("unchecked")
-        Map<String,String> fallbackMap = (Map<String,String>)graphics.getRenderingHint(Drawable.FONT_FALLBACK);
+        DrawFontManager dfm = DrawFactory.getInstance(graphics).getFontManager(graphics);
+        assert(dfm != null);
 
         for (TextRun run : paragraph){
             String runText = getRenderableText(graphics, run);
@@ -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,94 +640,97 @@ public class DrawTextParagraph implements Drawable {
         return string;
     }
 
-    private String getFontWithFallback(Map<String, String> fontMap, String mappedFont) {
-        if (fontMap != null) {
-            if (fontMap.containsKey(mappedFont)) {
-                mappedFont = fontMap.get(mappedFont);
-            } else if (fontMap.containsKey("*")) {
-                mappedFont = fontMap.get("*");
-            }
-        }
-        return mappedFont;
-    }
-
-    /**
-     * @return {@code true} if the HSLF implementation is used
-     */
-    protected boolean isHSLF() {
-        return DrawShape.isHSLF(paragraph.getParentShape());
-    }
-
     /**
-     * Map text charset depending on font family.
-     * Currently this only maps for wingdings font (into unicode private use area)
+     * Processing the glyphs is done in two steps.
+     * <li>determine the font group - a text run can have different font groups. Depending on the chars,
+     * the correct font group needs to be used
      *
-     * @param text the raw text
-     * @param fontFamily the font family
-     * @return AttributedString with mapped codepoints
+     * @param graphics
+     * @param dfm
+     * @param attList
+     * @param beginIndex
+     * @param run
+     * @param runText
      *
-     * @see <a href="http://stackoverflow.com/questions/8692095">Drawing exotic fonts in a java applet</a>
-     * @see StringUtil#mapMsCodepointString(String)
+     * @see <a href="https://blogs.msdn.microsoft.com/officeinteroperability/2013/04/22/office-open-xml-themes-schemes-and-fonts/">Office Open XML Themes, Schemes, and Fonts</a>
      */
-    protected String mapFontCharset(String text, String fontFamily) {
-        // TODO: find a real charset mapping solution instead of hard coding for Wingdings
-        String attStr = text;
-        if ("Wingdings".equalsIgnoreCase(fontFamily)) {
-            // wingdings doesn't contain high-surrogates, so chars are ok
-            boolean changed = false;
-            char chrs[] = attStr.toCharArray();
-            for (int i=0; i<chrs.length; i++) {
-                // only change valid chars
-                if ((0x20 <= chrs[i] && chrs[i] <= 0x7f) ||
-                    (0xa0 <= chrs[i] && chrs[i] <= 0xff)) {
-                    chrs[i] |= 0xf000;
-                    changed = true;
-                }
+    private void processGlyphs(Graphics2D graphics, DrawFontManager dfm, List<AttributedStringData> attList, final int beginIndex, TextRun run, String runText) {
+        // determine font group ranges of the textrun to focus the fallback handling only on that font group
+        List<FontGroupRange> ttrList = FontGroup.getFontGroupRanges(runText);
+        int rangeBegin = 0;
+        for (FontGroupRange ttr : ttrList) {
+            FontInfo fiRun = run.getFontInfo(ttr.getFontGroup());
+            if (fiRun == null) {
+                // if the font group specific font wasn't defined, fallback to LATIN
+                fiRun = run.getFontInfo(FontGroup.LATIN);
+            }
+            FontInfo fiMapped = dfm.getMappedFont(graphics, fiRun);
+            FontInfo fiFallback = dfm.getFallbackFont(graphics, fiRun);
+            assert(fiFallback != null);
+            if (fiMapped == null) {
+                fiMapped = dfm.getMappedFont(graphics, new DrawFontInfo(paragraph.getDefaultFontFamily()));
+            }
+            if (fiMapped == null) {
+                fiMapped = fiFallback;
             }
 
-            if (changed) {
-                attStr = new String(chrs);
+            Font fontMapped = dfm.createAWTFont(graphics, fiMapped, 10, run.isBold(), run.isItalic());
+            Font fontFallback = dfm.createAWTFont(graphics, fiFallback, 10, run.isBold(), run.isItalic());
+
+            // check for unsupported characters and add a fallback font for these
+            final int rangeLen = ttr.getLength();
+            int partEnd = rangeBegin;
+            while (partEnd<rangeBegin+rangeLen) {
+                // start with the assumption that the font is able to display the chars
+                int partBegin = partEnd;
+                partEnd = nextPart(fontMapped, runText, partBegin, rangeBegin+rangeLen, true);
+
+                // Now we have 3 cases:
+                // (a) the first part couldn't be displayed,
+                // (b) only part of the text run could be displayed
+                // (c) or all chars can be displayed (default)
+
+                if (partBegin < partEnd) {
+                    // handle (b) and (c)
+                    attList.add(new AttributedStringData(TextAttribute.FAMILY, fontMapped.getFontName(Locale.ROOT), beginIndex+partBegin, beginIndex+partEnd));
+                    if (LOG.check(POILogger.DEBUG)) {
+                        LOG.log(POILogger.DEBUG, "mapped: ",fontMapped.getFontName(Locale.ROOT)," ",(beginIndex+partBegin)," ",(beginIndex+partEnd)," - ",runText.substring(beginIndex+partBegin, beginIndex+partEnd));
+                    }
+                }
+
+                // fallback for unsupported glyphs
+                partBegin = partEnd;
+                partEnd = nextPart(fontMapped, runText, partBegin, rangeBegin+rangeLen, false);
+                
+                if (partBegin < partEnd) {
+                    // handle (a) and (b)
+                    attList.add(new AttributedStringData(TextAttribute.FAMILY, fontFallback.getFontName(Locale.ROOT), beginIndex+partBegin, beginIndex+partEnd));
+                    if (LOG.check(POILogger.DEBUG)) {
+                        LOG.log(POILogger.DEBUG, "fallback: ",fontFallback.getFontName(Locale.ROOT)," ",(beginIndex+partBegin)," ",(beginIndex+partEnd)," - ",runText.substring(beginIndex+partBegin, beginIndex+partEnd));
+                    }
+                }
             }
+            
+            rangeBegin += rangeLen;
         }
-        return attStr;
     }
     
-    /**
-     * Indicates whether or not this {@code Font} can display the characters in the specified {@code text}
-     * starting at {@code start} and ending at {@code limit}.<p>
-     * 
-     * This is a workaround for the Java 6 implementation of {@link Font#canDisplayUpTo(char[], int, int)}
-     *
-     * @param font the font to inspect
-     * @param text the specified array of {@code char} values
-     * @param start the specified starting offset (in
-     *              {@code char}s) into the specified array of
-     *              {@code char} values
-     * @param limit the specified ending offset (in
-     *              {@code char}s) into the specified array of
-     *              {@code char} values
-     * @return an offset into {@code text} that points
-     *          to the first character in {@code text} that this
-     *          {@code Font} cannot display; or {@code -1} if
-     *          this {@code Font} can display all characters in
-     *          {@code text}.
-     * 
-     * @see <a href="https://bugs.openjdk.java.net/browse/JDK-6623219">Font.canDisplayUpTo does not work with supplementary characters</a>
-     */
-    protected static int canDisplayUpTo(Font font, char[] text, int start, int limit) {
-        for (int i = start; i < limit; i++) {
-            char c = text[i];
-            if (font.canDisplay(c)) {
-                continue;
-            }
-            if (!Character.isHighSurrogate(c)) {
-                return i;
-            }
-            if (!font.canDisplay(Character.codePointAt(text, i, limit))) {
-                return i;
+    private static int nextPart(Font fontMapped, String runText, int beginPart, int endPart, boolean isDisplayed) {
+        int rIdx = beginPart;
+        while (rIdx < endPart) {
+            int codepoint = runText.codePointAt(rIdx);
+            if (fontMapped.canDisplay(codepoint) != isDisplayed) {
+                break;
             }
-            i++;
+            rIdx += Character.charCount(codepoint);
         }
-        return -1;
+        return rIdx;
+    }
+
+    /**
+     * @return {@code true} if the HSLF implementation is used
+     */
+    protected boolean isHSLF() {
+        return DrawShape.isHSLF(paragraph.getParentShape());
     }
 }
index 32b9c9933d08264fa54d9c420453a04d74f0ed06..394166071cc36751287c761865cbffa2ee36c509 100644 (file)
@@ -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 <code>null</code> unsets the Typeface attrubute from the underlying xml.
+     *      The value of {@code null} removes the run specific font setting, so the default setting is activated again.
      */
     void setFontFamily(String typeface);
 
+    /**
+     * Specifies the typeface, or name of the font that is to be used for this text run -
+     * convenience method for calling {@link #setFontInfo(FontInfo, FontGroup)} with just a font name
+     *
+     * @param typeface  the font to apply to this text run.
+     *      The value of {@code null} removes the run specific font setting, so the default setting is activated again.
+     * @param fontGroup the font group, i.e. the range of glpyhs to be covered.
+     *    if {@code null}, the font group matching the first character will be returned 
+     */
+    void setFontFamily(String typeface, FontGroup fontGroup);
+
+    /**
+     * Get the font info for the given font group
+     * 
+     * @param fontGroup the font group, i.e. the range of glpyhs to be covered.
+     *    if {@code null}, the font group matching the first character will be returned 
+     * @return  font info or {@code null} if not set
+     * 
+     * @since POI 3.17-beta2
+     */
+    FontInfo getFontInfo(FontGroup fontGroup);
+
+    /**
+     * Specifies the font to be used for this text run.
+     *
+     * @param fontInfo the font to apply to this text run.
+     *      The value of {@code null} removes the run specific font setting, so the default setting is activated again.
+     * @param fontGroup the font group, i.e. the range of glpyhs to be covered. defaults to latin, if {@code null}.
+     * 
+     * @since POI 3.17-beta2
+     */
+    void setFontInfo(FontInfo fontInfo, FontGroup fontGroup);
+    
     /**
      * @return true, if text is bold
      */
index 90e4038b31203ccf1b3ab8f372f6460c57857f07..64953675f7ed8f091eb011f4f2a1afb4cd710142 100644 (file)
 
 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),
index 353bbbf2b6a7967ff6a2580f00c8d8d35b3c3316..d5d57312f28bd219ca98f66015d0e953e688029c 100644 (file)
@@ -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) {
+        FontGroup fg = FontGroup.getFontGroupFirst(getRawText());
+        new XSLFFontInfo(fg).setTypeface(typeface);
     }
 
-    public void setFontFamily(String typeface, byte charset, byte pictAndFamily, boolean isSymbol){
-        CTTextCharacterProperties rPr = getRPr(true);
+    @Override
+    public void setFontFamily(String typeface, FontGroup fontGroup) {
+        new XSLFFontInfo(fontGroup).setTypeface(typeface);
+    }
 
-        if(typeface == null){
-            if(rPr.isSetLatin()) {
-                rPr.unsetLatin();
-            }
-            if(rPr.isSetCs()) {
-                rPr.unsetCs();
-            }
-            if(rPr.isSetSym()) {
-                rPr.unsetSym();
-            }
-        } else {
-            if(isSymbol){
-                CTTextFont font = rPr.isSetSym() ? rPr.getSym() : rPr.addNewSym();
-                font.setTypeface(typeface);
-            } else {
-                CTTextFont latin = rPr.isSetLatin() ? rPr.getLatin() : rPr.addNewLatin();
-                latin.setTypeface(typeface);
-                if(charset != -1) {
-                    latin.setCharset(charset);
-                }
-                if(pictAndFamily != -1) {
-                    latin.setPitchFamily(pictAndFamily);
-                }
-            }
-        }
+    @Override
+    public void setFontInfo(FontInfo fontInfo, FontGroup fontGroup) {
+        new XSLFFontInfo(fontGroup).copyFrom(fontInfo);
     }
 
     @Override
-    public String getFontFamily(){
-        final XSLFTheme theme = _p.getParentShape().getSheet().getTheme();
+    public String getFontFamily() {
+        FontGroup fg = FontGroup.getFontGroupFirst(getRawText());
+        return new XSLFFontInfo(fg).getTypeface();
+    }
 
-        CharacterPropertyFetcher<String> visitor = new CharacterPropertyFetcher<String>(_p.getIndentLevel()){
-            @Override
-            public boolean fetch(CTTextCharacterProperties props){
-                if (props != null) {
-                    CTTextFont font = props.getLatin();
-                    if (font != null) {
-                        String typeface = font.getTypeface();
-                        if("+mj-lt".equals(typeface)) {
-                            typeface = theme.getMajorFont();
-                        } else if ("+mn-lt".equals(typeface)){
-                            typeface = theme.getMinorFont();
-                        }
-                        setValue(typeface);
-                        return true;
-                    }
-                }
-                return false;
-            }
-        };
-        fetchCharacterProperty(visitor);
+    @Override
+    public String getFontFamily(FontGroup fontGroup) {
+        return new XSLFFontInfo(fontGroup).getTypeface();
+    }
 
-        return  visitor.getValue();
+    @Override
+    public FontInfo getFontInfo(FontGroup fontGroup) {
+        XSLFFontInfo fontInfo = new XSLFFontInfo(fontGroup);
+        return (fontInfo.getTypeface() != null) ? fontInfo : null;
     }
 
     @Override
     public byte getPitchAndFamily(){
-        // final XSLFTheme theme = _p.getParentShape().getSheet().getTheme();
-
-        CharacterPropertyFetcher<Byte> visitor = new CharacterPropertyFetcher<Byte>(_p.getIndentLevel()){
-            @Override
-            public boolean fetch(CTTextCharacterProperties props){
-                if (props != null) {
-                    CTTextFont font = props.getLatin();
-                    if (font != null) {
-                        setValue(font.getPitchFamily());
-                        return true;
-                    }
-                }
-                return false;
-            }
-        };
-        fetchCharacterProperty(visitor);
-
-        return  visitor.getValue() == null ? 0 : visitor.getValue();
+        FontGroup fg = FontGroup.getFontGroupFirst(getRawText());
+        XSLFFontInfo fontInfo = new XSLFFontInfo(fg);
+        FontPitch pitch = fontInfo.getPitch();
+        if (pitch == null) {
+            pitch = FontPitch.VARIABLE;
+        }
+        FontFamily family = fontInfo.getFamily();
+        if (family == null) {
+            family = FontFamily.FF_SWISS;
+        }
+        return FontPitch.getNativeId(pitch, family);
     }
 
     @Override
@@ -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<CTTextFont> visitor = new CharacterPropertyFetcher<CTTextFont>(_p.getIndentLevel()){
+                @Override
+                public boolean fetch(CTTextCharacterProperties props){
+                    CTTextFont font = getCTTextFont(props, false);
+                    if (font == null) {
+                        return false;
+                    }
+                    setValue(font);
+                    return true;
+                }
+            };
+            fetchCharacterProperty(visitor);
+
+            return  visitor.getValue();
+        }
+
+        private CTTextFont getCTTextFont(CTTextCharacterProperties props, boolean create) {
+            if (props == null) {
+                return null;
+            }
+
+            CTTextFont font;
+            switch (fontGroup) {
+            default:
+            case LATIN:
+                font = props.getLatin();
+                if (font == null && create) {
+                    font = props.addNewLatin();
+                }
+                break;
+            case EAST_ASIAN:
+                font = props.getEa();
+                if (font == null && create) {
+                    font = props.addNewEa();
+                }
+                break;
+            case COMPLEX_SCRIPT:
+                font = props.getCs();
+                if (font == null && create) {
+                    font = props.addNewCs();
+                }
+                break;
+            case SYMBOL:
+                font = props.getSym();
+                if (font == null && create) {
+                    font = props.addNewSym();
+                }
+                break;
+            }
+
+            if (font == null) {
+                return null;
+            }
+
+            String typeface = font.isSetTypeface() ? font.getTypeface() : "";
+            if (typeface.startsWith("+mj-") || typeface.startsWith("+mn-")) {
+                //  "+mj-lt".equals(typeface) || "+mn-lt".equals(typeface)
+                final XSLFTheme theme = _p.getParentShape().getSheet().getTheme();
+                CTFontScheme fontTheme = theme.getXmlObject().getThemeElements().getFontScheme();
+                CTFontCollection coll = typeface.startsWith("+mj-")
+                    ? fontTheme.getMajorFont() : fontTheme.getMinorFont();
+                // TODO: handle LCID codes
+                // see https://blogs.msdn.microsoft.com/officeinteroperability/2013/04/22/office-open-xml-themes-schemes-and-fonts/
+                String fgStr = typeface.substring(4);
+                if ("ea".equals(fgStr)) {
+                    font = coll.getEa();
+                } else if ("cs".equals(fgStr)) {
+                    font = coll.getCs();
+                } else {
+                    font = coll.getLatin();
+                }
+                // SYMBOL is missing
+                
+                if (font != null || !font.isSetTypeface() || "".equals(font.getTypeface())) {
+                    font = coll.getLatin();
+                }
+            }
+
+            return font;
+        }
+    }
 }
index 411c1929e3909ae1c56de271242412d089b78996..8b5afc5b8aeb079bd379104790bb8a5a54d774f0 100644 (file)
@@ -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<String,String> fallbackMap = new HashMap<String,String>();
         fallbackMap.put("NoSuchFont", "Mona");
+        // in XSLF the fonts default to the theme fonts (Calibri), if the font group is not overridden
+        // see XSLFTextRun.XSLFTextInfo.getCTTextFont
+        fallbackMap.put("Calibri", "Mona");
         graphics.setRenderingHint(Drawable.FONT_FALLBACK, fallbackMap);
         graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
-        
+
         DrawFactory.getInstance(graphics).fixFonts(graphics);
-        
+
         tb.resizeToFitText(graphics);
         graphics.dispose();
-        
-        return tb;
+
+        Rectangle2D anc = tb.getAnchor();
+        // ignore font metrics differences on windows / linux (... hopefully ...)
+        int tbHeight = (int)anc.getHeight();
+        boolean found = Arrays.binarySearch(expected_sizes, tbHeight) > -1;
+        assertTrue(tbHeight+" wasn't within the expected sizes: "+Arrays.toString(expected_sizes), found);
     }
-    
-    private void setFont(TextBox<?,?> tb, String fontFamily) {
+
+    private void setFont(TextBox<?,?> tb, String fontFamily, FontGroup fontGroup) {
         // TODO: set east asian font family - MS Office uses "MS Mincho" or "MS Gothic" as a fallback
         // see https://stackoverflow.com/questions/26063828 for good explanation about the font metrics
         // differences on different environments
         for (TextParagraph<?,?,?> p : tb.getTextParagraphs()) {
             for (TextRun r : p.getTextRuns()) {
-                r.setFontFamily(fontFamily);
-                if (r instanceof XSLFTextRun) {
-                    // TODO: provide API for HSLF
-                    XSLFTextRun xr = (XSLFTextRun)r;
-                    CTRegularTextRun tr = (CTRegularTextRun)xr.getXmlObject();
-                    tr.getRPr().addNewEa().setTypeface(fontFamily);
-     
-                }
+                r.setFontFamily(fontFamily, fontGroup);
             }
         }
     }
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 (file)
index 7de68dc..0000000
+++ /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.
- * <p>
- * In PowerPoint Font is a shared resource and can be shared among text object in the presentation.
- * </p>
- * Some commonly used fonts are predefined in static constants.
- *
- * @author Yegor Kozlov
- */
-public final class PPFont {
-    /**
-     * ANSI character set
-     */
-    public final static byte ANSI_CHARSET = 0;
-
-    /**
-     * Default character set.
-     */
-    public final static byte DEFAULT_CHARSET = 1;
-
-    /**
-     * Symbol character set
-     */
-    public final static byte SYMBOL_CHARSET = 2;
-
-
-    /**
-     * Constants for the pitch and family of the font.
-     * The two low-order bits specify the pitch of the font and can be one of the following values
-     */
-    public final static byte DEFAULT_PITCH  = 0;
-    public final static byte FIXED_PITCH    = 1;
-    public final static byte VARIABLE_PITCH = 2;
-
-    /**
-     * Don't care or don't know.
-     */
-    public final static byte FF_DONTCARE    = 0;
-    /**
-     * Fonts with variable stroke width (proportional) and with serifs. Times New Roman is an example.
-     */
-    public final static byte FF_ROMAN       = 16;
-    /**
-     * Fonts with variable stroke width (proportional) and without serifs. Arial is an example.
-     */
-    public final static byte FF_SWISS       = 32;
-    /**
-     * Fonts designed to look like handwriting. Script and Cursive are examples.
-     */
-    public final static byte FF_SCRIPT      = 64;
-    /**
-     * Fonts with constant stroke width (monospace), with or without serifs.
-     * Monospace fonts are usually modern. CourierNew is an example
-     */
-    public final static byte FF_MODERN      = 48;
-    /**
-     * Novelty fonts. Old English is an example
-     */
-    public final static byte FF_DECORATIVE  = 80;
-
-
-    private int charset;
-    private int type;
-    private int flags;
-    private int pitch;
-    private String name;
-
-    /**
-     * Creates a new instance of PPFont
-     */
-    public PPFont(){
-
-    }
-
-    /**
-     * Creates a new instance of PPFont and initialize it from the supplied font atom
-     */
-    public PPFont(FontEntityAtom fontAtom){
-        name = fontAtom.getFontName();
-        charset = fontAtom.getCharSet();
-        type = fontAtom.getFontType();
-        flags = fontAtom.getFontFlags();
-        pitch = fontAtom.getPitchAndFamily();
-    }
-
-    /**
-     * set the name for the font (i.e. Arial)
-     *
-     * @param val  String representing the name of the font to use
-     */
-     public void setFontName(String val){
-        name = val;
-    }
-
-    /**
-     * get the name for the font (i.e. Arial)
-     *
-     * @return String representing the name of the font to use
-     */
-    public String getFontName(){
-        return name;
-    }
-
-    /**
-     * set the character set
-     *
-     * @param val - characterset
-     */
-    public void setCharSet(int val){
-        charset = val;
-    }
-
-    /**
-     * get the character set
-     *
-     * @return charset - characterset
-     */
-    public int getCharSet(){
-        return charset;
-    }
-
-    /**
-     * set the font flags
-     * Bit 1: If set, font is subsetted
-     *
-     * @param val - the font flags
-     */
-    public void setFontFlags(int val){
-        flags = val;
-    }
-
-    /**
-     * get the character set
-     * Bit 1: If set, font is subsetted
-     *
-     * @return the font flags
-     */
-    public int getFontFlags(){
-        return flags;
-    }
-
-    /**
-     * set the font type
-     * <p>
-     * Bit 1: Raster Font
-     * Bit 2: Device Font
-     * Bit 3: TrueType Font
-     * </p>
-     *
-     * @param val - the font type
-     */
-    public void setFontType(int val){
-        type = val;
-    }
-
-    /**
-     * get the font type
-     * <p>
-     * Bit 1: Raster Font
-     * Bit 2: Device Font
-     * Bit 3: TrueType Font
-     * </p>
-     *
-     * @return the font type
-     */
-    public int getFontType(){
-        return type;
-    }
-
-    /**
-     * set lfPitchAndFamily
-     *
-     *
-     * @param val - Corresponds to the lfPitchAndFamily field of the Win32 API LOGFONT structure
-     */
-    public void setPitchAndFamily(int val){
-        pitch = val;
-    }
-
-    /**
-     * get lfPitchAndFamily
-     *
-     * @return corresponds to the lfPitchAndFamily field of the Win32 API LOGFONT structure
-     */
-    public int getPitchAndFamily(){
-        return pitch;
-    }
-
-    public static final PPFont ARIAL;
-    public static final PPFont TIMES_NEW_ROMAN ;
-    public static final PPFont COURIER_NEW;
-    public static final PPFont WINGDINGS;
-    static {
-        ARIAL = new PPFont();
-        ARIAL.setFontName("Arial");
-        ARIAL.setCharSet(ANSI_CHARSET);
-        ARIAL.setFontType(4);
-        ARIAL.setFontFlags(0);
-        ARIAL.setPitchAndFamily(VARIABLE_PITCH | FF_SWISS);
-
-        TIMES_NEW_ROMAN = new PPFont();
-        TIMES_NEW_ROMAN.setFontName("Times New Roman");
-        TIMES_NEW_ROMAN.setCharSet(ANSI_CHARSET);
-        TIMES_NEW_ROMAN.setFontType(4);
-        TIMES_NEW_ROMAN.setFontFlags(0);
-        TIMES_NEW_ROMAN.setPitchAndFamily(VARIABLE_PITCH | FF_ROMAN);
-
-        COURIER_NEW = new PPFont();
-        COURIER_NEW.setFontName("Courier New");
-        COURIER_NEW.setCharSet(ANSI_CHARSET);
-        COURIER_NEW.setFontType(4);
-        COURIER_NEW.setFontFlags(0);
-        COURIER_NEW.setPitchAndFamily(FIXED_PITCH | FF_MODERN);
-
-        WINGDINGS = new PPFont();
-        WINGDINGS.setFontName("Wingdings");
-        WINGDINGS.setCharSet(SYMBOL_CHARSET);
-        WINGDINGS.setFontType(4);
-        WINGDINGS.setFontFlags(0);
-        WINGDINGS.setPitchAndFamily(VARIABLE_PITCH | FF_DONTCARE);
-    }
-}
index f7cd0370106934cf872987003d2bf2d86fd366c4..021d1c4a8490517c6b412c3fbdc020c6bdf98348 100644 (file)
@@ -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
index b90c698dd66116ddd89917890f55765d55e98bf7..6f138fa0839ce49d4d8aaa28628153121e267cec 100644 (file)
 
 package org.apache.poi.hslf.record;
 
-import org.apache.poi.hslf.model.PPFont;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.poi.common.usermodel.fonts.FontInfo;
+import org.apache.poi.hslf.usermodel.HSLFFontInfo;
+import org.apache.poi.hslf.usermodel.HSLFFontInfoPredefined;
 import org.apache.poi.util.POILogger;
 
-import java.io.*;
-import java.util.*;
-
 /**
- * <code>FontCollection</code> ia a container that holds information
+ * {@code FontCollection} ia a container that holds information
  * about all the fonts in the presentation.
- *
- * @author Yegor Kozlov
  */
 
 public final class FontCollection extends RecordContainer {
-    private List<String> fonts;
+    private final Map<String,HSLFFontInfo> fonts = new LinkedHashMap<String,HSLFFontInfo>();
     private byte[] _header;
 
        protected FontCollection(byte[] source, int start, int len) {
-               // Grab the header
                _header = new byte[8];
                System.arraycopy(source,start,_header,0,8);
 
                _children = Record.findChildRecords(source,start+8,len-8);
 
-               // Save font names into <code>List</code>
-               fonts = new ArrayList<String>();
-               for (int i = 0; i < _children.length; i++){
-                       if(_children[i] instanceof FontEntityAtom) {
-                   FontEntityAtom atom = (FontEntityAtom)_children[i];
-                   fonts.add(atom.getFontName());
+               for (Record r : _children){
+                       if(r instanceof FontEntityAtom) {
+                           HSLFFontInfo fi = new HSLFFontInfo((FontEntityAtom)r);
+                   fonts.put(fi.getTypeface(), fi);
                        } else {
-                               logger.log(POILogger.WARN, "Warning: FontCollection child wasn't a FontEntityAtom, was " + _children[i]);
+                               logger.log(POILogger.WARN, "Warning: FontCollection child wasn't a FontEntityAtom, was " + r.getClass().getSimpleName());
                        }
                }
        }
@@ -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;
-
-        return addFont(name, 0, 0, 4, PPFont.FF_SWISS | PPFont.VARIABLE_PITCH);
-    }
+    public HSLFFontInfo addFont(FontInfo fontInfo) {
+        HSLFFontInfo fi = getFontInfo(fontInfo.getTypeface());
+        if (fi != null) {
+            return fi;
+        }
 
-    public int addFont(String name, int charset, int flags, int type, int pitch) {
-        FontEntityAtom fnt = new FontEntityAtom();
-        fnt.setFontIndex(fonts.size() << 4);
-        fnt.setFontName(name);
-        fnt.setCharSet(charset);
-        fnt.setFontFlags(flags);
-        fnt.setFontType(type);
-        fnt.setPitchAndFamily(pitch);
-        fonts.add(name);
+        fi = new HSLFFontInfo(fontInfo);
+        fi.setIndex(fonts.size());
+        fonts.put(fi.getTypeface(), fi);
+        
+        FontEntityAtom fnt = fi.createRecord();
 
         // Append new child to the end
-               appendChildRecord(fnt);
+        appendChildRecord(fnt);
 
-        return fonts.size()-1; //the added font is the last in the list
+        // the added font is the last in the list
+        return fi;
+    }
+
+
+    /**
+     * Lookup a FontInfo object by its typeface
+     * 
+     * @param typeface the full font name
+     * 
+     * @return the HSLFFontInfo for the given name or {@code null} if not found
+     */
+    public HSLFFontInfo getFontInfo(String typeface) {
+        return fonts.get(typeface);
     }
 
     /**
-     * @return zero based index of the font in the collection or -1 if not found
+     * Lookup a FontInfo object by its internal font index
+     * 
+     * @param index the internal font index
+     * 
+     * @return the HSLFFontInfo for the given index or {@code null} if not found
      */
-    public int getFontIndex(String name) {
-        for (int i = 0; i < fonts.size(); i++) {
-            if(fonts.get(i).equals(name)){
-                //if the font is already present return its index
-                return i;
+    public HSLFFontInfo getFontInfo(int index) {
+        for (HSLFFontInfo fi : fonts.values()) {
+            if (fi.getIndex() == index) {
+                return fi;
             }
         }
-        return -1;
+        return null;
     }
-
+    
+    /**
+     * @return the number of registered fonts
+     */
     public int getNumberOfFonts() {
         return fonts.size();
     }
-
-    /**
-        * Get the name of the font at the given ID, or null if there is
-        *  no font at that ID.
-        * @param id
-        */
-       public String getFontWithId(int id) {
-               if(id >= fonts.size()) {
-                       // No font with that id
-                       return null;
-               }
-               return fonts.get(id);
-       }
 }
index 80126fdb9fae8dffa090ef0120056f588324be8f..c0be885078fa2513664a559f6db639aa4e764b06 100644 (file)
@@ -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';
-               }
-
-               // Ensure it's not now too long
-               if(name.length() > 32) {
-                       throw new HSLFException("The length of the font name, including null termination, must not exceed 32 characters");
-               }
-
-               // Everything's happy, so save the name
+    public void setFontName(String name) {
+        // Ensure it's not now too long
+        int nameLen = name.length() + (name.endsWith("\u0000") ? 0 : 1);
+        if (nameLen > 32) {
+            throw new HSLFException("The length of the font name, including null termination, must not exceed 32 characters");
+        }
+
+        // Everything's happy, so save the name
         byte[] bytes = StringUtil.getToUnicodeLE(name);
         System.arraycopy(bytes, 0, _recdata, 0, bytes.length);
+        // null the remaining bytes
+        Arrays.fill(_recdata, 64-bytes.length, 64, (byte)0);
     }
 
     public void setFontIndex(int idx){
index 3ad7b086cc84c340522570a72f9be7ca1557d459..c3fc1f1d2705439eec8bcdda261747517b2781b5 100644 (file)
 
 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<Record> lst = new ArrayList<Record>();
         for(Record r : _children) {
-            if(r != ch) lst.add(r);
-            else rm = r;
+            if(r != ch) {
+                lst.add(r);
+            } else {
+                rm = r;
+            }
         }
         _children = lst.toArray(new Record[lst.size()]);
         return rm;
@@ -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 (file)
index 0000000..b56b553
--- /dev/null
@@ -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.<p>
+ * 
+ * In PowerPoint Font is a shared resource and can be shared among text object in the presentation.
+ * 
+ * @since POI 3.17-beta2
+ */
+public class HSLFFontInfo implements FontInfo {
+
+    public enum FontRenderType {
+        raster, device, truetype;
+    }
+    
+    /** A bit that specifies whether a subset of this font is embedded. */
+    private static final BitField FLAGS_EMBED_SUBSETTED      = BitFieldFactory.getInstance(0x01);
+    /** Bits that specifies whether the font is a raster,device or truetype font. */
+    private static final BitField FLAGS_RENDER_FONTTYPE      = BitFieldFactory.getInstance(0x07);
+    /** A bit that specifies whether font substitution logic is not applied for this font. */
+    private static final BitField FLAGS_NO_FONT_SUBSTITUTION = BitFieldFactory.getInstance(0x08);
+    
+    private int index = -1;
+    private String typeface = "undefined";
+    private FontCharset charset = FontCharset.ANSI;
+    private FontRenderType renderType = FontRenderType.truetype;
+    private FontFamily family = FontFamily.FF_SWISS;
+    private FontPitch pitch = FontPitch.VARIABLE;
+    private boolean isSubsetted = false;
+    private boolean isSubstitutable = true;
+
+    /**
+     * Creates a new instance of HSLFFontInfo with more or sensible defaults.<p>
+     * 
+     * If you don't use default fonts (see {@link HSLFFontInfoPredefined}) then the results
+     * of the font substitution will be better, if you also specify the other properties.
+     * 
+     * @param typeface the font name
+     */
+    public HSLFFontInfo(String typeface){
+        setTypeface(typeface);
+    }
+
+    /**
+     * Creates a new instance of HSLFFontInfo and initialize it from the supplied font atom
+     */
+    public HSLFFontInfo(FontEntityAtom fontAtom){
+        setIndex(fontAtom.getFontIndex());
+        setTypeface(fontAtom.getFontName());
+        setCharset(FontCharset.valueOf(fontAtom.getCharSet()));
+        // assumption: the render type is exclusive
+        switch (FLAGS_RENDER_FONTTYPE.getValue(fontAtom.getFontType())) {
+        case 1:
+            setRenderType(FontRenderType.raster);
+            break;
+        case 2:
+            setRenderType(FontRenderType.device);
+            break;
+        default:
+        case 4:
+            setRenderType(FontRenderType.truetype);
+            break;
+        }
+        
+        byte pitchAndFamily = (byte)fontAtom.getPitchAndFamily();
+        setPitch(FontPitch.valueOfPitchFamily(pitchAndFamily));
+        setFamily(FontFamily.valueOfPitchFamily(pitchAndFamily));
+        setEmbedSubsetted(FLAGS_EMBED_SUBSETTED.isSet(fontAtom.getFontFlags()));
+        setFontSubstitutable(!FLAGS_NO_FONT_SUBSTITUTION.isSet(fontAtom.getFontType()));
+    }
+
+    public HSLFFontInfo(FontInfo fontInfo) {
+        // don't copy font index on copy constructor - it depends on the FontCollection this record is in
+        setTypeface(fontInfo.getTypeface());
+        setCharset(fontInfo.getCharset());
+        setFamily(fontInfo.getFamily());
+        setPitch(fontInfo.getPitch());
+        if (fontInfo instanceof HSLFFontInfo) {
+            HSLFFontInfo hFontInfo = (HSLFFontInfo)fontInfo;
+            setRenderType(hFontInfo.getRenderType());
+            setEmbedSubsetted(hFontInfo.isEmbedSubsetted());
+            setFontSubstitutable(hFontInfo.isFontSubstitutable());
+        }
+    }
+    
+    @Override
+    public Integer getIndex() {
+        return index;
+    }
+
+    @Override
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+    @Override
+    public String getTypeface(){
+        return typeface;
+    }
+
+    @Override
+    public void setTypeface(String typeface){
+        if (typeface == null || "".equals(typeface)) {
+            throw new IllegalArgumentException("typeface can't be null nor empty");
+        }
+        this.typeface = typeface;
+    }
+
+    @Override
+    public void setCharset(FontCharset charset){
+        this.charset = (charset == null) ? FontCharset.ANSI : charset;
+    }
+
+    @Override
+    public FontCharset getCharset(){
+        return charset;
+    }
+    
+    @Override
+    public FontFamily getFamily() {
+        return family;
+    }
+
+    @Override
+    public void setFamily(FontFamily family) {
+        this.family = (family == null) ? FontFamily.FF_SWISS : family;
+    }
+    
+    @Override
+    public FontPitch getPitch() {
+        return pitch;
+    }
+
+    @Override
+    public void setPitch(FontPitch pitch) {
+        this.pitch = (pitch == null) ? FontPitch.VARIABLE : pitch;
+        
+    }
+
+    public FontRenderType getRenderType() {
+        return renderType;
+    }
+
+    public void setRenderType(FontRenderType renderType) {
+        this.renderType = (renderType == null) ? FontRenderType.truetype : renderType;
+    }
+
+    public boolean isEmbedSubsetted() {
+        return isSubsetted;
+    }
+
+    public void setEmbedSubsetted(boolean embedSubset) {
+        this.isSubsetted = embedSubset;
+    }
+
+    public boolean isFontSubstitutable() {
+        return this.isSubstitutable;
+    }
+
+    public void setFontSubstitutable(boolean isSubstitutable) {
+        this.isSubstitutable = isSubstitutable;
+    }
+    
+    public FontEntityAtom createRecord() {
+        FontEntityAtom fnt = new FontEntityAtom();
+        fnt.setFontIndex(getIndex() << 4);
+        fnt.setFontName(getTypeface());
+        fnt.setCharSet(getCharset().getNativeId());
+        fnt.setFontFlags((byte)(isEmbedSubsetted() ? 1 : 0));
+
+        int typeFlag;
+        switch (renderType) {
+        case device:
+            typeFlag = FLAGS_RENDER_FONTTYPE.setValue(0, 1);
+            break;
+        case raster:
+            typeFlag = FLAGS_RENDER_FONTTYPE.setValue(0, 2);
+            break;
+        default:
+        case truetype:
+            typeFlag = FLAGS_RENDER_FONTTYPE.setValue(0, 4);
+            break;
+        }
+        typeFlag = FLAGS_NO_FONT_SUBSTITUTION.setBoolean(typeFlag, isFontSubstitutable());
+        fnt.setFontType(typeFlag);
+        
+        fnt.setPitchAndFamily(FontPitch.getNativeId(pitch, family));
+        return fnt;
+    }
+}
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 (file)
index 0000000..1f016f9
--- /dev/null
@@ -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.");
+    }
+}
index 4cfa72d33b8c35d99dc0ee745f8ec12191ea790a..c79816de38674dcefb847cab91944990301bed99 100644 (file)
@@ -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 SlideShow<HSLFShape,HSLFTextParagrap
        /**
         * Add a font in this presentation
         *
-        * @param font
-        *            the font to add
-        * @return 0-based index of the font
+        * @param fontInfo the font to add
+        * @return the registered HSLFFontInfo - the font info object is unique based on the typeface
         */
-       public int addFont(PPFont font) {
-               FontCollection fonts = getDocumentRecord().getEnvironment().getFontCollection();
-               int idx = fonts.getFontIndex(font.getFontName());
-               if (idx == -1) {
-                       idx = fonts.addFont(font.getFontName(), font.getCharSet(), font.getFontFlags(), font
-                                       .getFontType(), font.getPitchAndFamily());
-               }
-               return idx;
+       public HSLFFontInfo addFont(FontInfo fontInfo) {
+               return getDocumentRecord().getEnvironment().getFontCollection().addFont(fontInfo);
        }
 
        /**
@@ -896,17 +915,8 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
         * @return of an instance of <code>PPFont</code> or <code>null</code> if not
         *         found
         */
-       public PPFont getFont(int idx) {
-               FontCollection fonts = getDocumentRecord().getEnvironment().getFontCollection();
-               for (Record ch : fonts.getChildRecords()) {
-                       if (ch instanceof FontEntityAtom) {
-                               FontEntityAtom atom = (FontEntityAtom) ch;
-                               if (atom.getFontIndex() == idx) {
-                                       return new PPFont(atom);
-                               }
-                       }
-               }
-               return null;
+       public HSLFFontInfo getFont(int idx) {
+           return getDocumentRecord().getEnvironment().getFontCollection().getFontInfo(idx);
        }
 
        /**
index 9fa73ac5dca5d1b2000e570f8bc792435a56101f..a3c75678beff8367007f41e287b98dd78edfd476 100644 (file)
@@ -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<HSLFShape,HSLFText
     public void setParagraphStyle(TextPropCollection paragraphStyle) {
         _paragraphStyle.copy(paragraphStyle);
     }
-    
+
     /**
      * Setting a master style reference
      *
@@ -376,11 +376,20 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
 
     @Override
     public String getDefaultFontFamily() {
-        String typeface = null;
+        FontInfo fontInfo = null;
         if (!_runs.isEmpty()) {
-            typeface = _runs.get(0).getFontFamily();
+            HSLFTextRun tr = _runs.get(0);
+            fontInfo = tr.getFontInfo(null);
+            // fallback to LATIN if the font for the font group wasn't defined
+            if (fontInfo == null) {
+                fontInfo = tr.getFontInfo(FontGroup.LATIN);
+            }
+        }
+        if (fontInfo == null) {
+            fontInfo = HSLFFontInfoPredefined.ARIAL;
         }
-        return (typeface != null) ? typeface : "Arial";
+        
+        return fontInfo.getTypeface();
     }
 
     @Override
@@ -657,10 +666,10 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
             return;
         }
 
-        FontCollection fc = getSheet().getSlideShow().getFontCollection();
-        int idx = fc.addFont(typeface);
+        HSLFFontInfo fi = new HSLFFontInfo(typeface);
+        fi = getSheet().getSlideShow().addFont(fi);
 
-        setParagraphTextPropVal("bullet.font", idx);
+        setParagraphTextPropVal("bullet.font", fi.getIndex());
         setFlag(ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, true);
     }
 
@@ -673,9 +682,9 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
         if (tp == null || !hasFont) {
             return getDefaultFontFamily();
         }
-        PPFont ppFont = getSheet().getSlideShow().getFont(tp.getValue());
+        HSLFFontInfo ppFont = getSheet().getSlideShow().getFont(tp.getValue());
         assert(ppFont != null);
-        return ppFont.getFontName();
+        return ppFont.getTypeface();
     }
 
     @Override
@@ -754,22 +763,14 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
         String propNames[] = propName.split(",");
         for (String pn : propNames) {
             TextProp prop = props.findByName(pn);
-            if (prop == null) {
-                continue;
-            }
-
-            // Font properties (maybe other too???) can have an index of -1
-            // so we check the master for this font index then
-            if (pn.contains("font") && prop.getValue() == -1) {
-                return getMasterPropVal(props, masterProps, pn);
+            if (isValidProp(prop)) {
+                return prop;
             }
-            
-            return prop;
         }
-        
+
         return getMasterPropVal(props, masterProps, propName);
     }
-    
+
     private TextProp getMasterPropVal(TextPropCollection props, TextPropCollection masterProps, String propName) {
         boolean isChar = props.getTextPropType() == TextPropType.character;
 
@@ -783,7 +784,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
         }
 
         String propNames[] = propName.split(",");
-        if (masterProps == null) { 
+        if (masterProps == null) {
             HSLFSheet sheet = getSheet();
             int txtype = getRunType();
             HSLFMasterSheet master = sheet.getMasterSheet();
@@ -794,22 +795,29 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
 
             for (String pn : propNames) {
                 TextProp prop = master.getStyleAttribute(txtype, getIndentLevel(), pn, isChar);
-                if (prop != null) {
+                if (isValidProp(prop)) {
                     return prop;
                 }
             }
         } else {
             for (String pn : propNames) {
                 TextProp prop = masterProps.findByName(pn);
-                if (prop != null) {
+                if (isValidProp(prop)) {
                     return prop;
                 }
             }
         }
-        
+
+
         return null;
     }
 
+    private static boolean isValidProp(TextProp prop) {
+        // Font properties (maybe other too???) can have an index of -1
+        // so we check the master for this font index then
+        return prop != null && (!prop.getName().contains("font") || prop.getValue() != -1);
+    }
+
     /**
      * Returns the named TextProp, either by fetching it (if it exists) or
      * adding it (if it didn't)
@@ -823,7 +831,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
         if (getSheet() instanceof MasterSheet && masterProps != null) {
             pc = masterProps;
         }
-        
+
         if (val == null) {
             pc.removeByName(name);
             return;
@@ -1020,7 +1028,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
         if (lastPTPC == null || lastRTPC == null || ptpc == null || rtpc == null) { // NOSONAR
             throw new HSLFException("Not all TextPropCollection could be determined.");
         }
-        
+
         ptpc.updateTextSize(ptpc.getCharactersCovered() + 1);
         rtpc.updateTextSize(rtpc.getCharactersCovered() + 1);
         lastPTPC.updateTextSize(lastPTPC.getCharactersCovered() + 1);
@@ -1075,11 +1083,11 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
             assert(info != null && txinfo != null);
             _txtbox.appendChildRecord(info);
             _txtbox.appendChildRecord(txinfo);
-        }    
+        }
     }
-    
+
     /**
-     * Writes the textbox records back to the document record 
+     * Writes the textbox records back to the document record
      */
     private static void refreshRecords(List<HSLFTextParagraph> paragraphs) {
         TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom;
@@ -1624,7 +1632,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
 
     /**
      * {@inheritDoc}
-     * 
+     *
      * @see RoundTripHFPlaceholder12
      */
     @Override
index 0d887a4120e072e8d082fdfa0d822311005c3ca2..8d6a5f0ef5f22f5ad0d24f2106f6d774a53d8940 100644 (file)
@@ -20,6 +20,8 @@ package org.apache.poi.hslf.usermodel;
 import java.awt.Color;
 import java.util.List;
 
+import org.apache.poi.common.usermodel.fonts.FontGroup;
+import org.apache.poi.common.usermodel.fonts.FontInfo;
 import org.apache.poi.hslf.exceptions.HSLFException;
 import org.apache.poi.hslf.model.textproperties.BitMaskTextProp;
 import org.apache.poi.hslf.model.textproperties.CharFlagsTextProp;
@@ -49,15 +51,16 @@ public final class HSLFTextRun implements TextRun {
        /** The TextRun we belong to */
        private HSLFTextParagraph parentParagraph;
        private String _runText = "";
-       private String _fontFamily;
+       /** Caches the font info objects until the text runs are attached to the container */
+       private HSLFFontInfo[] cachedFontInfo;
        private HSLFHyperlink link;
-       
+
        /**
         * Our paragraph and character style.
         * Note - we may share these styles with other RichTextRuns
         */
        private TextPropCollection characterStyle = new TextPropCollection(1, TextPropType.character);
-       
+
        private TextPropCollection masterStyle;
 
        /**
@@ -67,7 +70,7 @@ public final class HSLFTextRun implements TextRun {
        public HSLFTextRun(HSLFTextParagraph parentParagraph) {
                this.parentParagraph = parentParagraph;
        }
-       
+
        public TextPropCollection getCharacterStyle() {
            return characterStyle;
        }
@@ -76,27 +79,29 @@ public final class HSLFTextRun implements TextRun {
            this.characterStyle.copy(characterStyle);
            this.characterStyle.updateTextSize(_runText.length());
        }
-       
+
     /**
      * Setting a master style reference
      *
      * @param characterStyle the master style reference
-     * 
+     *
      * @since POI 3.14-Beta1
      */
        @Internal
     /* package */ void setMasterStyleReference(TextPropCollection masterStyle) {
         this.masterStyle = masterStyle;
     }
-       
+
+
        /**
         * Supply the SlideShow we belong to
         */
        public void updateSheet() {
-               if (_fontFamily != null) {
-                       setFontFamily(_fontFamily);
-                       _fontFamily = null;
+               if (cachedFontInfo != null) {
+                   for (FontGroup tt : FontGroup.values()) {
+                       setFontInfo(cachedFontInfo[tt.ordinal()], tt);
+                   }
+                   cachedFontInfo = null;
                }
        }
 
@@ -307,31 +312,97 @@ public final class HSLFTextRun implements TextRun {
        }
 
        @Override
-       public void setFontFamily(String fontFamily) {
-           HSLFSheet sheet = parentParagraph.getSheet();
-           @SuppressWarnings("resource")
+       public void setFontFamily(String typeface) {
+           setFontInfo(new HSLFFontInfo(typeface), FontGroup.LATIN);
+       }
+
+    @Override
+    public void setFontFamily(String typeface, FontGroup fontGroup) {
+        setFontInfo(new HSLFFontInfo(typeface), fontGroup);
+    }
+
+    @Override
+       public void setFontInfo(FontInfo fontInfo, FontGroup fontGroup) {
+        FontGroup fg = safeFontGroup(fontGroup);
+        
+        HSLFSheet sheet = parentParagraph.getSheet();
+        @SuppressWarnings("resource")
         HSLFSlideShow slideShow = (sheet == null) ? null : sheet.getSlideShow();
-               if (sheet == null || slideShow == null) {
-                       //we can't set font since slideshow is not assigned yet
-                       _fontFamily = fontFamily;
-                       return;
-               }
-               // Get the index for this font (adding if needed)
-               Integer fontIdx = (fontFamily == null) ? null : slideShow.getFontCollection().addFont(fontFamily);
-               setCharTextPropVal("font.index", fontIdx);
+        if (sheet == null || slideShow == null) {
+            // we can't set font since slideshow is not assigned yet
+            if (cachedFontInfo == null) {
+                cachedFontInfo = new HSLFFontInfo[FontGroup.values().length];
+            }
+            cachedFontInfo[fg.ordinal()] = (fontInfo != null) ? new HSLFFontInfo(fontInfo) : null;
+            return;
+        }
+
+        String propName;
+        switch (fg) {
+        default:
+        case LATIN:
+            propName = "font.index";
+            break;
+        case COMPLEX_SCRIPT:
+            // TODO: implement TextCFException10 structure
+        case EAST_ASIAN:
+            propName = "asian.font.index";
+            break;
+        case SYMBOL:
+            propName = "symbol.font.index";
+            break;
+        }
+
+
+        // Get the index for this font, if it is not to be removed (typeface == null)
+        Integer fontIdx = null;
+        if (fontInfo != null) {
+            fontIdx = slideShow.addFont(fontInfo).getIndex();
+        }
+
+
+        setCharTextPropVal(propName, fontIdx);
+    }
+
+    @Override
+       public String getFontFamily() {
+        return getFontFamily(null);
        }
 
+    @Override
+    public String getFontFamily(FontGroup fontGroup) {
+        HSLFFontInfo fi = getFontInfo(fontGroup);
+        return (fi != null) ? fi.getTypeface() : null;
+    }
+
        @Override
-       public String getFontFamily() {
-        HSLFSheet sheet = parentParagraph.getSheet();
+       public HSLFFontInfo getFontInfo(final FontGroup fontGroup) {
+        FontGroup fg = safeFontGroup(fontGroup);
+
+           HSLFSheet sheet = parentParagraph.getSheet();
         @SuppressWarnings("resource")
         HSLFSlideShow slideShow = (sheet == null) ? null : sheet.getSlideShow();
                if (sheet == null || slideShow == null) {
-                       return _fontFamily;
+                       return (cachedFontInfo != null) ? cachedFontInfo[fg.ordinal()] : null;
                }
-        TextProp tp = getTextParagraph().getPropVal(characterStyle, masterStyle, "font.index,asian.font.index,ansi.font.index,symbol.font.index");
-        if (tp == null) { return null; }
-               return slideShow.getFontCollection().getFontWithId(tp.getValue());
+
+               String propName;
+           switch (fg) {
+           default:
+           case LATIN:
+               propName = "font.index,ansi.font.index";
+               break;
+        case COMPLEX_SCRIPT:
+           case EAST_ASIAN:
+               propName = "asian.font.index";
+               break;
+        case SYMBOL:
+               propName = "symbol.font.index";
+               break;
+               }
+
+        TextProp tp = getTextParagraph().getPropVal(characterStyle, masterStyle, propName);
+               return (tp != null) ? slideShow.getFont(tp.getValue()) : null;
        }
 
        /**
@@ -363,7 +434,7 @@ public final class HSLFTextRun implements TextRun {
     public void setFontColor(Color color) {
         setFontColor(DrawPaint.createSolidPaint(color));
     }
-       
+
        @Override
        public void setFontColor(PaintStyle color) {
            if (!(color instanceof SolidPaint)) {
@@ -384,7 +455,7 @@ public final class HSLFTextRun implements TextRun {
     public HSLFTextParagraph getTextParagraph() {
         return parentParagraph;
     }
-    
+
     @Override
     public TextCap getTextCap() {
         return TextCap.NONE;
@@ -413,12 +484,12 @@ public final class HSLFTextRun implements TextRun {
     protected void setHyperlink(HSLFHyperlink link) {
         this.link = link;
     }
-    
+
     @Override
     public HSLFHyperlink getHyperlink() {
         return link;
     }
-    
+
     @Override
     public HSLFHyperlink createHyperlink() {
         if (link == null) {
@@ -427,12 +498,12 @@ public final class HSLFTextRun implements TextRun {
         }
         return link;
     }
-    
+
     @Override
     public FieldType getFieldType() {
         HSLFTextShape ts = getTextParagraph().getParentShape();
         Placeholder ph = ts.getPlaceholder();
-        
+
         if (ph != null) {
             switch (ph) {
             case SLIDE_NUMBER:
@@ -455,7 +526,11 @@ public final class HSLFTextRun implements TextRun {
             }
             return trList.get(0).getFieldType();
         }
-        
+
         return null;
     }
+    
+    private FontGroup safeFontGroup(FontGroup fontGroup) {
+        return (fontGroup != null) ? fontGroup : FontGroup.getFontGroupFirst(getRawText());
+    }
 }
index c18a2c65124ccddf0d5bbc9fb25380b8b62e65d8..0c5950dde264d09e086cc4e1ee1498efca972791 100644 (file)
@@ -35,9 +35,9 @@ import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
-import java.util.Map;
 import java.util.NoSuchElementException;
 
+import org.apache.poi.common.usermodel.fonts.FontInfo;
 import org.apache.poi.hwmf.record.HwmfBrushStyle;
 import org.apache.poi.hwmf.record.HwmfFont;
 import org.apache.poi.hwmf.record.HwmfHatchStyle;
@@ -48,7 +48,6 @@ import org.apache.poi.hwmf.record.HwmfPenStyle;
 import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash;
 import org.apache.poi.sl.draw.DrawFactory;
 import org.apache.poi.sl.draw.DrawFontManager;
-import org.apache.poi.sl.draw.Drawable;
 import org.apache.poi.util.LocaleUtil;
 
 public class HwmfGraphics {
@@ -330,9 +329,8 @@ public class HwmfGraphics {
         // TODO: another approx. ...
         double fontW = fontH/1.8;
         
-        int len = text.length;
-        Charset charset = (font.getCharSet().getCharset() == null)?
-                DEFAULT_CHARSET : font.getCharSet().getCharset();
+        Charset charset = (font.getCharset().getCharset() == null)?
+                DEFAULT_CHARSET : font.getCharset().getCharset();
         String textString = new String(text, charset);
         AttributedString as = new AttributedString(textString);
         if (dx == null || dx.length == 0) {
@@ -401,21 +399,10 @@ public class HwmfGraphics {
     }
     
     private void addAttributes(AttributedString as, HwmfFont font) {
-        DrawFontManager fontHandler = (DrawFontManager)graphicsCtx.getRenderingHint(Drawable.FONT_HANDLER);
-        String fontFamily = null;
-        @SuppressWarnings("unchecked")
-        Map<String,String> fontMap = (Map<String,String>)graphicsCtx.getRenderingHint(Drawable.FONT_MAP);
-        if (fontMap != null && fontMap.containsKey(font.getFacename())) {
-            fontFamily = fontMap.get(font.getFacename());
-        }
-        if (fontHandler != null) {
-            fontFamily = fontHandler.getRendererableFont(font.getFacename(), font.getPitchAndFamily());
-        }
-        if (fontFamily == null) {
-            fontFamily = font.getFacename();
-        }
+        DrawFontManager fontHandler = DrawFactory.getInstance(graphicsCtx).getFontManager(graphicsCtx);
+        FontInfo fontInfo = fontHandler.getMappedFont(graphicsCtx, font);
         
-        as.addAttribute(TextAttribute.FAMILY, fontFamily);
+        as.addAttribute(TextAttribute.FAMILY, fontInfo.getTypeface());
         as.addAttribute(TextAttribute.SIZE, getFontHeight(font));
         as.addAttribute(TextAttribute.STRIKETHROUGH, font.isStrikeOut());
         if (font.isUnderline()) {
index f6e25638174824641eb7a0edc6dd1051bf50a48d..ac667f73fcb700dc78ae58813d29b69805631b42 100644 (file)
@@ -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 <code>null</code> if there is no matching charset or
-         *         if the charset is a &quot;default&quot;
-         */
-        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.");
+    }
 }
index 063d8b7b0a137e32873b7656bcee1cbb53bda9bb..d0da433f2bae06a4776ebbc051256df7736b6772 100644 (file)
@@ -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();
             }
         }
index d50ac4ec0e97102ecb7cedae49219c5885441e94..ebbc17e38367eea9b82caf75ad5efa0120765a2a 100644 (file)
@@ -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 {
index 2a1a6afa7e5d4d0150ce707824c70dd24d572499..bc4c61c14c02c58c19c63545fafdd3fc006f899d 100644 (file)
@@ -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();
     }
index b18683ddddeb90c8d48b7c98dee03c50200f872f..80772d3a1c8319a9949e0691ab7719b28dc00e9d 100644 (file)
@@ -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();
     }
index 2d595f332542a1b4b0ef284aa4582eb8126f587f..7ca07bd82e4658f352004254ce252ef8758486d3 100644 (file)
 package org.apache.poi.hslf.record;
 
 import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 
-import junit.framework.TestCase;
+import org.apache.poi.hslf.usermodel.HSLFFontInfo;
+import org.apache.poi.hslf.usermodel.HSLFFontInfoPredefined;
+import org.apache.poi.poifs.storage.RawDataUtil;
+import org.junit.BeforeClass;
+import org.junit.Test;
 
 /**
- * Tests <code>FontCollection</code> and <code>FontEntityAtom</code> records
- *
- * @author Yegor Kozlov
+ * Tests {@code FontCollection} and {@code FontEntityAtom} records
  */
-public final class TestFontCollection extends TestCase {
+public final class TestFontCollection {
     // From a real file
-    private final byte[] data = new byte[]  {
-        0x0F, 0x00, 0xD5-256, 0x07, 0x4C, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0xB7-256, 0x0F, 0x44, 0x00, 0x00, 0x00,
-        0x54, 0x00, 0x69, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x73, 0x00,
-        0x20, 0x00, 0x4E, 0x00, 0x65, 0x00, 0x77, 0x00, 0x20, 0x00,
-        0x52, 0x00, 0x6F, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x6E, 0x00,
-        0x00, 0x00, 0x74, 0x34, 0xB8-256, 0x00, 0x7C, 0xDA-256, 0x12, 0x00,
-        0x64, 0xDA-256, 0x12, 0x00, 0x76, 0xC7-256, 0x0B, 0x30, 0x08, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xDA-256, 0x12, 0x00,
-        0x28, 0xDD-256, 0x0D, 0x30, 0x00, 0x00, 0x04, 0x00  };
+    private static byte[] data;
+    
+    @BeforeClass
+    public static void init() throws IOException {
+        data = RawDataUtil.decompress(
+            "H4sIAAAAAAAAAONnuMruwwAC2/ldgGQIQyZDLkMqQzGDAoMfkC4H0kEM+U"+
+            "CxRIY8oHyJyQ6GmltCDClAXHac24CDAQJAYhp3eQ0YGFgYAAusGftUAAAA"
+        );
+    }
 
+    @Test
     public void testFonts() {
         FontCollection fonts = new FontCollection(data, 0, data.length);
         Record[] child = fonts.getChildRecords();
@@ -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();
index 462b77ed27be5321f499e1421e3c5d7fa89fb02f..0246501876a8b248415fdbf8fbd97e6b14218b3c 100644 (file)
@@ -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<HSLFTextParagraph> paraList : sl.getTextParagraphs()) {
             for (HSLFTextParagraph htp : paraList) {
                 for (HSLFTextRun htr : htp) {
-                    String actFamily = htr.getFontFamily();
+                    String actFamily = htr.getFontFamily(FontGroup.EAST_ASIAN);
                     assertEquals(expFamily, actFamily);
                 }
             }
index 210ae95cbe18ff079b3a2e211b4442536f1767da..8aac3b29b4b016e1652cd96111a5ef257487096a 100644 (file)
 
 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;
index 9d53f67b65e9ac3d2a941a33268315c8106eeaf0..50ddda3297e51c07c4e2d41a3d97663eb8e9f708 100644 (file)
@@ -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();