From 1d3ef4d81bcf5542ba4d2bb38fb0bb5c404b62f9 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Sun, 8 Nov 2020 21:30:54 +0000 Subject: [PATCH] #64867 - Provide PDF rendering with PPTX2PNG render text as text, i.e. not as shapes git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1883212 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/sl/draw/DrawTextParagraph.java | 92 ++- src/multimodule/ooxml/java9/module-info.class | Bin 2923 -> 3074 bytes src/multimodule/ooxml/java9/module-info.java | 7 + src/multimodule/ooxml/test9/module-info.class | Bin 3792 -> 3943 bytes src/multimodule/ooxml/test9/module-info.java | 8 + .../apache/poi/xslf/util/PDFFontMapper.java | 102 +++ .../apache/poi/xslf/util/PDFFontMapper2.java | 681 ++++++++++++++++++ .../org/apache/poi/xslf/util/PDFFormat.java | 19 +- .../org/apache/poi/xslf/util/PPTX2PNG.java | 25 +- .../poi/xslf/usermodel/TestPPTX2PNG.java | 4 + .../apache/poi/hwmf/draw/HwmfGraphics.java | 17 +- .../testcases/commons-logging.properties | 17 + src/scratchpad/testcases/log4j.properties | 4 +- 13 files changed, 933 insertions(+), 43 deletions(-) create mode 100644 src/ooxml/java/org/apache/poi/xslf/util/PDFFontMapper.java create mode 100644 src/ooxml/java/org/apache/poi/xslf/util/PDFFontMapper2.java create mode 100644 src/scratchpad/testcases/commons-logging.properties diff --git a/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java b/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java index c3e2693213..b3d70fff26 100644 --- a/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java +++ b/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java @@ -32,8 +32,10 @@ import java.text.AttributedCharacterIterator.Attribute; import java.text.AttributedString; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import org.apache.poi.common.usermodel.fonts.FontGroup; import org.apache.poi.common.usermodel.fonts.FontGroup.FontGroupRange; @@ -257,9 +259,18 @@ public class DrawTextParagraph implements Drawable { DrawFactory fact = DrawFactory.getInstance(graphics); StringBuilder text = new StringBuilder(); - AttributedString at = getAttributedString(graphics, text); - AttributedCharacterIterator it = at.getIterator(); + List attList = getAttributedString(graphics, text); + AttributedString as = new AttributedString(text.toString()); + AttributedString asNoCR = new AttributedString(text.toString().replaceAll("[\\r\\n]", " ")); + + for (AttributedStringData asd : attList) { + as.addAttribute(asd.attribute, asd.value, asd.beginIndex, asd.endIndex); + asNoCR.addAttribute(asd.attribute, asd.value, asd.beginIndex, asd.endIndex); + } + + AttributedCharacterIterator it = as.getIterator(); + AttributedCharacterIterator itNoCR = asNoCR.getIterator(); LineBreakMeasurer measurer = new LineBreakMeasurer(it, graphics.getFontRenderContext()); for (;;) { int startIndex = measurer.getPosition(); @@ -308,7 +319,7 @@ public class DrawTextParagraph implements Drawable { } } - AttributedString str = new AttributedString(it, startIndex, endIndex); + AttributedString str = new AttributedString(itNoCR, startIndex, endIndex); DrawTextFragment line = fact.getTextFragment(layout, str); lines.add(line); @@ -369,10 +380,14 @@ public class DrawTextParagraph implements Drawable { // TODO: check font group defaulting to Symbol buFont = dfm.getMappedFont(graphics, buFont); + Map att = new HashMap<>(); + att.put(TextAttribute.FOREGROUND, fgPaint); + att.put(TextAttribute.FAMILY, buFont.getTypeface()); + att.put(TextAttribute.SIZE, fontSize); + att.put(TextAttribute.FONT, new Font(att)); + AttributedString str = new AttributedString(dfm.mapFontCharset(graphics,buFont,buCharacter)); - str.addAttribute(TextAttribute.FOREGROUND, fgPaint); - str.addAttribute(TextAttribute.FAMILY, buFont.getTypeface()); - str.addAttribute(TextAttribute.SIZE, fontSize); + att.forEach(str::addAttribute); TextLayout layout = new TextLayout(str.getIterator(), graphics.getFontRenderContext()); DrawFactory fact = DrawFactory.getInstance(graphics); @@ -559,8 +574,7 @@ public class DrawTextParagraph implements Drawable { }; } - protected AttributedString getAttributedString(Graphics2D graphics, StringBuilder text){ - List attList = new ArrayList<>(); + protected List getAttributedString(Graphics2D graphics, StringBuilder text) { if (text == null) { text = new StringBuilder(); } @@ -569,6 +583,9 @@ public class DrawTextParagraph implements Drawable { DrawFontManager dfm = DrawFactory.getInstance(graphics).getFontManager(graphics); assert(dfm != null); + final Map att = new HashMap<>(); + final List attList = new ArrayList<>(); + for (TextRun run : paragraph){ String runText = getRenderableText(graphics, run); // skip empty runs @@ -576,66 +593,79 @@ public class DrawTextParagraph implements Drawable { continue; } - // user can pass an custom object to convert fonts + att.clear(); - runText = dfm.mapFontCharset(graphics, run.getFontInfo(null), runText); - int beginIndex = text.length(); + // user can pass an custom object to convert fonts + FontInfo fontInfo = run.getFontInfo(null); + runText = dfm.mapFontCharset(graphics, fontInfo, runText); + final int beginIndex = text.length(); text.append(runText); - int endIndex = text.length(); + final int endIndex = text.length(); PaintStyle fgPaintStyle = run.getFontColor(); Paint fgPaint = dp.getPaint(graphics, fgPaintStyle); - attList.add(new AttributedStringData(TextAttribute.FOREGROUND, fgPaint, beginIndex, endIndex)); + + att.put(TextAttribute.FOREGROUND, fgPaint); Double fontSz = run.getFontSize(); if (fontSz == null) { fontSz = paragraph.getDefaultFontSize(); } - attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz.floatValue(), beginIndex, endIndex)); + att.put(TextAttribute.SIZE, fontSz.floatValue()); if(run.isBold()) { - attList.add(new AttributedStringData(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, beginIndex, endIndex)); + att.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); } if(run.isItalic()) { - attList.add(new AttributedStringData(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE, beginIndex, endIndex)); + att.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); } if(run.isUnderlined()) { - attList.add(new AttributedStringData(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, beginIndex, endIndex)); - attList.add(new AttributedStringData(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_TWO_PIXEL, beginIndex, endIndex)); + att.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); + att.put(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_TWO_PIXEL); } if(run.isStrikethrough()) { - attList.add(new AttributedStringData(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, beginIndex, endIndex)); + att.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON); } if(run.isSubscript()) { - attList.add(new AttributedStringData(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB, beginIndex, endIndex)); + att.put(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB); } if(run.isSuperscript()) { - attList.add(new AttributedStringData(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, beginIndex, endIndex)); + att.put(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER); } 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)); + att.put(HYPERLINK_HREF, hl.getAddress()); + att.put(HYPERLINK_LABEL, hl.getLabel()); } + if (fontInfo != null) { + att.put(TextAttribute.FAMILY, fontInfo.getTypeface()); + } else { + att.put(TextAttribute.FAMILY, paragraph.getDefaultFontFamily()); + } + + att.put(TextAttribute.FONT, new Font(att)); + + att.forEach((k,v) -> attList.add(new AttributedStringData(k,v,beginIndex,endIndex))); + processGlyphs(graphics, dfm, attList, beginIndex, run, runText); } // ensure that the paragraph contains at least one character // We need this trick to correctly measure text if (text.length() == 0) { - Double fontSz = paragraph.getDefaultFontSize(); text.append(" "); - attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz.floatValue(), 0, 1)); + + Double fontSz = paragraph.getDefaultFontSize(); + att.put(TextAttribute.SIZE, fontSz.floatValue()); + att.put(TextAttribute.FAMILY, paragraph.getDefaultFontFamily()); + att.put(TextAttribute.FONT, new Font(att)); + + att.forEach((k,v) -> attList.add(new AttributedStringData(k,v,0,1))); } - AttributedString string = new AttributedString(text.toString()); - for (AttributedStringData asd : attList) { - string.addAttribute(asd.attribute, asd.value, asd.beginIndex, asd.endIndex); - } - - return string; + return attList; } /** diff --git a/src/multimodule/ooxml/java9/module-info.class b/src/multimodule/ooxml/java9/module-info.class index f88bd8cb64c275d22a4b88ce4865790f753791e9..51342739ec73c778add58fae3eb3132ebe62cee6 100644 GIT binary patch literal 3074 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 fonts = new HashMap<>(); + private final Set registered = new HashSet<>(); + + public PDFFontMapper(String fontDir, String fontTtf) { + registerFonts(fontDir, fontTtf); + } + + + private void registerFonts(String fontDir, String fontTtf) { + if (fontDir == null) { + String OS = System.getProperty("os.name", "generic").toLowerCase(Locale.ROOT); + if (OS.contains("mac") || OS.contains("darwin")) { + fontDir = FONTDIRS_MAC; + } else if (OS.contains("win")) { + fontDir = FONTDIRS_WIN; + } else { + fontDir = FONTDIRS_UNX; + } + } + + String fd = fontDir.replace("$HOME", System.getProperty("user.home")); + final LinkedList dirs = new LinkedList<>(); + Stream.of(fd.split(";")).map(File::new).filter(File::isDirectory).forEach(dirs::add); + + Pattern p = Pattern.compile(fontTtf == null ? DEFAULT_TTF_PATTERN : fontTtf); + + while (!dirs.isEmpty()) { + File[] ttfs = dirs.removeFirst().listFiles((f, n) -> { + File f2 = new File(f, n); + if (f2.isDirectory()) { + dirs.add(f2); + return false; + } else { + return p.matcher(n).matches(); + } + }); + + if (ttfs == null) { + continue; + } + + for (File f : ttfs) { + try { + Font font = Font.createFont(Font.TRUETYPE_FONT, f); + fonts.put(font.getFontName(Locale.ROOT), f); + } catch (IOException|FontFormatException ignored) { + } + + } + } + } + + @Override + protected PDFont mapFont(Font font, IFontTextDrawerEnv env) throws IOException, FontFormatException { + String name = font.getFontName(Locale.ROOT); + if (!registered.contains(name)) { + registered.add(name); + File f = fonts.get(name); + if (f != null) { + super.registerFont(name, f); + } + } + return super.mapFont(font, env); + } +} diff --git a/src/ooxml/java/org/apache/poi/xslf/util/PDFFontMapper2.java b/src/ooxml/java/org/apache/poi/xslf/util/PDFFontMapper2.java new file mode 100644 index 0000000000..58ac115bf3 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xslf/util/PDFFontMapper2.java @@ -0,0 +1,681 @@ +package org.apache.poi.xslf.util; + +import java.awt.Font; +import java.awt.FontFormatException; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Paint; +import java.awt.font.FontRenderContext; +import java.awt.font.LineMetrics; +import java.awt.font.TextAttribute; +import java.awt.geom.Rectangle2D; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.AttributedCharacterIterator; +import java.text.CharacterIterator; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import de.rototor.pdfbox.graphics2d.PdfBoxGraphics2DFontTextDrawer; +import de.rototor.pdfbox.graphics2d.PdfBoxGraphics2DFontTextDrawerDefaultFonts; +import org.apache.fontbox.ttf.TrueTypeCollection; +import org.apache.fontbox.ttf.TrueTypeFont; +import org.apache.pdfbox.io.IOUtils; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDType0Font; +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.apache.pdfbox.util.Matrix; +import org.apache.poi.util.Internal; + +/** + * Workaround class until PdfBoxGraphics2DFontTextDrawer is fixed + */ +@Internal +public class PDFFontMapper2 extends PdfBoxGraphics2DFontTextDrawer +{ + /** + * Close / delete all resources associated with this drawer. This mainly means + * deleting all temporary files. You can not use this object after a call to + * close. + *

+ * Calling close multiple times does nothing. + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + @Override + public void close() + { + for (File tempFile : tempFiles) + tempFile.delete(); + tempFiles.clear(); + fontFiles.clear(); + fontMap.clear(); + } + + private static class FontEntry + { + String overrideName; + File file; + } + + private final List fontFiles = new ArrayList(); + private final List tempFiles = new ArrayList(); + private final Map fontMap = new HashMap(); + + /** + * Register a font. If possible, try to use a font file, i.e. + * {@link #registerFont(String, File)}. This method will lead to the creation of + * a temporary file which stores the font data. + * + * @param fontName the name of the font to use. If null, the name is taken from the + * font. + * @param fontStream the input stream of the font. This file must be a ttf/otf file! + * You have to close the stream outside, this method will not close + * the stream. + * @throws IOException when something goes wrong with reading the font or writing the + * font to the content stream of the PDF: + */ + @SuppressWarnings("WeakerAccess") + public void registerFont(String fontName, InputStream fontStream) throws IOException + { + File fontFile = File.createTempFile("pdfboxgfx2dfont", ".ttf"); + FileOutputStream out = new FileOutputStream(fontFile); + try + { + IOUtils.copy(fontStream, out); + } + finally + { + out.close(); + } + fontFile.deleteOnExit(); + tempFiles.add(fontFile); + registerFont(fontName, fontFile); + } + + /** + * Register a font. + * + * @param fontName the name of the font to use. If null, the name is taken from the + * font. + * @param fontFile the font file. This file must exist for the live time of this + * object, as the font data will be read lazy on demand + */ + @SuppressWarnings("WeakerAccess") + public void registerFont(String fontName, File fontFile) + { + if (!fontFile.exists()) + throw new IllegalArgumentException("Font " + fontFile + " does not exist!"); + FontEntry entry = new FontEntry(); + entry.overrideName = fontName; + entry.file = fontFile; + fontFiles.add(entry); + } + + /** + * Override for registerFont(null,fontFile) + * + * @param fontFile the font file + */ + @SuppressWarnings("WeakerAccess") + public void registerFont(File fontFile) + { + registerFont(null, fontFile); + } + + /** + * Override for registerFont(null,fontStream) + * + * @param fontStream the font file + * @throws IOException when something goes wrong with reading the font or writing the + * font to the content stream of the PDF: + */ + @SuppressWarnings("WeakerAccess") + public void registerFont(InputStream fontStream) throws IOException + { + registerFont(null, fontStream); + } + + /** + * Register a font which is already associated with the PDDocument + * + * @param name the name of the font as returned by + * {@link java.awt.Font#getFontName()}. This name is used for the + * mapping the java.awt.Font to this PDFont. + * @param font the PDFont to use. This font must be loaded in the current + * document. + */ + @SuppressWarnings("WeakerAccess") + public void registerFont(String name, PDFont font) + { + fontMap.put(name, font); + } + + /** + * @return true if the font mapping is populated on demand. This is usually only + * the case if this class has been derived. The default implementation + * just checks for this. + */ + @SuppressWarnings("WeakerAccess") + protected boolean hasDynamicFontMapping() + { + return true; + } + + @Override + public boolean canDrawText(AttributedCharacterIterator iterator, IFontTextDrawerEnv env) + throws IOException, FontFormatException + { + /* + * When no font is registered we can not display the text using a font... + */ + if (fontMap.size() == 0 && fontFiles.size() == 0 && !hasDynamicFontMapping()) + return false; + + boolean run = true; + StringBuilder sb = new StringBuilder(); + while (run) + { + + Font attributeFont = (Font) iterator.getAttribute(TextAttribute.FONT); + if (attributeFont == null) + attributeFont = env.getFont(); + if (mapFont(attributeFont, env) == null) + return false; + + /* + * We can not do a Background on the text currently. + */ + if (iterator.getAttribute(TextAttribute.BACKGROUND) != null) + return false; + + boolean isStrikeThrough = TextAttribute.STRIKETHROUGH_ON + .equals(iterator.getAttribute(TextAttribute.STRIKETHROUGH)); + boolean isUnderline = TextAttribute.UNDERLINE_ON + .equals(iterator.getAttribute(TextAttribute.UNDERLINE)); + boolean isLigatures = TextAttribute.LIGATURES_ON + .equals(iterator.getAttribute(TextAttribute.LIGATURES)); + if (isStrikeThrough || isUnderline || isLigatures) + return false; + + run = iterateRun(iterator, sb); + String s = sb.toString(); + int l = s.length(); + for (int i = 0; i < l; ) + { + int codePoint = s.codePointAt(i); + switch (Character.getDirectionality(codePoint)) + { + /* + * We can handle normal LTR. + */ + case Character.DIRECTIONALITY_LEFT_TO_RIGHT: + case Character.DIRECTIONALITY_EUROPEAN_NUMBER: + case Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR: + case Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR: + case Character.DIRECTIONALITY_WHITESPACE: + case Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR: + case Character.DIRECTIONALITY_NONSPACING_MARK: + case Character.DIRECTIONALITY_BOUNDARY_NEUTRAL: + case Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR: + case Character.DIRECTIONALITY_SEGMENT_SEPARATOR: + case Character.DIRECTIONALITY_OTHER_NEUTRALS: + case Character.DIRECTIONALITY_ARABIC_NUMBER: + break; + case Character.DIRECTIONALITY_RIGHT_TO_LEFT: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE: + case Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT: + /* + * We can not handle this + */ + return false; + default: + /* + * Default: We can not handle this + */ + return false; + } + + if (!attributeFont.canDisplay(codePoint)) + return false; + + i += Character.charCount(codePoint); + } + } + return true; + } + + @Override + public void drawText(AttributedCharacterIterator iterator, IFontTextDrawerEnv env) + throws IOException, FontFormatException + { + PDPageContentStream contentStream = env.getContentStream(); + + contentStream.beginText(); + + Matrix textMatrix = new Matrix(); + textMatrix.scale(1, -1); + contentStream.setTextMatrix(textMatrix); + + StringBuilder sb = new StringBuilder(); + boolean run = true; + while (run) + { + + Font attributeFont = (Font) iterator.getAttribute(TextAttribute.FONT); + if (attributeFont == null) + attributeFont = env.getFont(); + + Number fontSize = ((Number) iterator.getAttribute(TextAttribute.SIZE)); + if (fontSize != null) + attributeFont = attributeFont.deriveFont(fontSize.floatValue()); + PDFont font = applyFont(attributeFont, env); + + Paint paint = (Paint) iterator.getAttribute(TextAttribute.FOREGROUND); + if (paint == null) + paint = env.getPaint(); + + boolean isStrikeThrough = TextAttribute.STRIKETHROUGH_ON + .equals(iterator.getAttribute(TextAttribute.STRIKETHROUGH)); + boolean isUnderline = TextAttribute.UNDERLINE_ON + .equals(iterator.getAttribute(TextAttribute.UNDERLINE)); + boolean isLigatures = TextAttribute.LIGATURES_ON + .equals(iterator.getAttribute(TextAttribute.LIGATURES)); + + run = iterateRun(iterator, sb); + String text = sb.toString(); + + /* + * Apply the paint + */ + env.applyPaint(paint, null); + + /* + * If we force the text write we may encounter situations where the font can not + * display the characters. PDFBox will throw an exception in this case. We will + * just silently ignore the text and not display it instead. + */ + try + { + showTextOnStream(env, contentStream, attributeFont, font, isStrikeThrough, + isUnderline, isLigatures, text); + } + catch (IllegalArgumentException e) + { + if (font instanceof PDType1Font && !font.isEmbedded()) + { + /* + * We tried to use a builtin default font, but it does not have the needed + * characters. So we use a embedded font as fallback. + */ + try + { + if (fallbackFontUnknownEncodings == null) + fallbackFontUnknownEncodings = findFallbackFont(env); + if (fallbackFontUnknownEncodings != null) + { + env.getContentStream().setFont(fallbackFontUnknownEncodings, + attributeFont.getSize2D()); + showTextOnStream(env, contentStream, attributeFont, + fallbackFontUnknownEncodings, isStrikeThrough, isUnderline, + isLigatures, text); + e = null; + } + } + catch (IllegalArgumentException e1) + { + e = e1; + } + } + + if (e != null) + System.err.println("PDFBoxGraphics: Can not map text " + text + " with font " + + attributeFont.getFontName() + ": " + e.getMessage()); + } + } + contentStream.endText(); + } + + @Override + public FontMetrics getFontMetrics(final Font f, IFontTextDrawerEnv env) + throws IOException, FontFormatException + { + final FontMetrics defaultMetrics = env.getCalculationGraphics().getFontMetrics(f); + final PDFont pdFont = mapFont(f, env); + /* + * By default we delegate to the buffered image based calculation. This is wrong + * as soon as we use the native PDF Box font, as those have sometimes different widths. + * + * But it is correct and fine as long as we use vector shapes. + */ + if (pdFont == null) + return defaultMetrics; + return new FontMetrics(f) + { + public int getDescent() + { + return defaultMetrics.getDescent(); + } + + public int getHeight() + { + return defaultMetrics.getHeight(); + } + + public int getMaxAscent() + { + return defaultMetrics.getMaxAscent(); + } + + public int getMaxDescent() + { + return defaultMetrics.getMaxDescent(); + } + + public boolean hasUniformLineMetrics() + { + return defaultMetrics.hasUniformLineMetrics(); + } + + public LineMetrics getLineMetrics(String str, Graphics context) + { + return defaultMetrics.getLineMetrics(str, context); + } + + public LineMetrics getLineMetrics(String str, int beginIndex, int limit, + Graphics context) + { + return defaultMetrics.getLineMetrics(str, beginIndex, limit, context); + } + + public LineMetrics getLineMetrics(char[] chars, int beginIndex, int limit, + Graphics context) + { + return defaultMetrics.getLineMetrics(chars, beginIndex, limit, context); + } + + public LineMetrics getLineMetrics(CharacterIterator ci, int beginIndex, int limit, + Graphics context) + { + return defaultMetrics.getLineMetrics(ci, beginIndex, limit, context); + } + + public Rectangle2D getStringBounds(String str, Graphics context) + { + return defaultMetrics.getStringBounds(str, context); + } + + public Rectangle2D getStringBounds(String str, int beginIndex, int limit, + Graphics context) + { + return defaultMetrics.getStringBounds(str, beginIndex, limit, context); + } + + public Rectangle2D getStringBounds(char[] chars, int beginIndex, int limit, + Graphics context) + { + return defaultMetrics.getStringBounds(chars, beginIndex, limit, context); + } + + public Rectangle2D getStringBounds(CharacterIterator ci, int beginIndex, int limit, + Graphics context) + { + return defaultMetrics.getStringBounds(ci, beginIndex, limit, context); + } + + public Rectangle2D getMaxCharBounds(Graphics context) + { + return defaultMetrics.getMaxCharBounds(context); + } + + @Override + public int getAscent() + { + return defaultMetrics.getAscent(); + } + + @Override + public int getMaxAdvance() + { + return defaultMetrics.getMaxAdvance(); + } + + @Override + public int getLeading() + { + return defaultMetrics.getLeading(); + } + + @Override + public FontRenderContext getFontRenderContext() + { + return defaultMetrics.getFontRenderContext(); + } + + @Override + public int charWidth(char ch) + { + char[] chars = { ch }; + return charsWidth(chars, 0, chars.length); + } + + @Override + public int charWidth(int codePoint) + { + char[] data = Character.toChars(codePoint); + return charsWidth(data, 0, data.length); + } + + @Override + public int charsWidth(char[] data, int off, int len) + { + return stringWidth(new String(data, off, len)); + } + + @Override + public int stringWidth(String str) + { + try + { + return (int) (pdFont.getStringWidth(str) / 1000 * f.getSize()); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + catch (IllegalArgumentException e) + { + /* + * We let unknown chars be handled with + */ + return defaultMetrics.stringWidth(str); + } + } + + @Override + public int[] getWidths() + { + try + { + int[] first256Widths = new int[256]; + for (int i = 0; i < first256Widths.length; i++) + first256Widths[i] = (int) (pdFont.getWidth(i) / 1000 * f.getSize()); + return first256Widths; + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + }; + } + + private PDFont fallbackFontUnknownEncodings; + + private PDFont findFallbackFont(IFontTextDrawerEnv env) throws IOException + { + /* + * We search for the right font in the system folders... We try to use + * LucidaSansRegular and if not found Arial, because this fonts often exists. We + * use the Java default font as fallback. + * + * Normally this method is only used and called if a default font misses some + * special characters, e.g. Hebrew or Arabic characters. + */ + String javaHome = System.getProperty("java.home", "."); + String javaFontDir = javaHome + "/lib/fonts"; + String windir = System.getenv("WINDIR"); + if (windir == null) + windir = javaFontDir; + File[] paths = new File[] { new File(new File(windir), "fonts"), + new File(System.getProperty("user.dir", ".")), new File("/Library/Fonts"), + new File("/usr/share/fonts/truetype"), new File("/usr/share/fonts/truetype/dejavu"), + new File("/usr/share/fonts/truetype/liberation"), + new File("/usr/share/fonts/truetype/noto"), new File(javaFontDir) }; + File foundFontFile = null; + for (String fontFileName : new String[] { "LucidaSansRegular.ttf", "arial.ttf", "Arial.ttf", + "DejaVuSans.ttf", "LiberationMono-Regular.ttf", "NotoSerif-Regular.ttf" }) + { + for (File path : paths) + { + File arialFile = new File(path, fontFileName); + if (arialFile.exists()) + { + foundFontFile = arialFile; + break; + } + } + if (foundFontFile != null) + break; + } + /* + * If we did not find any font, we can't do anything :( + */ + if (foundFontFile == null) + return null; + return PDType0Font.load(env.getDocument(), foundFontFile); + } + + private void showTextOnStream(IFontTextDrawerEnv env, PDPageContentStream contentStream, + Font attributeFont, PDFont font, boolean isStrikeThrough, boolean isUnderline, + boolean isLigatures, String text) throws IOException + { + if (isStrikeThrough || isUnderline) + { + // noinspection unused + float stringWidth = font.getStringWidth(text); + // noinspection unused + LineMetrics lineMetrics = attributeFont + .getLineMetrics(text, env.getFontRenderContext()); + /* + * TODO: We can not draw that yet, we must do that later. While in textmode its + * not possible to draw lines... + */ + } + // noinspection StatementWithEmptyBody + if (isLigatures) + { + /* + * No idea how to map this ... + */ + } + contentStream.showText(text); + } + + private PDFont applyFont(Font font, IFontTextDrawerEnv env) + throws IOException, FontFormatException + { + PDFont fontToUse = mapFont(font, env); + if (fontToUse == null) + { + /* + * If we have no font but are forced to apply a font, we just use the default + * builtin PDF font... + */ + fontToUse = PdfBoxGraphics2DFontTextDrawerDefaultFonts.chooseMatchingHelvetica(font); + } + env.getContentStream().setFont(fontToUse, font.getSize2D()); + return fontToUse; + } + + /** + * Try to map the java.awt.Font to a PDFont. + * + * @param font the java.awt.Font for which a mapping should be found + * @param env environment of the font mapper + * @return the PDFont or null if none can be found. + * @throws IOException when the font can not be loaded + * @throws FontFormatException when the font file can not be loaded + */ + @SuppressWarnings("WeakerAccess") + protected PDFont mapFont(final Font font, final IFontTextDrawerEnv env) + throws IOException, FontFormatException + { + /* + * If we have any font registering's, we must perform them now + */ + for (final FontEntry fontEntry : fontFiles) + { + if (fontEntry.overrideName == null) + { + Font javaFont = Font.createFont(Font.TRUETYPE_FONT, fontEntry.file); + fontEntry.overrideName = javaFont.getFontName(); + } + if (fontEntry.file.getName().toLowerCase(Locale.US).endsWith(".ttc")) + { + TrueTypeCollection collection = new TrueTypeCollection(fontEntry.file); + collection.processAllFonts(new TrueTypeCollection.TrueTypeFontProcessor() + { + @Override + public void process(TrueTypeFont ttf) throws IOException + { + PDFont pdFont = PDType0Font.load(env.getDocument(), ttf, true); + fontMap.put(fontEntry.overrideName, pdFont); + fontMap.put(pdFont.getName(), pdFont); + } + }); + } + else + { + /* + * We load the font using the file. + */ + PDFont pdFont = PDType0Font.load(env.getDocument(), fontEntry.file); + fontMap.put(fontEntry.overrideName, pdFont); + } + } + fontFiles.clear(); + + return fontMap.get(font.getFontName()); + } + + private boolean iterateRun(AttributedCharacterIterator iterator, StringBuilder sb) + { + sb.setLength(0); + + int charCount = iterator.getRunLimit() - iterator.getRunStart(); + while (charCount-- > 0) + { + char c = iterator.current(); + iterator.next(); + if (c == AttributedCharacterIterator.DONE) + { + return false; + } + else + { + sb.append(c); + } + } + return (iterator.getIndex() < iterator.getRunLimit()); + } + +} diff --git a/src/ooxml/java/org/apache/poi/xslf/util/PDFFormat.java b/src/ooxml/java/org/apache/poi/xslf/util/PDFFormat.java index 2ada1c738b..1b71fb3f7b 100644 --- a/src/ooxml/java/org/apache/poi/xslf/util/PDFFormat.java +++ b/src/ooxml/java/org/apache/poi/xslf/util/PDFFormat.java @@ -24,6 +24,7 @@ import java.io.File; import java.io.IOException; import de.rototor.pdfbox.graphics2d.PdfBoxGraphics2D; +import de.rototor.pdfbox.graphics2d.PdfBoxGraphics2DFontTextDrawer; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; @@ -36,17 +37,25 @@ public class PDFFormat implements OutputFormat { private final PDDocument document; private PDPageContentStream contentStream; private PdfBoxGraphics2D pdfBoxGraphics2D; + private PdfBoxGraphics2DFontTextDrawer fontTextDrawer; + + public PDFFormat(boolean textAsShapes, String fontDir, String fontTtf) { + if (!textAsShapes) { + fontTextDrawer = new PDFFontMapper(fontDir, fontTtf); + } - public PDFFormat() { document = new PDDocument(); } @Override - public Graphics2D addSlide(double width, double height) throws IOException { + public Graphics2D addSlide(double width, double height) throws IOException { PDPage page = new PDPage(new PDRectangle((float) width, (float) height)); document.addPage(page); contentStream = new PDPageContentStream(document, page); - pdfBoxGraphics2D = new PdfBoxGraphics2D(document, (float)width, (float)height); + pdfBoxGraphics2D = new PdfBoxGraphics2D(document, (float) width, (float) height); + if (fontTextDrawer != null) { + pdfBoxGraphics2D.setFontTextDrawer(fontTextDrawer); + } return pdfBoxGraphics2D; } @@ -67,5 +76,9 @@ public class PDFFormat implements OutputFormat { @Override public void close() throws IOException { document.close(); + if (fontTextDrawer != null) { + fontTextDrawer.close(); + } } + } diff --git a/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java b/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java index db034e8caa..232e29340d 100644 --- a/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java +++ b/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java @@ -74,7 +74,10 @@ public final class PPTX2PNG { " -textAsShapes text elements are saved as shapes in SVG, necessary for variable spacing\n" + " often found in math formulas\n" + " -charset sets the default charset to be used, defaults to Windows-1252\n" + - " -emfHeaderBounds force the usage of the emf header bounds to calculate the bounding box"; + " -emfHeaderBounds force the usage of the emf header bounds to calculate the bounding box\n" + + " -fontdir

(PDF only) font directories separated by \";\" - use $HOME for current users home dir\n" + + " defaults to the usual plattform directories\n" + + " -fontTtf (PDF only) regex to match the .ttf filenames"; System.out.println(msg); // no System.exit here, as we also run in junit tests! @@ -104,6 +107,8 @@ public final class PPTX2PNG { private boolean textAsShapes = false; private Charset charset = LocaleUtil.CHARSET_1252; private boolean emfHeaderBounds = false; + private String fontDir = null; + private String fontTtf = null; private PPTX2PNG() { } @@ -192,6 +197,22 @@ public final class PPTX2PNG { case "-emfheaderbounds": emfHeaderBounds = true; break; + case "-fontdir": + if (opt != null) { + fontDir = opt; + i++; + } else { + fontDir = null; + } + break; + case "-fontttf": + if (opt != null) { + fontTtf = opt; + i++; + } else { + fontTtf = null; + } + break; default: file = new File(args[i]); break; @@ -313,7 +334,7 @@ public final class PPTX2PNG { case "svg": return new SVGFormat(textAsShapes); case "pdf": - return new PDFFormat(); + return new PDFFormat(textAsShapes,fontDir,fontTtf); default: return new BitmapFormat(format); } diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java index ab59877dac..7717c5062f 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java @@ -49,6 +49,7 @@ public class TestPPTX2PNG { private static boolean xslfOnly; private static final POIDataSamples samples = POIDataSamples.getSlideShowInstance(); private static final File basedir = null; + private static final String files = "bug64693.pptx, 53446.ppt, alterman_security.ppt, alterman_security.pptx, KEY02.pptx, themes.pptx, " + "backgrounds.pptx, layouts.pptx, sample.pptx, shapes.pptx, 54880_chinese.ppt, keyframes.pptx," + @@ -74,6 +75,8 @@ public class TestPPTX2PNG { @Parameter public String pptFile; + + @Parameters(name="{0}") public static Collection data() { Function> fun = (basedir == null) ? Stream::of : @@ -105,6 +108,7 @@ public class TestPPTX2PNG { "-quiet", // "-charset", "GBK", // "-emfHeaderBounds", + // "-textAsShapes", "-fixside", "long", "-scale", "800" )); diff --git a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java index b849a55bfb..48790f8e0a 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java @@ -575,16 +575,18 @@ public class HwmfGraphics implements HwmfCharsetAware { } private void addAttributes(BiConsumer attributes, HwmfFont font, String typeface) { - attributes.accept(TextAttribute.FAMILY, typeface); - attributes.accept(TextAttribute.SIZE, getFontHeight(font)); + Map att = new HashMap<>(); + att.put(TextAttribute.FAMILY, typeface); + att.put(TextAttribute.SIZE, getFontHeight(font)); + if (font.isStrikeOut()) { - attributes.accept(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON); + att.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON); } if (font.isUnderline()) { - attributes.accept(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); + att.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); } if (font.isItalic()) { - attributes.accept(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); + att.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); } // convert font weight to awt font weight - usually a font weight of 400 is regarded as regular final int fw = font.getWeight(); @@ -595,7 +597,10 @@ public class HwmfGraphics implements HwmfCharsetAware { break; } } - attributes.accept(TextAttribute.WEIGHT, awtFW); + att.put(TextAttribute.WEIGHT, awtFW); + att.put(TextAttribute.FONT, new Font(att)); + + att.forEach(attributes); } private double getFontHeight(HwmfFont font) { diff --git a/src/scratchpad/testcases/commons-logging.properties b/src/scratchpad/testcases/commons-logging.properties new file mode 100644 index 0000000000..3b4d40d05e --- /dev/null +++ b/src/scratchpad/testcases/commons-logging.properties @@ -0,0 +1,17 @@ +# 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. + +org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger +log4j.configuration=log4j.properties \ No newline at end of file diff --git a/src/scratchpad/testcases/log4j.properties b/src/scratchpad/testcases/log4j.properties index ac2be68500..23d316d79c 100644 --- a/src/scratchpad/testcases/log4j.properties +++ b/src/scratchpad/testcases/log4j.properties @@ -18,4 +18,6 @@ log4j.rootLogger=ALL,CONSOLE log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.target=System.out log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout -log4j.appender.CONSOLE.layout.ConversionPattern=%d{dd.MM HH:mm:ss} %-30.30c %5p %m%n +log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %c %5p %m%n + +log4j.logger.org.apache.fontbox.ttf=INFO