From 1460fd33b2adff92edb4ef86caa5b9c75a12ade0 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Fri, 12 Dec 2014 01:43:45 +0000 Subject: [PATCH] Added workarounds to tests for JDK 6 LineBreakMeasurer bug and handle it git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1644806 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/hssf/usermodel/HSSFPicture.java | 14 ++-- src/java/org/apache/poi/util/JvmBugs.java | 51 ++++++++++++ .../poi/xslf/usermodel/XSLFTextParagraph.java | 45 +++++++--- .../poi/xslf/usermodel/TestPPTX2PNG.java | 27 ++++-- .../ss/usermodel/BaseTestBugzillaIssues.java | 1 + .../BaseTestSheetAutosizeColumn.java | 82 +++++++++++++------ 6 files changed, 169 insertions(+), 51 deletions(-) create mode 100644 src/java/org/apache/poi/util/JvmBugs.java diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java index 2d2eaa752..e7784ae42 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java @@ -20,6 +20,7 @@ package org.apache.poi.hssf.usermodel; import java.awt.Dimension; import java.io.ByteArrayInputStream; import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; import org.apache.poi.ddf.DefaultEscherRecordFactory; import org.apache.poi.ddf.EscherBSERecord; @@ -218,19 +219,14 @@ public class HSSFPicture extends HSSFSimpleShape implements Picture { } /** - * The color applied to the lines of this shape. + * The filename of the embedded image */ public String getFileName() { EscherComplexProperty propFile = (EscherComplexProperty) getOptRecord().lookup( EscherProperties.BLIP__BLIPFILENAME); - try { - if (null == propFile){ - return ""; - } - return new String(propFile.getComplexData(), "UTF-16LE").trim(); - } catch (UnsupportedEncodingException e) { - return ""; - } + return (null == propFile) + ? "" + : new String(propFile.getComplexData(), Charset.forName("UTF-16LE")).trim(); } public void setFileName(String data){ diff --git a/src/java/org/apache/poi/util/JvmBugs.java b/src/java/org/apache/poi/util/JvmBugs.java new file mode 100644 index 000000000..ecb7e3200 --- /dev/null +++ b/src/java/org/apache/poi/util/JvmBugs.java @@ -0,0 +1,51 @@ +/* ==================================================================== + 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.util; + +public class JvmBugs { + private static final POILogger LOG = POILogFactory.getLogger(JvmBugs.class); + + /** + * The LineBreakMeasurer is used for calculating text bounds. + * The last official JDK 6 version (1.6.0_45) and also JDK 7 (1.7.0_21) + * for Windows are affected. For JDK 7 - update to a more recent version. + * For JDK 6 - replace the fontmanager.dll with the previous release. + * + * For performance reasons, this method only checks for a windows jvm + * with version 1.6.0_45 and 1.7.0_21. + * + * Set system property "org.apache.poi.JvmBugs.LineBreakMeasurer.ignore" to "true" + * to bypass this check and use the normal fonts. + * + * @return true, if jvm is bugged, caller code should use Lucida Sans + * instead of Calibri and Lucida Bright instead of Cambria + * + * @see POI Bug #54904 + * @see JDK Bug #6501991 + * @see LineBreakMeasurerTest + */ + public static boolean hasLineBreakMeasurerBug() { + String version = System.getProperty("java.version"); + String os = System.getProperty("os.name").toLowerCase(); + boolean ignore = Boolean.getBoolean("org.apache.poi.JvmBugs.LineBreakMeasurer.ignore"); + boolean hasBug = (!ignore && (os.contains("win") && ("1.6.0_45".equals(version) || "1.7.0_21".equals(version)))); + if (hasBug) { + LOG.log(POILogger.WARN, "JVM has LineBreakMeasurer bug - see POI bug #54904 - caller code might default to Lucida Sans"); + } + return hasBug; + } +} diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java index 78efb49dd..e0b383a76 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java @@ -16,15 +16,6 @@ ==================================================================== */ package org.apache.poi.xslf.usermodel; -import org.apache.poi.util.Beta; -import org.apache.poi.util.Internal; -import org.apache.poi.util.Units; -import org.apache.poi.xslf.model.ParagraphPropertyFetcher; -import org.apache.xmlbeans.XmlObject; -import org.openxmlformats.schemas.drawingml.x2006.main.*; -import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; -import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; - import java.awt.Color; import java.awt.Graphics2D; import java.awt.font.LineBreakMeasurer; @@ -36,6 +27,35 @@ import java.text.AttributedString; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Map; + +import org.apache.poi.hslf.model.TextPainter; +import org.apache.poi.util.Beta; +import org.apache.poi.util.Internal; +import org.apache.poi.util.Units; +import org.apache.poi.xslf.model.ParagraphPropertyFetcher; +import org.apache.xmlbeans.XmlObject; +import org.openxmlformats.schemas.drawingml.x2006.main.CTColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun; +import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextAutonumberBullet; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBulletSizePercent; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBulletSizePoint; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharBullet; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextField; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextLineBreak; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextNormalAutofit; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextSpacing; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextTabStop; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextTabStopList; +import org.openxmlformats.schemas.drawingml.x2006.main.STTextAlignType; +import org.openxmlformats.schemas.drawingml.x2006.main.STTextAutonumberScheme; +import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; +import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; /** * Represents a paragraph of text within the containing text body. @@ -823,6 +843,11 @@ public class XSLFTextParagraph implements Iterable{ // user can pass an custom object to convert fonts String fontFamily = run.getFontFamily(); + @SuppressWarnings("unchecked") + Map fontMap = (Map)graphics.getRenderingHint(TextPainter.KEY_FONTMAP); + if (fontMap != null && fontMap.containsKey(fontFamily)) { + fontFamily = fontMap.get(fontFamily); + } if(fontHandler != null) { fontFamily = fontHandler.getRendererableFont(fontFamily, run.getPitchAndFamily()); } @@ -1016,7 +1041,7 @@ public class XSLFTextParagraph implements Iterable{ } } - private boolean fetchParagraphProperty(ParagraphPropertyFetcher visitor){ + private boolean fetchParagraphProperty(ParagraphPropertyFetcher visitor){ boolean ok = false; if(_p.isSetPPr()) ok = visitor.fetch(_p.getPPr()); 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 948b792b8..5c616cd43 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java @@ -19,20 +19,25 @@ package org.apache.poi.xslf.usermodel; -import junit.framework.TestCase; -import org.apache.poi.xslf.XSLFTestDataSamples; - import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.image.BufferedImage; +import java.util.HashMap; +import java.util.Map; + +import org.apache.poi.hslf.model.TextPainter; +import org.apache.poi.util.JvmBugs; +import org.apache.poi.xslf.XSLFTestDataSamples; +import org.junit.Test; /** * Date: 10/26/11 * * @author Yegor Kozlov */ -public class TestPPTX2PNG extends TestCase { - public void testRender(){ +public class TestPPTX2PNG { + @Test + public void render(){ String[] testFiles = {"layouts.pptx", "sample.pptx", "shapes.pptx", "themes.pptx", "backgrounds.pptx"}; for(String sampleFile : testFiles){ @@ -41,10 +46,20 @@ public class TestPPTX2PNG extends TestCase { for(XSLFSlide slide : pptx.getSlides()){ BufferedImage img = new BufferedImage(pg.width, pg.height, BufferedImage.TYPE_INT_RGB); Graphics2D graphics = img.createGraphics(); - + fixFonts(graphics); slide.draw(graphics); } } } + + @SuppressWarnings("unchecked") + private void fixFonts(Graphics2D graphics) { + if (!JvmBugs.hasLineBreakMeasurerBug()) return; + Map fontMap = (Map)graphics.getRenderingHint(TextPainter.KEY_FONTMAP); + if (fontMap == null) fontMap = new HashMap(); + fontMap.put("Calibri", "Lucida Sans"); + fontMap.put("Cambria", "Lucida Bright"); + graphics.setRenderingHint(TextPainter.KEY_FONTMAP, fontMap); + } } diff --git a/src/testcases/org/apache/poi/ss/usermodel/BaseTestBugzillaIssues.java b/src/testcases/org/apache/poi/ss/usermodel/BaseTestBugzillaIssues.java index fb9dd8ea9..fe11df658 100644 --- a/src/testcases/org/apache/poi/ss/usermodel/BaseTestBugzillaIssues.java +++ b/src/testcases/org/apache/poi/ss/usermodel/BaseTestBugzillaIssues.java @@ -334,6 +334,7 @@ public abstract class BaseTestBugzillaIssues { @Test public final void bug506819_testAutoSize() { Workbook wb = _testDataProvider.createWorkbook(); + BaseTestSheetAutosizeColumn.fixFonts(wb); Sheet sheet = wb.createSheet("Sheet1"); Row row = sheet.createRow(0); Cell cell0 = row.createCell(0); diff --git a/src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetAutosizeColumn.java b/src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetAutosizeColumn.java index d4b68a0c8..b1aeb637c 100644 --- a/src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetAutosizeColumn.java +++ b/src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetAutosizeColumn.java @@ -17,9 +17,12 @@ package org.apache.poi.ss.usermodel; -import junit.framework.TestCase; +import static org.junit.Assert.*; + import org.apache.poi.ss.ITestDataProvider; import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.util.JvmBugs; +import org.junit.Test; import java.util.Calendar; @@ -28,7 +31,7 @@ import java.util.Calendar; * * @author Yegor Kozlov */ -public abstract class BaseTestSheetAutosizeColumn extends TestCase { +public abstract class BaseTestSheetAutosizeColumn { private final ITestDataProvider _testDataProvider; @@ -36,23 +39,10 @@ public abstract class BaseTestSheetAutosizeColumn extends TestCase { _testDataProvider = testDataProvider; } - // TODO should we have this stuff in the FormulaEvaluator? - private void evaluateWorkbook(Workbook workbook){ - FormulaEvaluator eval = workbook.getCreationHelper().createFormulaEvaluator(); - for(int i=0; i < workbook.getNumberOfSheets(); i++) { - Sheet sheet = workbook.getSheetAt(i); - for (Row r : sheet) { - for (Cell c : r) { - if (c.getCellType() == Cell.CELL_TYPE_FORMULA){ - eval.evaluateFormulaCell(c); - } - } - } - } - } - - public void testNumericCells(){ + @Test + public void numericCells(){ Workbook workbook = _testDataProvider.createWorkbook(); + fixFonts(workbook); DataFormat df = workbook.getCreationHelper().createDataFormat(); Sheet sheet = workbook.createSheet(); @@ -89,8 +79,10 @@ public abstract class BaseTestSheetAutosizeColumn extends TestCase { assertEquals(sheet.getColumnWidth(4), sheet.getColumnWidth(5)); // 10.0000 and '10.0000' } - public void testBooleanCells(){ + @Test + public void booleanCells(){ Workbook workbook = _testDataProvider.createWorkbook(); + fixFonts(workbook); Sheet sheet = workbook.createSheet(); Row row = sheet.createRow(0); @@ -116,8 +108,10 @@ public abstract class BaseTestSheetAutosizeColumn extends TestCase { assertEquals(sheet.getColumnWidth(2), sheet.getColumnWidth(3)); // columns 1, 2 and 3 should have the same width } - public void testDateCells(){ + @Test + public void dateCells(){ Workbook workbook = _testDataProvider.createWorkbook(); + fixFonts(workbook); Sheet sheet = workbook.createSheet(); DataFormat df = workbook.getCreationHelper().createDataFormat(); @@ -180,11 +174,13 @@ public abstract class BaseTestSheetAutosizeColumn extends TestCase { assertEquals(sheet.getColumnWidth(4), sheet.getColumnWidth(7)); // date formula formatted as 'mmm' } - public void testStringCells(){ + @Test + public void stringCells(){ Workbook workbook = _testDataProvider.createWorkbook(); + fixFonts(workbook); Sheet sheet = workbook.createSheet(); Row row = sheet.createRow(0); - + Font defaultFont = workbook.getFontAt((short)0); CellStyle style1 = workbook.createCellStyle(); @@ -207,11 +203,14 @@ public abstract class BaseTestSheetAutosizeColumn extends TestCase { assertTrue(2*sheet.getColumnWidth(0) < sheet.getColumnWidth(1)); // width is roughly proportional to the number of characters assertTrue(2*sheet.getColumnWidth(1) < sheet.getColumnWidth(2)); assertEquals(sheet.getColumnWidth(4), sheet.getColumnWidth(3)); - assertTrue(sheet.getColumnWidth(5) > sheet.getColumnWidth(4)); //larger font results in a wider column width + boolean ignoreFontSizeX2 = JvmBugs.hasLineBreakMeasurerBug(); + assertTrue(ignoreFontSizeX2 || sheet.getColumnWidth(5) > sheet.getColumnWidth(4)); //larger font results in a wider column width } - public void testRotatedText(){ + @Test + public void rotatedText(){ Workbook workbook = _testDataProvider.createWorkbook(); + fixFonts(workbook); Sheet sheet = workbook.createSheet(); Row row = sheet.createRow(0); @@ -233,8 +232,10 @@ public abstract class BaseTestSheetAutosizeColumn extends TestCase { assertTrue(w0*5 < w1); // rotated text occupies at least five times less horizontal space than normal text } - public void testMergedCells(){ + @Test + public void mergedCells(){ Workbook workbook = _testDataProvider.createWorkbook(); + fixFonts(workbook); Sheet sheet = workbook.createSheet(); Row row = sheet.createRow(0); @@ -257,8 +258,10 @@ public abstract class BaseTestSheetAutosizeColumn extends TestCase { * Auto-Sizing a column needs to work when we have rows * passed the 32767 boundary. See bug #48079 */ - public void testLargeRowNumbers() throws Exception { + @Test + public void largeRowNumbers() throws Exception { Workbook workbook = _testDataProvider.createWorkbook(); + fixFonts(workbook); Sheet sheet = workbook.createSheet(); Row r0 = sheet.createRow(0); @@ -291,4 +294,31 @@ public abstract class BaseTestSheetAutosizeColumn extends TestCase { r60708.createCell(0).setCellValue("Near the end"); sheet.autoSizeColumn(0); } + + // TODO should we have this stuff in the FormulaEvaluator? + private void evaluateWorkbook(Workbook workbook){ + FormulaEvaluator eval = workbook.getCreationHelper().createFormulaEvaluator(); + for(int i=0; i < workbook.getNumberOfSheets(); i++) { + Sheet sheet = workbook.getSheetAt(i); + for (Row r : sheet) { + for (Cell c : r) { + if (c.getCellType() == Cell.CELL_TYPE_FORMULA){ + eval.evaluateFormulaCell(c); + } + } + } + } + } + + protected static void fixFonts(Workbook workbook) { + if (!JvmBugs.hasLineBreakMeasurerBug()) return; + for (int i=workbook.getNumberOfFonts()-1; i>=0; i--) { + Font f = workbook.getFontAt((short)0); + if ("Calibri".equals(f.getFontName())) { + f.setFontName("Lucida Sans"); + } else if ("Cambria".equals(f.getFontName())) { + f.setFontName("Lucida Bright"); + } + } + } } \ No newline at end of file