Browse Source

Bug 61331 - Font group handling / common font interface

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1802741 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_3_17_FINAL
Andreas Beeker 6 years ago
parent
commit
b8242721fb
35 changed files with 2009 additions and 983 deletions
  1. 124
    0
      src/java/org/apache/poi/common/usermodel/fonts/FontCharset.java
  2. 80
    0
      src/java/org/apache/poi/common/usermodel/fonts/FontFamily.java
  3. 149
    0
      src/java/org/apache/poi/common/usermodel/fonts/FontGroup.java
  4. 102
    0
      src/java/org/apache/poi/common/usermodel/fonts/FontInfo.java
  5. 74
    0
      src/java/org/apache/poi/common/usermodel/fonts/FontPitch.java
  6. 11
    0
      src/java/org/apache/poi/sl/draw/DrawFactory.java
  7. 89
    0
      src/java/org/apache/poi/sl/draw/DrawFontInfo.java
  8. 41
    13
      src/java/org/apache/poi/sl/draw/DrawFontManager.java
  9. 101
    0
      src/java/org/apache/poi/sl/draw/DrawFontManagerDefault.java
  10. 8
    3
      src/java/org/apache/poi/sl/draw/DrawTextFragment.java
  11. 109
    147
      src/java/org/apache/poi/sl/draw/DrawTextParagraph.java
  12. 56
    2
      src/java/org/apache/poi/sl/usermodel/TextRun.java
  13. 4
    1
      src/java/org/apache/poi/ss/usermodel/FontCharset.java
  14. 273
    83
      src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
  15. 24
    43
      src/ooxml/testcases/org/apache/poi/sl/TestFonts.java
  16. 0
    243
      src/scratchpad/src/org/apache/poi/hslf/model/PPFont.java
  17. 1
    1
      src/scratchpad/src/org/apache/poi/hslf/model/textproperties/CharFlagsTextProp.java
  18. 66
    60
      src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java
  19. 11
    12
      src/scratchpad/src/org/apache/poi/hslf/record/FontEntityAtom.java
  20. 47
    18
      src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java
  21. 215
    0
      src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfo.java
  22. 97
    0
      src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfoPredefined.java
  23. 34
    24
      src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java
  24. 41
    33
      src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java
  25. 110
    35
      src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java
  26. 6
    19
      src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java
  27. 61
    182
      src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java
  28. 5
    9
      src/scratchpad/src/org/apache/poi/hwpf/HWPFOldDocument.java
  29. 3
    3
      src/scratchpad/src/org/apache/poi/hwpf/model/OldFfn.java
  30. 16
    12
      src/scratchpad/testcases/org/apache/poi/hslf/model/TestPPFont.java
  31. 4
    4
      src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java
  32. 35
    27
      src/scratchpad/testcases/org/apache/poi/hslf/record/TestFontCollection.java
  33. 2
    1
      src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java
  34. 8
    6
      src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java
  35. 2
    2
      src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestHWPFOldDocument.java

+ 124
- 0
src/java/org/apache/poi/common/usermodel/fonts/FontCharset.java View File

@@ -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];
}
}

+ 80
- 0
src/java/org/apache/poi/common/usermodel/fonts/FontFamily.java View File

@@ -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);
}

}

+ 149
- 0
src/java/org/apache/poi/common/usermodel/fonts/FontGroup.java View File

@@ -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;
}
}

+ 102
- 0
src/java/org/apache/poi/common/usermodel/fonts/FontInfo.java View File

@@ -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);
}

+ 74
- 0
src/java/org/apache/poi/common/usermodel/fonts/FontPitch.java View File

@@ -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);
}
}


+ 11
- 0
src/java/org/apache/poi/sl/draw/DrawFactory.java View 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();
}
}

+ 89
- 0
src/java/org/apache/poi/sl/draw/DrawFontInfo.java View File

@@ -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.");
}
}

+ 41
- 13
src/java/org/apache/poi/sl/draw/DrawFontManager.java View File

@@ -19,6 +19,12 @@

package org.apache.poi.sl.draw;

import java.awt.Font;
import java.awt.Graphics2D;

import org.apache.poi.common.usermodel.fonts.FontInfo;
import org.apache.poi.util.StringUtil;

/**
* Manages fonts when rendering slides.
*
@@ -29,28 +35,50 @@ public interface DrawFontManager {
/**
* select a font to be used to paint text
*
* @param typeface the font family as defined in the .pptx file.
* This can be unknown or missing in the graphic environment.
* @param pitchFamily a pitch-and-family,
* see {@link org.apache.poi.hwmf.record.HwmfFont#getFamily()} and
* {@link org.apache.poi.hwmf.record.HwmfFont#getPitch()}
* for how to calculate those (ancient) values
* @param graphics the graphics context to request additional rendering hints
* @param fontInfo the font info object corresponding to the text run font
*
* @return the font to be used to paint text
*/
String getRendererableFont(String typeface, int pitchFamily);
FontInfo getMappedFont(Graphics2D graphics, FontInfo fontInfo);

/**
* In case the original font doesn't contain a glyph, use the
* returned fallback font as an alternative
*
* @param typeface the font family as defined in the .pptx file.
* @param pitchFamily a pitch-and-family,
* see {@link org.apache.poi.hwmf.record.HwmfFont#getFamily()} and
* {@link org.apache.poi.hwmf.record.HwmfFont#getPitch()}
* for how to calculate those (ancient) values
* @param graphics the graphics context to request additional rendering hints
* @param fontInfo the font info object corresponding to the text run font
*
* @return the font to be used as a fallback for the original typeface
*/
String getFallbackFont(String typeface, int pitchFamily);
FontInfo getFallbackFont(Graphics2D graphics, FontInfo fontInfo);

/**
* Map text charset depending on font family.<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);
}

+ 101
- 0
src/java/org/apache/poi/sl/draw/DrawFontManagerDefault.java View File

@@ -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;
}
}

+ 8
- 3
src/java/org/apache/poi/sl/draw/DrawTextFragment.java View 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;
}
/**

+ 109
- 147
src/java/org/apache/poi/sl/draw/DrawTextParagraph.java View 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());
}
}

+ 56
- 2
src/java/org/apache/poi/sl/usermodel/TextRun.java View 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
*/

+ 4
- 1
src/java/org/apache/poi/ss/usermodel/FontCharset.java View File

@@ -17,13 +17,16 @@

package org.apache.poi.ss.usermodel;

import org.apache.poi.util.Removal;

/**
* Charset represents the basic set of characters associated with a font (that it can display), and
* corresponds to the ANSI codepage (8-bit or DBCS) of that character set used by a given language.
*
* @author Gisella Bronzetti
* @deprecated enum will be replaced by common version org.apache.poi.common.usermodel.FontCharset
*/
@Removal(version="4.0")
@Deprecated
public enum FontCharset {

ANSI(0),

+ 273
- 83
src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java View 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;
}
}
}

+ 24
- 43
src/ooxml/testcases/org/apache/poi/sl/TestFonts.java View 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);
}
}
}

+ 0
- 243
src/scratchpad/src/org/apache/poi/hslf/model/PPFont.java View File

@@ -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);
}
}

+ 1
- 1
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/CharFlagsTextProp.java View 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

+ 66
- 60
src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java View File

@@ -17,38 +17,37 @@

package org.apache.poi.hslf.record;

import org.apache.poi.hslf.model.PPFont;
import java.io.IOException;
import java.io.OutputStream;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.poi.common.usermodel.fonts.FontInfo;
import org.apache.poi.hslf.usermodel.HSLFFontInfo;
import org.apache.poi.hslf.usermodel.HSLFFontInfoPredefined;
import org.apache.poi.util.POILogger;

import java.io.*;
import java.util.*;

/**
* <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);
}
}

+ 11
- 12
src/scratchpad/src/org/apache/poi/hslf/record/FontEntityAtom.java View 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){

+ 47
- 18
src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java View File

@@ -17,20 +17,19 @@

package org.apache.poi.hslf.record;

import org.apache.poi.util.ArrayUtil;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.hslf.util.MutableByteArrayOutputStream;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;

import org.apache.poi.hslf.util.MutableByteArrayOutputStream;
import org.apache.poi.util.ArrayUtil;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.Removal;

/**
* Abstract class which all container records will extend. Providers
* helpful methods for writing child records out to disk
*
* @author Nick Burch
*/

public abstract class RecordContainer extends Record
@@ -40,12 +39,14 @@ public abstract class RecordContainer extends Record
/**
* Return any children
*/
public Record[] getChildRecords() { return _children; }
@Override
public Record[] getChildRecords() { return _children; }

/**
* We're not an atom
*/
public boolean isAnAtom() { return false; }
@Override
public boolean isAnAtom() { return false; }


/* ===============================================================
@@ -70,14 +71,16 @@ public abstract class RecordContainer extends Record
/**
* Adds a child record, at the very end.
* @param newChild The child record to add
* @return the position of the added child
*/
private void appendChild(Record newChild) {
private int appendChild(Record newChild) {
// Copy over, and pop the child in at the end
Record[] nc = new Record[(_children.length + 1)];
System.arraycopy(_children, 0, nc, 0, _children.length);
// Switch the arrays
nc[_children.length] = newChild;
_children = nc;
return _children.length;
}

/**
@@ -138,8 +141,11 @@ public abstract class RecordContainer extends Record
Record rm = null;
ArrayList<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

+ 215
- 0
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfo.java View File

@@ -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;
}
}

+ 97
- 0
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfoPredefined.java View File

@@ -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.");
}
}

+ 34
- 24
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java View 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);
}

/**

+ 41
- 33
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java View 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

+ 110
- 35
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java View 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());
}
}

+ 6
- 19
src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java View 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()) {

+ 61
- 182
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java View 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.");
}
}

+ 5
- 9
src/scratchpad/src/org/apache/poi/hwpf/HWPFOldDocument.java View 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();
}
}

+ 3
- 3
src/scratchpad/src/org/apache/poi/hwpf/model/OldFfn.java View 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 {

+ 16
- 12
src/scratchpad/testcases/org/apache/poi/hslf/model/TestPPFont.java View 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();
}

+ 4
- 4
src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java View 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();
}

+ 35
- 27
src/scratchpad/testcases/org/apache/poi/hslf/record/TestFontCollection.java View File

@@ -18,29 +18,34 @@
package org.apache.poi.hslf.record;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import junit.framework.TestCase;
import org.apache.poi.hslf.usermodel.HSLFFontInfo;
import org.apache.poi.hslf.usermodel.HSLFFontInfoPredefined;
import org.apache.poi.poifs.storage.RawDataUtil;
import org.junit.BeforeClass;
import org.junit.Test;

/**
* Tests <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();

+ 2
- 1
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java View 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);
}
}

+ 8
- 6
src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java View File

@@ -17,11 +17,9 @@

package org.apache.poi.hwmf;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.apache.poi.POITestCase.assertContains;
import static org.junit.Assert.assertEquals;

import javax.imageio.ImageIO;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
@@ -40,6 +38,8 @@ import java.util.Locale;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.imageio.ImageIO;

import org.apache.poi.POIDataSamples;
import org.apache.poi.hwmf.record.HwmfFill.HwmfImageRecord;
import org.apache.poi.hwmf.record.HwmfFont;
@@ -118,7 +118,9 @@ public class TestHwmfParsing {
SlideShow<?,?> ss = SlideShowFactory.create(fis);
int wmfIdx = 1;
for (PictureData pd : ss.getPictureData()) {
if (pd.getType() != PictureType.WMF) continue;
if (pd.getType() != PictureType.WMF) {
continue;
}
byte wmfData[] = pd.getData();
String filename = String.format(Locale.ROOT, "%s-%04d.wmf", basename, wmfIdx);
FileOutputStream fos = new FileOutputStream(new File(outdir, filename));
@@ -211,7 +213,7 @@ public class TestHwmfParsing {
for (HwmfRecord r : wmf.getRecords()) {
if (r.getRecordType().equals(HwmfRecordType.createFontIndirect)) {
HwmfFont font = ((HwmfText.WmfCreateFontIndirect)r).getFont();
charset = (font.getCharSet().getCharset() == null) ? LocaleUtil.CHARSET_1252 : font.getCharSet().getCharset();
charset = (font.getCharset().getCharset() == null) ? LocaleUtil.CHARSET_1252 : font.getCharset().getCharset();
}
if (r.getRecordType().equals(HwmfRecordType.extTextOut)) {
HwmfText.WmfExtTextOut textOut = (HwmfText.WmfExtTextOut)r;
@@ -239,7 +241,7 @@ public class TestHwmfParsing {
for (HwmfRecord r : wmf.getRecords()) {
if (r.getRecordType().equals(HwmfRecordType.createFontIndirect)) {
HwmfFont font = ((HwmfText.WmfCreateFontIndirect)r).getFont();
charset = (font.getCharSet().getCharset() == null) ? LocaleUtil.CHARSET_1252 : font.getCharSet().getCharset();
charset = (font.getCharset().getCharset() == null) ? LocaleUtil.CHARSET_1252 : font.getCharset().getCharset();
}
if (r.getRecordType().equals(HwmfRecordType.extTextOut)) {
HwmfText.WmfExtTextOut textOut = (HwmfText.WmfExtTextOut)r;

+ 2
- 2
src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestHWPFOldDocument.java View 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();


Loading…
Cancel
Save