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
This commit is contained in:
Andreas Beeker 2016-12-03 20:32:08 +00:00
parent 610ce0bb98
commit dcd8a53c64
3 changed files with 99 additions and 17 deletions

View File

@ -31,8 +31,26 @@ public interface DrawFontManager {
* *
* @param typeface the font family as defined in the .pptx file. * @param typeface the font family as defined in the .pptx file.
* This can be unknown or missing in the graphic environment. * 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 * @return the font to be used to paint text
*/ */
String getRendererableFont(String typeface, int pitchFamily); 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);
} }

View File

@ -18,6 +18,7 @@
package org.apache.poi.sl.draw; package org.apache.poi.sl.draw;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Paint; import java.awt.Paint;
import java.awt.font.FontRenderContext; import java.awt.font.FontRenderContext;
@ -500,6 +501,10 @@ public class DrawTextParagraph implements Drawable {
PlaceableShape<?,?> ps = getParagraphShape(); PlaceableShape<?,?> ps = getParagraphShape();
DrawFontManager fontHandler = (DrawFontManager)graphics.getRenderingHint(Drawable.FONT_HANDLER); 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);
for (TextRun run : paragraph){ for (TextRun run : paragraph){
String runText = getRenderableText(run); String runText = getRenderableText(run);
@ -507,31 +512,48 @@ public class DrawTextParagraph implements Drawable {
if (runText.isEmpty()) continue; if (runText.isEmpty()) continue;
// user can pass an custom object to convert fonts // user can pass an custom object to convert fonts
String fontFamily = run.getFontFamily(); String mappedFont = run.getFontFamily();
@SuppressWarnings("unchecked") String fallbackFont = Font.SANS_SERIF;
Map<String,String> fontMap = (Map<String,String>)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();
}
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(); int beginIndex = text.length();
text.append(mapFontCharset(runText,fontFamily)); text.append(runText);
int endIndex = text.length(); 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(); PaintStyle fgPaintStyle = run.getFontColor();
Paint fgPaint = new DrawPaint(ps).getPaint(graphics, fgPaintStyle); Paint fgPaint = new DrawPaint(ps).getPaint(graphics, fgPaintStyle);
attList.add(new AttributedStringData(TextAttribute.FOREGROUND, fgPaint, beginIndex, endIndex)); attList.add(new AttributedStringData(TextAttribute.FOREGROUND, fgPaint, beginIndex, endIndex));
Double fontSz = run.getFontSize(); 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)); attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz.floatValue(), beginIndex, endIndex));
if(run.isBold()) { 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_HREF, hl.getAddress(), beginIndex, endIndex));
attList.add(new AttributedStringData(HYPERLINK_LABEL, hl.getLabel(), 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 // ensure that the paragraph contains at least one character
@ -576,7 +625,7 @@ public class DrawTextParagraph implements Drawable {
return string; return string;
} }
protected boolean isHSLF() { protected boolean isHSLF() {
return paragraph.getClass().getName().contains("HSLF"); return paragraph.getClass().getName().contains("HSLF");
} }

View File

@ -106,10 +106,25 @@ public interface Drawable {
int TEXT_AS_SHAPES = 2; 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}.<p>
*
* In case a {@code FONT_HANDLER} is register, {@code FONT_FALLBACK} and {@code FONT_MAP} are ignored
*/ */
DrawableHint FONT_HANDLER = new DrawableHint(7); DrawableHint FONT_HANDLER = new DrawableHint(7);
/**
* Key for a font fallback map of type {@code Map<String,String>} 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); DrawableHint FONT_FALLBACK = new DrawableHint(8);
/**
* Key for a font map of type {@code Map<String,String>} which maps
* the original font family (key) to the mapped font family (value)
*/
DrawableHint FONT_MAP = new DrawableHint(9); DrawableHint FONT_MAP = new DrawableHint(9);
DrawableHint GSAVE = new DrawableHint(10); DrawableHint GSAVE = new DrawableHint(10);