You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ki...@apache.org on 2014/02/12 00:16:54 UTC

svn commit: r1567455 - in /poi: site/src/documentation/content/xdocs/ trunk/ trunk/src/scratchpad/src/org/apache/poi/hslf/model/ trunk/src/scratchpad/testcases/org/apache/poi/hslf/ trunk/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/ trunk/tes...

Author: kiwiwings
Date: Tue Feb 11 23:16:54 2014
New Revision: 1567455

URL: http://svn.apache.org/r1567455
Log:
Bug 55902 - Mixed fonts issue with Chinese characters (unable to form images from ppt)

Added:
    poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestFontRendering.java
    poi/trunk/test-data/slideshow/bug55902-mixedChars.png   (with props)
    poi/trunk/test-data/slideshow/bug55902-mixedFontChineseCharacters.ppt   (with props)
Modified:
    poi/site/src/documentation/content/xdocs/status.xml
    poi/trunk/build.xml
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java
    poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/AllHSLFTests.java
    poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/AllHSLFUserModelTests.java

Modified: poi/site/src/documentation/content/xdocs/status.xml
URL: http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/status.xml?rev=1567455&r1=1567454&r2=1567455&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/status.xml (original)
+++ poi/site/src/documentation/content/xdocs/status.xml Tue Feb 11 23:16:54 2014
@@ -36,6 +36,7 @@
 
     <changes>
         <release version="3.11-beta1" date="2014-??-??">
+          <action dev="poi-developers" type="fix">55902 - Mixed fonts issue with Chinese characters (unable to form images from ppt)</action>
           <action dev="poi-developers" type="add">56022 - XSSF Event Text Extractor header/footer support</action>
           <action dev="poi-developers" type="fix">53282 - Hyperlink with a non-breaking space throws java.lang.IllegalStateException</action>
           <action dev="poi-developers" type="fix">55802 - Special Letters not exported correct</action>

Modified: poi/trunk/build.xml
URL: http://svn.apache.org/viewvc/poi/trunk/build.xml?rev=1567455&r1=1567454&r2=1567455&view=diff
==============================================================================
--- poi/trunk/build.xml (original)
+++ poi/trunk/build.xml Tue Feb 11 23:16:54 2014
@@ -834,7 +834,7 @@ under the License.
         </uptodate>
     </target>
 
-    <target name="test-scratchpad" depends="compile-main,compile-scratchpad,-test-scratchpad-check,jacocotask"
+    <target name="test-scratchpad" depends="compile-main,compile-scratchpad,-test-scratchpad-check,jacocotask,test-scratchpad-download-resources"
             unless="scratchpad.test.notRequired" xmlns:jacoco="antlib:org.jacoco.ant">
         <jacoco:coverage enabled="${coverage.enabled}" excludes="${coverage.excludes}" destfile="build/jacoco-scratchpad.exec">
             <junit printsummary="yes" fork="yes" forkmode="once" haltonfailure="${halt.on.test.failure}"
@@ -1383,7 +1383,6 @@ under the License.
     </target>
 	
 	<target name="findbugs"><!-- depends="assemble" -->
-		
         <antcall target="downloadfile">
             <param name="sourcefile" value="http://prdownloads.sourceforge.net/findbugs/findbugs-noUpdateChecks-2.0.3.zip?download"/>
             <param name="destfile" value="${main.lib}/findbugs-noUpdateChecks-2.0.3.zip"/>
@@ -1425,4 +1424,26 @@ under the License.
 			<sourcePath path="src/scratchpad/src" />
 		</findbugs>		
 	</target>
+
+	<target name="test-scratchpad-download-resources">
+		<mkdir dir="build/scratchpad-test-resources"/>
+		
+        <antcall target="downloadfile">
+            <param name="sourcefile" value="http://sourceforge.net/projects/monafont/files/monafont/monafont-2.90/monafont-ttf-2.90.zip/download"/>
+            <param name="destfile" value="build/scratchpad-test-resources/monafont-ttf-2.90.zip"/>
+        </antcall>
+
+		<unzip src="build/scratchpad-test-resources/monafont-ttf-2.90.zip"
+		       dest="build/scratchpad-test-resources">
+		    <patternset>
+		        <include name="mona.ttf"/>
+		    </patternset>
+		</unzip>
+		
+        <antcall target="downloadfile">
+            <param name="sourcefile" value="https://googlefontdirectory.googlecode.com/hg-history/c5955de4df3e40f6ab705bbccbd1f5ad93998287/cabin/Cabin-Regular.ttf"/>
+            <param name="destfile" value="build/scratchpad-test-resources/Cabin-Regular.ttf"/>
+        </antcall>
+	</target>
+	
 </project>

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java?rev=1567455&r1=1567454&r2=1567455&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java Tue Feb 11 23:16:54 2014
@@ -20,6 +20,7 @@ package org.apache.poi.hslf.model;
 import java.awt.Color;
 import java.awt.Font;
 import java.awt.Graphics2D;
+import java.awt.RenderingHints;
 import java.awt.font.FontRenderContext;
 import java.awt.font.LineBreakMeasurer;
 import java.awt.font.TextAttribute;
@@ -31,6 +32,7 @@ import java.text.AttributedCharacterIter
 import java.text.AttributedString;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.poi.hslf.record.TextRulerAtom;
 import org.apache.poi.hslf.usermodel.RichTextRun;
@@ -43,6 +45,9 @@ import org.apache.poi.util.POILogger;
  * @author Yegor Kozlov
  */
 public final class TextPainter {
+    public static final Key KEY_FONTFALLBACK = new Key(50, "Font fallback map");
+    public static final Key KEY_FONTMAP = new Key(51, "Font map");
+    
     protected POILogger logger = POILogFactory.getLogger(this.getClass());
 
     /**
@@ -58,10 +63,14 @@ public final class TextPainter {
         _shape = shape;
     }
 
+    public AttributedString getAttributedString(TextRun txrun) {
+        return getAttributedString(txrun, null);
+    }
+    
     /**
      * Convert the underlying set of rich text runs into java.text.AttributedString
      */
-    public AttributedString getAttributedString(TextRun txrun){
+    public AttributedString getAttributedString(TextRun txrun, Graphics2D graphics){
         String text = txrun.getText();
         //TODO: properly process tabs
         text = text.replace('\t', ' ');
@@ -77,7 +86,22 @@ public final class TextPainter {
                 continue;
             }
 
-            at.addAttribute(TextAttribute.FAMILY, rt[i].getFontName(), start, end);
+            String mappedFont = rt[i].getFontName();
+            String fallbackFont = Font.SANS_SERIF;
+            if (graphics != null) {
+                @SuppressWarnings("unchecked")
+                Map<String,String> fontMap = (Map<String,String>)graphics.getRenderingHint(KEY_FONTMAP);
+                if (fontMap != null && fontMap.containsKey(mappedFont)) {
+                    mappedFont = fontMap.get(mappedFont);
+                }
+                @SuppressWarnings("unchecked")
+                Map<String,String> fallbackMap = (Map<String,String>)graphics.getRenderingHint(KEY_FONTFALLBACK);
+                if (fallbackMap != null && fallbackMap.containsKey(mappedFont)) {
+                    fallbackFont = fallbackMap.get(mappedFont);
+                }
+            }
+            
+            at.addAttribute(TextAttribute.FAMILY, mappedFont, start, end);
             at.addAttribute(TextAttribute.SIZE, new Float(rt[i].getFontSize()), start, end);
             at.addAttribute(TextAttribute.FOREGROUND, rt[i].getFontColor(), start, end);
             if(rt[i].isBold()) at.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, start, end);
@@ -89,7 +113,31 @@ public final class TextPainter {
             if(rt[i].isStrikethrough()) at.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, start, end);
             int superScript = rt[i].getSuperscript();
             if(superScript != 0) at.addAttribute(TextAttribute.SUPERSCRIPT, superScript > 0 ? TextAttribute.SUPERSCRIPT_SUPER : TextAttribute.SUPERSCRIPT_SUB, start, end);
-
+            
+            
+            int style = (rt[i].isBold() ? Font.BOLD : 0) | (rt[i].isItalic() ? Font.ITALIC : 0);
+            Font f = new Font(mappedFont, style, rt[i].getFontSize());
+            
+            // check for unsupported characters and add a fallback font for these
+            char textChr[] = text.toCharArray();
+            int nextEnd = f.canDisplayUpTo(textChr, start, end);
+            boolean isNextValid = nextEnd == start;
+            for (int last = start; nextEnd != -1 && nextEnd <= end; ) {
+                if (isNextValid) {
+                    nextEnd = f.canDisplayUpTo(textChr, nextEnd, end);
+                    isNextValid = false;
+                } else {
+                    if (nextEnd >= end || f.canDisplay(Character.codePointAt(textChr, nextEnd, end)) ) {
+                        at.addAttribute(TextAttribute.FAMILY, fallbackFont, last, Math.min(nextEnd,end));
+                        if (nextEnd >= end) break;
+                        last = nextEnd;
+                        isNextValid = true;
+                    } else {
+                        boolean isHS = Character.isHighSurrogate(textChr[nextEnd]);
+                        nextEnd+=(isHS?2:1);
+                    }
+                }
+            }            
         }
         return at;
     }
@@ -98,7 +146,7 @@ public final class TextPainter {
         AffineTransform tx = graphics.getTransform();
 
         Rectangle2D anchor = _shape.getLogicalAnchor2D();
-        TextElement[] elem = getTextElements((float)anchor.getWidth(), graphics.getFontRenderContext());
+        TextElement[] elem = getTextElements((float)anchor.getWidth(), graphics.getFontRenderContext(), graphics);
         if(elem == null) return;
 
         float textHeight = 0;
@@ -183,13 +231,17 @@ public final class TextPainter {
     }
 
     public TextElement[] getTextElements(float textWidth, FontRenderContext frc){
+        return getTextElements(textWidth, frc, null);
+    }
+    
+    public TextElement[] getTextElements(float textWidth, FontRenderContext frc, Graphics2D graphics){
         TextRun run = _shape.getTextRun();
         if (run == null) return null;
 
         String text = run.getText();
         if (text == null || text.equals("")) return null;
 
-        AttributedString at = getAttributedString(run);
+        AttributedString at = getAttributedString(run, graphics);
 
         AttributedCharacterIterator it = at.getIterator();
         int paragraphStart = it.getBeginIndex();
@@ -342,4 +394,25 @@ public final class TextPainter {
         public float advance;
         public int textStartIndex, textEndIndex;
     }
+
+    public static class Key extends RenderingHints.Key {
+      String description;
+
+      public Key(int paramInt, String paramString) {
+        super(paramInt);
+        this.description = paramString;
+      }
+
+      public final int getIndex() {
+        return intKey();
+      }
+
+      public final String toString() {
+        return this.description;
+      }
+
+      public boolean isCompatibleValue(Object paramObject) {
+        return true;
+      }
+    }
 }

Modified: poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/AllHSLFTests.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/AllHSLFTests.java?rev=1567455&r1=1567454&r2=1567455&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/AllHSLFTests.java (original)
+++ poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/AllHSLFTests.java Tue Feb 11 23:16:54 2014
@@ -17,35 +17,30 @@
 
 package org.apache.poi.hslf;
 
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
 import org.apache.poi.hslf.extractor.TestCruddyExtractor;
 import org.apache.poi.hslf.extractor.TestExtractor;
 import org.apache.poi.hslf.model.AllHSLFModelTests;
 import org.apache.poi.hslf.record.AllHSLFRecordTests;
 import org.apache.poi.hslf.usermodel.AllHSLFUserModelTests;
 import org.apache.poi.hslf.util.TestSystemTimeUtils;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
 
 /**
  * Collects all tests from the package <tt>org.apache.poi.hslf</tt> and all sub-packages.
- * 
- * @author Josh Micich
  */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+    TestEncryptedFile.class,
+    TestRecordCounts.class,
+    TestReWrite.class,
+    TestReWriteSanity.class,
+    TestCruddyExtractor.class,
+    TestExtractor.class,
+    AllHSLFModelTests.class,
+    AllHSLFRecordTests.class,
+    AllHSLFUserModelTests.class,
+    TestSystemTimeUtils.class
+})
 public class AllHSLFTests {
-	
-	public static Test suite() {
-		TestSuite result = new TestSuite(AllHSLFTests.class.getName());
-		result.addTestSuite(TestEncryptedFile.class);
-		result.addTestSuite(TestRecordCounts.class);
-		result.addTestSuite(TestReWrite.class);
-		result.addTestSuite(TestReWriteSanity.class);
-		result.addTestSuite(TestCruddyExtractor.class);
-		result.addTestSuite(TestExtractor.class);
-		result.addTest(AllHSLFModelTests.suite());
-		result.addTest(AllHSLFRecordTests.suite());
-		result.addTest(AllHSLFUserModelTests.suite());
-		result.addTestSuite(TestSystemTimeUtils.class);
-		return result;
-	}
 }

Modified: poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/AllHSLFUserModelTests.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/AllHSLFUserModelTests.java?rev=1567455&r1=1567454&r2=1567455&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/AllHSLFUserModelTests.java (original)
+++ poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/AllHSLFUserModelTests.java Tue Feb 11 23:16:54 2014
@@ -17,30 +17,27 @@
 
 package org.apache.poi.hslf.usermodel;
 
-import junit.framework.Test;
-import junit.framework.TestSuite;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
 
 /**
  * Collects all tests from the package <tt>org.apache.poi.hslf.usermodel</tt>.
- * 
- * @author Josh Micich
  */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+    TestAddingSlides.class,
+    TestBugs.class,
+    TestCounts.class,
+    TestMostRecentRecords.class,
+    TestNotesText.class,
+    TestPictures.class,
+    TestReOrderingSlides.class,
+    TestRecordSetup.class,
+    TestRichTextRun.class,
+    TestSheetText.class,
+    TestSlideOrdering.class,
+    TestSoundData.class,
+    TestFontRendering.class
+})
 public class AllHSLFUserModelTests {
-	
-	public static Test suite() {
-		TestSuite result = new TestSuite(AllHSLFUserModelTests.class.getName());
-		result.addTestSuite(TestAddingSlides.class);
-		result.addTestSuite(TestBugs.class);
-		result.addTestSuite(TestCounts.class);
-		result.addTestSuite(TestMostRecentRecords.class);
-		result.addTestSuite(TestNotesText.class);
-		result.addTestSuite(TestPictures.class);
-		result.addTestSuite(TestReOrderingSlides.class);
-		result.addTestSuite(TestRecordSetup.class);
-		result.addTestSuite(TestRichTextRun.class);
-		result.addTestSuite(TestSheetText.class);
-		result.addTestSuite(TestSlideOrdering.class);
-		result.addTestSuite(TestSoundData.class);
-		return result;
-	}
 }

Added: poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestFontRendering.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestFontRendering.java?rev=1567455&view=auto
==============================================================================
--- poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestFontRendering.java (added)
+++ poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestFontRendering.java Tue Feb 11 23:16:54 2014
@@ -0,0 +1,112 @@
+/* ====================================================================
+   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.hslf.usermodel;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.GraphicsEnvironment;
+import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferByte;
+import java.io.File;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.imageio.ImageIO;
+
+import org.apache.poi.POIDataSamples;
+import org.apache.poi.hslf.model.Slide;
+import org.apache.poi.hslf.model.TextPainter;
+import org.junit.Test;
+
+/**
+ * Test font rendering of alternative and fallback fonts
+ */
+public class TestFontRendering {
+    private static POIDataSamples slTests = POIDataSamples.getSlideShowInstance();
+
+    @Test
+    public void bug55902mixedFontWithChineseCharacters() throws Exception {
+        // font files need to be downloaded first via
+        // ant test-scratchpad-download-resources
+        String fontFiles[][] = {
+            // Calibri is not available on *nix systems, so we need to use another similar free font
+            { "build/scratchpad-test-resources/Cabin-Regular.ttf", "mapped", "Calibri" },
+
+            // use "MS PGothic" if available (Windows only) ...
+            // for the junit test not all chars are rendered
+            { "build/scratchpad-test-resources/mona.ttf", "fallback", "Cabin" }
+        };
+        
+        // setup fonts (especially needed, when run under *nix systems)
+        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+        Map<String,String> fontMap = new HashMap<String,String>();
+        Map<String,String> fallbackMap = new HashMap<String,String>();
+        
+        for (String fontFile[] : fontFiles) {
+            File f = new File(fontFile[0]);
+            assumeTrue("necessary font file "+f.getName()+" not downloaded.", f.exists());
+            
+            Font font = Font.createFont(Font.TRUETYPE_FONT, f);
+            ge.registerFont(font);
+            
+            Map<String,String> map = ("mapped".equals(fontFile[1]) ? fontMap : fallbackMap);
+            map.put(fontFile[2], font.getFamily());
+        }
+        
+        InputStream is = slTests.openResourceAsStream("bug55902-mixedFontChineseCharacters.ppt");
+        SlideShow ss = new SlideShow(is);
+        is.close();
+        
+        Dimension pgsize = ss.getPageSize();
+        
+        Slide slide = ss.getSlides()[0];
+        
+        // render it
+        double zoom = 1;
+        AffineTransform at = new AffineTransform();
+        at.setToScale(zoom, zoom);
+        
+        BufferedImage imgActual = new BufferedImage((int)Math.ceil(pgsize.width*zoom), (int)Math.ceil(pgsize.height*zoom), BufferedImage.TYPE_3BYTE_BGR);
+        Graphics2D graphics = imgActual.createGraphics();
+        graphics.setRenderingHint(TextPainter.KEY_FONTFALLBACK, fallbackMap);
+        graphics.setRenderingHint(TextPainter.KEY_FONTMAP, fontMap);
+        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+        graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+        graphics.setTransform(at);                
+        graphics.setPaint(Color.white);
+        graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height));
+        slide.draw(graphics);             
+        
+        BufferedImage imgExpected = ImageIO.read(slTests.getFile("bug55902-mixedChars.png"));
+        DataBufferByte expectedDB = (DataBufferByte)imgExpected.getRaster().getDataBuffer();
+        DataBufferByte actualDB = (DataBufferByte)imgActual.getRaster().getDataBuffer();
+        assertTrue(Arrays.equals(expectedDB.getData(0), actualDB.getData(0)));
+    }
+}

Added: poi/trunk/test-data/slideshow/bug55902-mixedChars.png
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/slideshow/bug55902-mixedChars.png?rev=1567455&view=auto
==============================================================================
Binary file - no diff available.

Propchange: poi/trunk/test-data/slideshow/bug55902-mixedChars.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: poi/trunk/test-data/slideshow/bug55902-mixedFontChineseCharacters.ppt
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/slideshow/bug55902-mixedFontChineseCharacters.ppt?rev=1567455&view=auto
==============================================================================
Binary file - no diff available.

Propchange: poi/trunk/test-data/slideshow/bug55902-mixedFontChineseCharacters.ppt
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org