FOP-2975: Put composite glyphs to separate font

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1882341 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/fop-core/src/main/java/org/apache/fop/render/pcl/fonts/PCLSoftFontManager.java b/fop-core/src/main/java/org/apache/fop/render/pcl/fonts/PCLSoftFontManager.java
index 4261658..410971e 100644
--- a/fop-core/src/main/java/org/apache/fop/render/pcl/fonts/PCLSoftFontManager.java
+++ b/fop-core/src/main/java/org/apache/fop/render/pcl/fonts/PCLSoftFontManager.java
@@ -31,7 +31,12 @@
 
 import org.apache.fop.fonts.CustomFont;
 import org.apache.fop.fonts.Typeface;
+import org.apache.fop.fonts.truetype.GlyfTable;
+import org.apache.fop.fonts.truetype.OFDirTabEntry;
+import org.apache.fop.fonts.truetype.OFMtxEntry;
+import org.apache.fop.fonts.truetype.OFTableName;
 import org.apache.fop.render.java2d.CustomFontMetricsMapper;
+import org.apache.fop.util.CharUtilities;
 
 public class PCLSoftFontManager {
     private Map<Typeface, PCLFontReader> fontReaderMap;
@@ -45,11 +50,11 @@
     }
 
     public ByteArrayOutputStream makeSoftFont(Typeface font, String text) throws IOException {
-        List<Map<Character, Integer>> mappedGlyphs = mapFontGlyphs(font);
         if (!fontReaderMap.containsKey(font)) {
             fontReaderMap.put(font, PCLFontReaderFactory.createInstance(font));
         }
         fontReader = fontReaderMap.get(font);
+        List<Map<Character, Integer>> mappedGlyphs = mapFontGlyphs(font);
         if (mappedGlyphs.isEmpty()) {
             mappedGlyphs.add(new HashMap<Character, Integer>());
         }
@@ -95,7 +100,7 @@
         return f;
     }
 
-    private List<Map<Character, Integer>> mapFontGlyphs(Typeface tf) {
+    private List<Map<Character, Integer>> mapFontGlyphs(Typeface tf) throws IOException {
         List<Map<Character, Integer>> mappedGlyphs = new ArrayList<Map<Character, Integer>>();
         if (tf instanceof CustomFontMetricsMapper) {
             CustomFontMetricsMapper fontMetrics = (CustomFontMetricsMapper) tf;
@@ -105,10 +110,13 @@
         return mappedGlyphs;
     }
 
-    private List<Map<Character, Integer>> mapGlyphs(Map<Integer, Integer> usedGlyphs, CustomFont font) {
+    private List<Map<Character, Integer>> mapGlyphs(Map<Integer, Integer> usedGlyphs, CustomFont font)
+        throws IOException {
         int charCount = 32;
+        int charCountComposite = 32;
         List<Map<Character, Integer>> mappedGlyphs = new ArrayList<Map<Character, Integer>>();
         Map<Character, Integer> fontGlyphs = new HashMap<Character, Integer>();
+        Map<Character, Integer> fontGlyphsComposite = new HashMap<Character, Integer>();
         for (Entry<Integer, Integer> entry : usedGlyphs.entrySet()) {
             int glyphID = entry.getKey();
             if (glyphID == 0) {
@@ -120,14 +128,38 @@
                 charCount = 32;
                 fontGlyphs = new HashMap<Character, Integer>();
             }
-            fontGlyphs.put(unicode, charCount++);
+            if (isComposite(font, unicode)) {
+                fontGlyphsComposite.put(unicode, charCountComposite++);
+            } else {
+                fontGlyphs.put(unicode, charCount++);
+            }
         }
         if (fontGlyphs.size() > 0) {
             mappedGlyphs.add(fontGlyphs);
         }
+        if (fontGlyphsComposite.size() > 0) {
+            mappedGlyphs.add(fontGlyphsComposite);
+        }
         return mappedGlyphs;
     }
 
+    private boolean isComposite(CustomFont customFont, int unicode) throws IOException {
+        OFDirTabEntry glyfTableInfo = fontReader.getFontFile().getDirectoryEntry(OFTableName.GLYF);
+        if (glyfTableInfo == null) {
+            return false;
+        }
+        List<OFMtxEntry> mtx = fontReader.getFontFile().getMtx();
+        Map<Integer, Integer> subsetGlyphs = customFont.getUsedGlyphs();
+        GlyfTable glyfTable = new GlyfTable(fontReader.getFontFileReader(), mtx.toArray(new OFMtxEntry[mtx.size()]),
+                glyfTableInfo, subsetGlyphs);
+        Map<Integer, Integer> mtxCharacters = fontReader.scanMtxCharacters();
+        if (mtxCharacters.containsKey(unicode)) {
+            int mtxChar = mtxCharacters.get(unicode);
+            return glyfTable.isComposite(mtxChar);
+        }
+        return false;
+    }
+
     private void writeFontID(int fontID, OutputStream os) throws IOException {
         os.write(assignFontID(fontID));
     }
@@ -256,6 +288,9 @@
         int curFontID = -1;
         String current = "";
         for (char ch : text.toCharArray()) {
+            if (ch == CharUtilities.NBSPACE) {
+                ch = ' ';
+            }
             for (PCLSoftFont softFont : fonts) {
                 if (curFontID == -1) {
                     curFontID = softFont.getFontID();
diff --git a/fop-core/src/main/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFFontReader.java b/fop-core/src/main/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFFontReader.java
index 6b2fc23..f578a9b 100644
--- a/fop-core/src/main/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFFontReader.java
+++ b/fop-core/src/main/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFFontReader.java
@@ -22,7 +22,6 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -553,7 +552,7 @@
     }
 
     private void writeTrueTypeTable(ByteArrayOutputStream baos, OFTableName table,
-            List<TableOffset> tableOffsets) throws IOException, UnsupportedEncodingException {
+            List<TableOffset> tableOffsets) throws IOException {
         OFDirTabEntry tabEntry = ttfFont.getDirectoryEntry(table);
         if (tabEntry != null) {
             baos.write(tabEntry.getTag());
@@ -566,7 +565,7 @@
         }
     }
 
-    private void writeGDIR(ByteArrayOutputStream baos) throws UnsupportedEncodingException, IOException {
+    private void writeGDIR(ByteArrayOutputStream baos) throws IOException {
         baos.write("gdir".getBytes("ISO-8859-1"));
         baos.write(PCLByteWriterUtil.unsignedLongInt(0)); // Checksum
         baos.write(PCLByteWriterUtil.unsignedLongInt(0)); // Offset
@@ -695,28 +694,24 @@
         return (int) sum;
     }
 
-    protected byte[] createHmtx(Map<Character, Integer> mappedGlyphs) throws IOException {
+    protected byte[] createHmtx(Map<Character, Integer> mappedGlyphs) {
         byte[] hmtxTable = new byte[((mappedGlyphs.size() + 32) * 4)];
         OFDirTabEntry entry = ttfFont.getDirectoryEntry(OFTableName.HMTX);
-
         if (entry != null) {
             for (Entry<Character, Integer> glyphSubset : mappedGlyphs.entrySet()) {
                 char unicode = glyphSubset.getKey();
-                int originalIndex = 0;
                 int softFontGlyphIndex = glyphSubset.getValue();
                 if (font instanceof MultiByteFont) {
-                    originalIndex = ((MultiByteFont) font).getGIDFromChar(unicode);
-
-                    writeUShort(hmtxTable, (softFontGlyphIndex) * 4,
+                    int originalIndex = ((MultiByteFont) font).getGIDFromChar(unicode);
+                    writeUShort(hmtxTable, softFontGlyphIndex * 4,
                             ttfFont.getMtx().get(originalIndex).getWx());
-                    writeUShort(hmtxTable, (softFontGlyphIndex) * 4 + 2,
+                    writeUShort(hmtxTable, softFontGlyphIndex * 4 + 2,
                             ttfFont.getMtx().get(originalIndex).getLsb());
                 } else {
-                    originalIndex = ((SingleByteFont) font).getGIDFromChar(unicode);
-
-                    writeUShort(hmtxTable, (softFontGlyphIndex) * 4,
+                    int originalIndex = ((SingleByteFont) font).getGIDFromChar(unicode);
+                    writeUShort(hmtxTable, softFontGlyphIndex * 4,
                             font.getWidth(originalIndex, 1));
-                    writeUShort(hmtxTable, (softFontGlyphIndex) * 4 + 2, 0);
+                    writeUShort(hmtxTable, softFontGlyphIndex * 4 + 2, 0);
                 }
             }
         }
diff --git a/fop-core/src/test/java/org/apache/fop/render/pcl/fonts/PCLSoftFontManagerTestCase.java b/fop-core/src/test/java/org/apache/fop/render/pcl/fonts/PCLSoftFontManagerTestCase.java
new file mode 100644
index 0000000..e52fed6
--- /dev/null
+++ b/fop-core/src/test/java/org/apache/fop/render/pcl/fonts/PCLSoftFontManagerTestCase.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+package org.apache.fop.render.pcl.fonts;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.net.URI;
+import java.util.HashMap;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.FopFactory;
+import org.apache.fop.fonts.CMapSegment;
+import org.apache.fop.fonts.EmbeddingMode;
+import org.apache.fop.fonts.FontType;
+import org.apache.fop.fonts.MultiByteFont;
+import org.apache.fop.render.java2d.CustomFontMetricsMapper;
+
+public class PCLSoftFontManagerTestCase {
+    @Test
+    public void testSplitCompositeGlyphs() throws Exception {
+        FOUserAgent ua = FopFactory.newInstance(new File(".").toURI()).newFOUserAgent();
+        PCLSoftFontManager pclSoftFontManager = new PCLSoftFontManager(new HashMap());
+        MultiByteFont mbf = new MultiByteFont(ua.getResourceResolver(), EmbeddingMode.SUBSET);
+        mbf.setEmbedURI(new URI("test/resources/fonts/ttf/DejaVuLGCSerif.ttf"));
+        mbf.setFontType(FontType.TRUETYPE);
+        CMapSegment cMapSegment1 = new CMapSegment('a', 'a', 1);
+        CMapSegment cMapSegment2 = new CMapSegment('\u00E0', '\u00E0', 2);
+        mbf.setCMap(new CMapSegment[] {cMapSegment1, cMapSegment2});
+        mbf.mapChar('a');
+        mbf.mapChar('\u00E0');
+        CustomFontMetricsMapper font = new CustomFontMetricsMapper(mbf);
+        ByteArrayOutputStream bos = pclSoftFontManager.makeSoftFont(font, "");
+        String[] fontStrings = bos.toString().split("DejaVu changes are in public domain");
+        Assert.assertEquals(fontStrings.length, 3);
+    }
+}