*\r
* @param typeface the font family as defined in the .pptx file.\r
* This can be unknown or missing in the graphic environment.\r
+ * @param pitchFamily a pitch-and-family,\r
+ * see {@link org.apache.poi.hwmf.record.HwmfFont#getFamily()} and\r
+ * {@link org.apache.poi.hwmf.record.HwmfFont#getPitch()}\r
+ * for how to calculate those (ancient) values\r
*\r
* @return the font to be used to paint text\r
*/\r
String getRendererableFont(String typeface, int pitchFamily);\r
+\r
+ /**\r
+ * In case the original font doesn't contain a glyph, use the\r
+ * returned fallback font as an alternative\r
+ *\r
+ * @param typeface the font family as defined in the .pptx file.\r
+ * @param pitchFamily a pitch-and-family,\r
+ * see {@link org.apache.poi.hwmf.record.HwmfFont#getFamily()} and\r
+ * {@link org.apache.poi.hwmf.record.HwmfFont#getPitch()}\r
+ * for how to calculate those (ancient) values\r
+ * \r
+ * @return the font to be used as a fallback for the original typeface\r
+ */\r
+ String getFallbackFont(String typeface, int pitchFamily);\r
}\r
package org.apache.poi.sl.draw;\r
\r
import java.awt.Dimension;\r
+import java.awt.Font;\r
import java.awt.Graphics2D;\r
import java.awt.Paint;\r
import java.awt.font.FontRenderContext;\r
PlaceableShape<?,?> ps = getParagraphShape();\r
\r
DrawFontManager fontHandler = (DrawFontManager)graphics.getRenderingHint(Drawable.FONT_HANDLER);\r
+ @SuppressWarnings("unchecked")\r
+ Map<String,String> fontMap = (Map<String,String>)graphics.getRenderingHint(Drawable.FONT_MAP);\r
+ @SuppressWarnings("unchecked")\r
+ Map<String,String> fallbackMap = (Map<String,String>)graphics.getRenderingHint(Drawable.FONT_FALLBACK);\r
\r
for (TextRun run : paragraph){\r
String runText = getRenderableText(run);\r
if (runText.isEmpty()) continue;\r
\r
// user can pass an custom object to convert fonts\r
- String fontFamily = run.getFontFamily();\r
- @SuppressWarnings("unchecked")\r
- Map<String,String> fontMap = (Map<String,String>)graphics.getRenderingHint(Drawable.FONT_MAP);\r
- if (fontMap != null && fontMap.containsKey(fontFamily)) {\r
- fontFamily = fontMap.get(fontFamily);\r
- }\r
- if(fontHandler != null) {\r
- fontFamily = fontHandler.getRendererableFont(fontFamily, run.getPitchAndFamily());\r
+ String mappedFont = run.getFontFamily();\r
+ String fallbackFont = Font.SANS_SERIF;\r
+\r
+ if (mappedFont == null) {\r
+ mappedFont = paragraph.getDefaultFontFamily();\r
}\r
- if (fontFamily == null) {\r
- fontFamily = paragraph.getDefaultFontFamily();\r
+ if (mappedFont == null) {\r
+ mappedFont = Font.SANS_SERIF;\r
+ } \r
+ if (fontHandler != null) {\r
+ String font = fontHandler.getRendererableFont(mappedFont, run.getPitchAndFamily());\r
+ if (font != null) {\r
+ mappedFont = font;\r
+ }\r
+ font = fontHandler.getFallbackFont(mappedFont, run.getPitchAndFamily());\r
+ if (font != null) {\r
+ fallbackFont = font;\r
+ }\r
+ } else {\r
+ if (fontMap != null && fontMap.containsKey(mappedFont)) {\r
+ mappedFont = fontMap.get(mappedFont);\r
+ }\r
+ if (fallbackMap != null && fallbackMap.containsKey(mappedFont)) {\r
+ fallbackFont = fallbackMap.get(mappedFont);\r
+ }\r
}\r
-\r
+ \r
+ runText = mapFontCharset(runText,mappedFont);\r
int beginIndex = text.length();\r
- text.append(mapFontCharset(runText,fontFamily));\r
+ text.append(runText);\r
int endIndex = text.length();\r
\r
- attList.add(new AttributedStringData(TextAttribute.FAMILY, fontFamily, beginIndex, endIndex));\r
+ attList.add(new AttributedStringData(TextAttribute.FAMILY, mappedFont, beginIndex, endIndex));\r
\r
PaintStyle fgPaintStyle = run.getFontColor();\r
Paint fgPaint = new DrawPaint(ps).getPaint(graphics, fgPaintStyle);\r
attList.add(new AttributedStringData(TextAttribute.FOREGROUND, fgPaint, beginIndex, endIndex));\r
\r
Double fontSz = run.getFontSize();\r
- if (fontSz == null) fontSz = paragraph.getDefaultFontSize();\r
+ if (fontSz == null) {\r
+ fontSz = paragraph.getDefaultFontSize();\r
+ }\r
attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz.floatValue(), beginIndex, endIndex));\r
\r
if(run.isBold()) {\r
attList.add(new AttributedStringData(HYPERLINK_HREF, hl.getAddress(), beginIndex, endIndex));\r
attList.add(new AttributedStringData(HYPERLINK_LABEL, hl.getLabel(), beginIndex, endIndex));\r
}\r
+ \r
+ int style = (run.isBold() ? Font.BOLD : 0) | (run.isItalic() ? Font.ITALIC : 0);\r
+ Font f = new Font(mappedFont, style, (int)Math.rint(fontSz));\r
+ \r
+ // check for unsupported characters and add a fallback font for these\r
+ char textChr[] = runText.toCharArray();\r
+ int nextEnd = f.canDisplayUpTo(textChr, 0, textChr.length);\r
+ int last = nextEnd;\r
+ boolean isNextValid = (nextEnd == 0);\r
+ while ( nextEnd != -1 && nextEnd <= textChr.length ) {\r
+ if (isNextValid) {\r
+ nextEnd = f.canDisplayUpTo(textChr, nextEnd, textChr.length);\r
+ isNextValid = false;\r
+ } else {\r
+ if (nextEnd >= textChr.length || f.canDisplay(Character.codePointAt(textChr, nextEnd, textChr.length)) ) {\r
+ attList.add(new AttributedStringData(TextAttribute.FAMILY, fallbackFont, beginIndex+last, beginIndex+Math.min(nextEnd,textChr.length)));\r
+ if (nextEnd >= textChr.length) {\r
+ break;\r
+ }\r
+ last = nextEnd;\r
+ isNextValid = true;\r
+ } else {\r
+ boolean isHS = Character.isHighSurrogate(textChr[nextEnd]);\r
+ nextEnd+=(isHS?2:1);\r
+ }\r
+ }\r
+ } \r
}\r
\r
// ensure that the paragraph contains at least one character\r
\r
return string;\r
}\r
-\r
+ \r
protected boolean isHSLF() {\r
return paragraph.getClass().getName().contains("HSLF");\r
}\r
int TEXT_AS_SHAPES = 2;\r
\r
/**\r
- * Use this object to resolve unknown / missing fonts when rendering slides\r
+ * Use this object to resolve unknown / missing fonts when rendering slides.\r
+ * The font handler must be of type {@link DrawFontManager}.<p>\r
+ * \r
+ * In case a {@code FONT_HANDLER} is register, {@code FONT_FALLBACK} and {@code FONT_MAP} are ignored \r
*/\r
DrawableHint FONT_HANDLER = new DrawableHint(7);\r
+ \r
+ /**\r
+ * Key for a font fallback map of type {@code Map<String,String>} which maps\r
+ * the original font family (key) to the fallback font family (value).\r
+ * In case there is also a {@code FONT_MAP} registered, the original font\r
+ * is first mapped via the font_map and then the fallback font is determined\r
+ */\r
DrawableHint FONT_FALLBACK = new DrawableHint(8);\r
+\r
+ /**\r
+ * Key for a font map of type {@code Map<String,String>} which maps\r
+ * the original font family (key) to the mapped font family (value)\r
+ */\r
DrawableHint FONT_MAP = new DrawableHint(9);\r
\r
DrawableHint GSAVE = new DrawableHint(10);\r