From dcd8a53c643bb731217c2dcad592379f526c5ae6 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Sat, 3 Dec 2016 20:32:08 +0000 Subject: [PATCH] regression #55902 - Mixed fonts issue with Chinese characters (unable to form images from ppt) git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1772485 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/sl/draw/DrawFontManager.java | 18 +++++ .../apache/poi/sl/draw/DrawTextParagraph.java | 81 +++++++++++++++---- src/java/org/apache/poi/sl/draw/Drawable.java | 17 +++- 3 files changed, 99 insertions(+), 17 deletions(-) diff --git a/src/java/org/apache/poi/sl/draw/DrawFontManager.java b/src/java/org/apache/poi/sl/draw/DrawFontManager.java index 9c49489ff..2416837b0 100644 --- a/src/java/org/apache/poi/sl/draw/DrawFontManager.java +++ b/src/java/org/apache/poi/sl/draw/DrawFontManager.java @@ -31,8 +31,26 @@ public interface DrawFontManager { * * @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 * * @return the font to be used to paint text */ String getRendererableFont(String typeface, int pitchFamily); + + /** + * 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 + * + * @return the font to be used as a fallback for the original typeface + */ + String getFallbackFont(String typeface, int pitchFamily); } diff --git a/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java b/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java index efdd7f4da..751e08985 100644 --- a/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java +++ b/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java @@ -18,6 +18,7 @@ package org.apache.poi.sl.draw; import java.awt.Dimension; +import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.font.FontRenderContext; @@ -500,6 +501,10 @@ public class DrawTextParagraph implements Drawable { PlaceableShape ps = getParagraphShape(); DrawFontManager fontHandler = (DrawFontManager)graphics.getRenderingHint(Drawable.FONT_HANDLER); + @SuppressWarnings("unchecked") + Map fontMap = (Map)graphics.getRenderingHint(Drawable.FONT_MAP); + @SuppressWarnings("unchecked") + Map fallbackMap = (Map)graphics.getRenderingHint(Drawable.FONT_FALLBACK); for (TextRun run : paragraph){ String runText = getRenderableText(run); @@ -507,31 +512,48 @@ public class DrawTextParagraph implements Drawable { if (runText.isEmpty()) continue; // user can pass an custom object to convert fonts - String fontFamily = run.getFontFamily(); - @SuppressWarnings("unchecked") - Map fontMap = (Map)graphics.getRenderingHint(Drawable.FONT_MAP); - if (fontMap != null && fontMap.containsKey(fontFamily)) { - fontFamily = fontMap.get(fontFamily); - } - if(fontHandler != null) { - fontFamily = fontHandler.getRendererableFont(fontFamily, run.getPitchAndFamily()); - } - if (fontFamily == null) { - fontFamily = paragraph.getDefaultFontFamily(); - } + 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 { + if (fontMap != null && fontMap.containsKey(mappedFont)) { + mappedFont = fontMap.get(mappedFont); + } + if (fallbackMap != null && fallbackMap.containsKey(mappedFont)) { + fallbackFont = fallbackMap.get(mappedFont); + } + } + + runText = mapFontCharset(runText,mappedFont); int beginIndex = text.length(); - text.append(mapFontCharset(runText,fontFamily)); + text.append(runText); int endIndex = text.length(); - attList.add(new AttributedStringData(TextAttribute.FAMILY, fontFamily, beginIndex, endIndex)); + 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)); Double fontSz = run.getFontSize(); - if (fontSz == null) fontSz = paragraph.getDefaultFontSize(); + if (fontSz == null) { + fontSz = paragraph.getDefaultFontSize(); + } attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz.floatValue(), beginIndex, endIndex)); if(run.isBold()) { @@ -559,6 +581,33 @@ public class DrawTextParagraph implements Drawable { 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 = f.canDisplayUpTo(textChr, 0, textChr.length); + int last = nextEnd; + boolean isNextValid = (nextEnd == 0); + while ( nextEnd != -1 && nextEnd <= textChr.length ) { + if (isNextValid) { + nextEnd = f.canDisplayUpTo(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); + } + } + } } // ensure that the paragraph contains at least one character @@ -576,7 +625,7 @@ public class DrawTextParagraph implements Drawable { return string; } - + protected boolean isHSLF() { return paragraph.getClass().getName().contains("HSLF"); } diff --git a/src/java/org/apache/poi/sl/draw/Drawable.java b/src/java/org/apache/poi/sl/draw/Drawable.java index 54128b82c..7df8533b5 100644 --- a/src/java/org/apache/poi/sl/draw/Drawable.java +++ b/src/java/org/apache/poi/sl/draw/Drawable.java @@ -106,10 +106,25 @@ public interface Drawable { int TEXT_AS_SHAPES = 2; /** - * Use this object to resolve unknown / missing fonts when rendering slides + * Use this object to resolve unknown / missing fonts when rendering slides. + * The font handler must be of type {@link DrawFontManager}.

+ * + * In case a {@code FONT_HANDLER} is register, {@code FONT_FALLBACK} and {@code FONT_MAP} are ignored */ DrawableHint FONT_HANDLER = new DrawableHint(7); + + /** + * Key for a font fallback map of type {@code Map} which maps + * the original font family (key) to the fallback font family (value). + * In case there is also a {@code FONT_MAP} registered, the original font + * is first mapped via the font_map and then the fallback font is determined + */ DrawableHint FONT_FALLBACK = new DrawableHint(8); + + /** + * Key for a font map of type {@code Map} which maps + * the original font family (key) to the mapped font family (value) + */ DrawableHint FONT_MAP = new DrawableHint(9); DrawableHint GSAVE = new DrawableHint(10);