FOP-3089: Switch cmap format to support iPhone

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop-pdf-images/trunk@1903561 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/java/org/apache/fop/render/pdf/pdfbox/MergeTTFonts.java b/src/java/org/apache/fop/render/pdf/pdfbox/MergeTTFonts.java
index e0dffd0..b7d5a54 100644
--- a/src/java/org/apache/fop/render/pdf/pdfbox/MergeTTFonts.java
+++ b/src/java/org/apache/fop/render/pdf/pdfbox/MergeTTFonts.java
@@ -18,7 +18,6 @@
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
@@ -352,54 +351,44 @@
         }
 
         for (Cmap cmap : cmaps) {
-            if (cmap.platformId == 1 && cmaps.size() == 1) {
-                writeUShort(6); //subtableFormat
-                int firstCode = -1;
-                int lastCode = -1;
-                List<Integer> codes = new ArrayList<Integer>();
-                for (Map.Entry<Integer, Integer> g : cmap.glyphIdToCharacterCode.entrySet()) {
-                    if (firstCode < 0) {
-                        firstCode = g.getKey();
-                    }
-                    while (lastCode > 0 && lastCode + 1 < g.getKey()) {
-                        codes.add(0);
-                        lastCode++;
-                    }
-                    codes.add(g.getValue());
-                    lastCode = g.getKey();
-                }
-                writeUShort((codes.size() * 2) + 6); //length
-                writeUShort(0); //version
-                writeUShort(firstCode); //firstCode
-                writeUShort(codes.size()); //entryCount
-                for (int i : codes) {
-                    writeUShort(i);
-                }
-            } else {
-                writeUShort(12); //subtableFormat
-                writeUShort(0);
-                writeULong(currentPos, (cmap.glyphIdToCharacterCode.size() * 12) + 16);
-                currentPos += 4;
-                writeULong(currentPos, 0);
-                currentPos += 4;
-                writeULong(currentPos, cmap.glyphIdToCharacterCode.size());
-                currentPos += 4;
-
-                for (Map.Entry<Integer, Integer> g : cmap.glyphIdToCharacterCode.entrySet()) {
-                    writeULong(currentPos, g.getKey());
-                    currentPos += 4;
-                    writeULong(currentPos, g.getKey());
-                    currentPos += 4;
-                    writeULong(currentPos, g.getValue());
-                    currentPos += 4;
-                }
+            writeUShort(4); //subtableFormat
+            int segCount = cmap.glyphIdToCharacterCode.size() + 1;
+            writeUShort(16 + (segCount * 8)); //length
+            writeUShort(0); //lang
+            writeUShort(segCount * 2); //segCountX2
+            double searchRange = Math.pow(2, Math.floor(logBase2(segCount))) * 2;
+            writeUShort((int) searchRange); //searchRange
+            double entrySelector = Math.floor(logBase2(segCount));
+            writeUShort((int) entrySelector); //entrySelector
+            double rangeShift = (segCount * 2) - searchRange;
+            writeUShort((int) rangeShift); //rangeShift
+            for (int c : cmap.glyphIdToCharacterCode.keySet()) {
+                writeUShort(c); //endCode
             }
+            writeUShort(0xFFFF);
+            writeUShort(0); //reservedPad
+            for (int c : cmap.glyphIdToCharacterCode.keySet()) {
+                writeUShort(c); //startCode
+            }
+            writeUShort(0);
+            for (Map.Entry<Integer, Integer> entry : cmap.glyphIdToCharacterCode.entrySet()) {
+                writeUShort(entry.getValue() - entry.getKey()); //idDelta
+            }
+            writeUShort(0);
+            for (int g : cmap.glyphIdToCharacterCode.keySet()) {
+                writeUShort(0); //idRangeOffsets
+            }
+            writeUShort(0);
         }
 
         updateCheckSum(checksum, currentPos - cmapPos, OFTableName.CMAP);
         realSize += currentPos - cmapPos;
     }
 
+    private int logBase2(int n) {
+        return (int)(Math.log(n) / Math.log(2));
+    }
+
     private void mergeUniCmap(List<Cmap> cmaps) {
         Cmap uniCmap = null;
         for (Cmap cmap : cmaps) {
@@ -418,7 +407,8 @@
         int result = 0;
         for (int i = 0; i < index; i++) {
             Cmap curCmap = cmaps.get(i);
-            result += (curCmap.glyphIdToCharacterCode.size() * 12) + 16;
+            int segCount = curCmap.glyphIdToCharacterCode.size() + 1;
+            result += 16 + (segCount * 8); //length
         }
         return result;
     }
diff --git a/test/java/org/apache/fop/render/pdf/FOPPDFSingleMultiByteFontTestCase.java b/test/java/org/apache/fop/render/pdf/FOPPDFSingleMultiByteFontTestCase.java
index 78ed269..13877c7 100644
--- a/test/java/org/apache/fop/render/pdf/FOPPDFSingleMultiByteFontTestCase.java
+++ b/test/java/org/apache/fop/render/pdf/FOPPDFSingleMultiByteFontTestCase.java
@@ -16,6 +16,7 @@
  */
 package org.apache.fop.render.pdf;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Set;
@@ -29,6 +30,9 @@
 import org.apache.fontbox.cff.CFFParser;
 import org.apache.fontbox.ttf.GlyphData;
 import org.apache.fontbox.ttf.GlyphTable;
+import org.apache.fontbox.ttf.TTFParser;
+import org.apache.fontbox.ttf.TTFTable;
+import org.apache.fontbox.ttf.TrueTypeFont;
 import org.apache.fontbox.type1.Type1Font;
 import org.apache.pdfbox.cos.COSArray;
 import org.apache.pdfbox.cos.COSDictionary;
@@ -137,7 +141,14 @@
         Assert.assertEquals(name, "TimesNewRomanPSMT_TrueType");
         Assert.assertEquals(mbfont.getFontName(), "TimesNewRomanPSMT_TrueType");
         byte[] is = IOUtils.toByteArray(mbfont.getInputStream());
-        Assert.assertEquals(is.length, 41112);
+        Assert.assertEquals(is.length, 41104);
+
+        TrueTypeFont trueTypeFont = new TTFParser().parse(new ByteArrayInputStream(is));
+        TTFTable ttfTable = trueTypeFont.getTableMap().get("cmap");
+        ByteArrayInputStream bis = new ByteArrayInputStream(is);
+        bis.skip(ttfTable.getOffset() + 21);
+        Assert.assertEquals(bis.read(), 4); //subtableFormat
+
         doc.close();
         doc2.close();
     }