git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1802741 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_17_FINAL
@@ -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 "default" | |||
*/ | |||
public Charset getCharset() { | |||
return charset; | |||
} | |||
public int getNativeId() { | |||
return nativeId; | |||
} | |||
public static FontCharset valueOf(int value){ | |||
return (value < 0 || value >= _table.length) ? null :_table[value]; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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); | |||
} | |||
} | |||
@@ -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(); | |||
} | |||
} |
@@ -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."); | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
/** |
@@ -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()); | |||
} | |||
} |
@@ -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 | |||
*/ |
@@ -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), |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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 |
@@ -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); | |||
} | |||
} |
@@ -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){ |
@@ -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 |
@@ -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; | |||
} | |||
} |
@@ -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."); | |||
} | |||
} |
@@ -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); | |||
} | |||
/** |
@@ -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 |
@@ -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()); | |||
} | |||
} |
@@ -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()) { |
@@ -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 "default" | |||
*/ | |||
public Charset getCharset() { | |||
return charset; | |||
} | |||
public static WmfCharset valueOf(int flag) { | |||
for (WmfCharset cs : values()) { | |||
if (cs.flag == flag) return cs; | |||
} | |||
return null; | |||
} | |||
} | |||
public class HwmfFont implements FontInfo { | |||
/** | |||
* The output precision defines how closely the output must match the requested font's height, | |||
@@ -176,7 +92,9 @@ public class HwmfFont { | |||
static WmfOutPrecision valueOf(int flag) { | |||
for (WmfOutPrecision op : values()) { | |||
if (op.flag == flag) return op; | |||
if (op.flag == flag) { | |||
return op; | |||
} | |||
} | |||
return null; | |||
} | |||
@@ -237,7 +155,9 @@ public class HwmfFont { | |||
static WmfClipPrecision valueOf(int flag) { | |||
for (WmfClipPrecision cp : values()) { | |||
if (cp.flag == flag) return cp; | |||
if (cp.flag == flag) { | |||
return cp; | |||
} | |||
} | |||
return null; | |||
} | |||
@@ -292,90 +212,15 @@ public class HwmfFont { | |||
static WmfFontQuality valueOf(int flag) { | |||
for (WmfFontQuality fq : values()) { | |||
if (fq.flag == flag) return fq; | |||
if (fq.flag == flag) { | |||
return fq; | |||
} | |||
} | |||
return null; | |||
} | |||
} | |||
/** | |||
* A property of a font that describes its general appearance. | |||
*/ | |||
public enum WmfFontFamilyClass { | |||
/** | |||
* The default font is specified, which is implementation-dependent. | |||
*/ | |||
FF_DONTCARE (0x00), | |||
/** | |||
* Fonts with variable stroke widths, which are proportional to the actual widths of | |||
* the glyphs, and which have serifs. "MS Serif" is an example. | |||
*/ | |||
FF_ROMAN (0x01), | |||
/** | |||
* Fonts with variable stroke widths, which are proportional to the actual widths of the | |||
* glyphs, and which do not have serifs. "MS Sans Serif" is an example. | |||
*/ | |||
FF_SWISS (0x02), | |||
/** | |||
* Fonts with constant stroke width, with or without serifs. Fixed-width fonts are | |||
* usually modern. "Pica", "Elite", and "Courier New" are examples. | |||
*/ | |||
FF_MODERN (0x03), | |||
/** | |||
* Fonts designed to look like handwriting. "Script" and "Cursive" are examples. | |||
*/ | |||
FF_SCRIPT (0x04), | |||
/** | |||
* Novelty fonts. "Old English" is an example. | |||
*/ | |||
FF_DECORATIVE (0x05); | |||
int flag; | |||
WmfFontFamilyClass(int flag) { | |||
this.flag = flag; | |||
} | |||
static WmfFontFamilyClass valueOf(int flag) { | |||
for (WmfFontFamilyClass ff : values()) { | |||
if (ff.flag == flag) return ff; | |||
} | |||
return null; | |||
} | |||
} | |||
/** | |||
* A property of a font that describes the pitch, of the characters. | |||
*/ | |||
public enum WmfFontPitch { | |||
/** | |||
* The default pitch, which is implementation-dependent. | |||
*/ | |||
DEFAULT_PITCH (0x00), | |||
/** | |||
* A fixed pitch, which means that all the characters in the font occupy the same | |||
* width when output in a string. | |||
*/ | |||
FIXED_PITCH (0x01), | |||
/** | |||
* A variable pitch, which means that the characters in the font occupy widths | |||
* that are proportional to the actual widths of the glyphs when output in a string. For example, | |||
* the "i" and space characters usually have much smaller widths than a "W" or "O" character. | |||
*/ | |||
VARIABLE_PITCH (0x02); | |||
int flag; | |||
WmfFontPitch(int flag) { | |||
this.flag = flag; | |||
} | |||
static WmfFontPitch valueOf(int flag) { | |||
for (WmfFontPitch fp : values()) { | |||
if (fp.flag == flag) return fp; | |||
} | |||
return null; | |||
} | |||
} | |||
/** | |||
* A 16-bit signed integer that specifies the height, in logical units, of the font's | |||
* character cell. The character height is computed as the character cell height minus the | |||
@@ -454,7 +299,7 @@ public class HwmfFont { | |||
* If a typeface name in the FaceName field is specified, the CharSet value MUST match the | |||
* character set of that typeface. | |||
*/ | |||
WmfCharset charSet; | |||
FontCharset charSet; | |||
/** | |||
* An 8-bit unsigned integer that defines the output precision. | |||
@@ -486,12 +331,12 @@ public class HwmfFont { | |||
* intended for specifying fonts when the exact typeface wanted is not available. | |||
* (LSB 4 bits) | |||
*/ | |||
WmfFontFamilyClass family; | |||
FontFamily family; | |||
/** | |||
* A property of a font that describes the pitch (MSB 2 bits) | |||
*/ | |||
WmfFontPitch pitch; | |||
FontPitch pitch; | |||
/** | |||
* A null-terminated string of 8-bit Latin-1 [ISO/IEC-8859-1] ANSI | |||
@@ -509,7 +354,7 @@ public class HwmfFont { | |||
italic = leis.readByte() != 0; | |||
underline = leis.readByte() != 0; | |||
strikeOut = leis.readByte() != 0; | |||
charSet = WmfCharset.valueOf(leis.readUByte()); | |||
charSet = FontCharset.valueOf(leis.readUByte()); | |||
outPrecision = WmfOutPrecision.valueOf(leis.readUByte()); | |||
clipPrecision = WmfClipPrecision.valueOf(leis.readUByte()); | |||
quality = WmfFontQuality.valueOf(leis.readUByte()); | |||
@@ -561,10 +406,6 @@ public class HwmfFont { | |||
return strikeOut; | |||
} | |||
public WmfCharset getCharSet() { | |||
return charSet; | |||
} | |||
public WmfOutPrecision getOutPrecision() { | |||
return outPrecision; | |||
} | |||
@@ -581,15 +422,53 @@ public class HwmfFont { | |||
return pitchAndFamily; | |||
} | |||
public WmfFontFamilyClass getFamily() { | |||
return WmfFontFamilyClass.valueOf(pitchAndFamily & 0xF); | |||
@Override | |||
public FontFamily getFamily() { | |||
return FontFamily.valueOf(pitchAndFamily & 0xF); | |||
} | |||
public WmfFontPitch getPitch() { | |||
return WmfFontPitch.valueOf((pitchAndFamily >>> 6) & 3); | |||
@Override | |||
public void setFamily(FontFamily family) { | |||
throw new UnsupportedOperationException("setCharset not supported by HwmfFont."); | |||
} | |||
public String getFacename() { | |||
@Override | |||
public FontPitch getPitch() { | |||
return FontPitch.valueOf((pitchAndFamily >>> 6) & 3); | |||
} | |||
@Override | |||
public void setPitch(FontPitch pitch) { | |||
throw new UnsupportedOperationException("setPitch not supported by HwmfFont."); | |||
} | |||
@Override | |||
public Integer getIndex() { | |||
return null; | |||
} | |||
@Override | |||
public void setIndex(int index) { | |||
throw new UnsupportedOperationException("setIndex not supported by HwmfFont."); | |||
} | |||
@Override | |||
public String getTypeface() { | |||
return facename; | |||
} | |||
@Override | |||
public void setTypeface(String typeface) { | |||
throw new UnsupportedOperationException("setTypeface not supported by HwmfFont."); | |||
} | |||
@Override | |||
public FontCharset getCharset() { | |||
return charSet; | |||
} | |||
@Override | |||
public void setCharset(FontCharset charset) { | |||
throw new UnsupportedOperationException("setCharset not supported by HwmfFont."); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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 { |
@@ -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(); | |||
} |
@@ -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(); | |||
} |
@@ -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(); |
@@ -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); | |||
} | |||
} |
@@ -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; |
@@ -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(); | |||