Index: java/org/apache/fop/fonts/CIDFont.java
===================================================================
--- java/org/apache/fop/fonts/CIDFont.java	(revision 356250)
+++ java/org/apache/fop/fonts/CIDFont.java	(working copy)
@@ -1,12 +1,12 @@
 /*
  * Copyright 1999-2004 The Apache Software Foundation.
- * 
+ *
  * Licensed 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.
@@ -37,6 +37,11 @@
     public Map usedGlyphsIndex = new java.util.HashMap();
     public int usedGlyphsCount = 0;
 
+    /**
+     * usedCharsIndex contains new glyph, original char
+     */
+    public Map usedCharsIndex = new java.util.HashMap();
+
     //private PDFWArray warray = new PDFWArray();
     public int width[] = null;
 
@@ -90,4 +95,8 @@
         return true;
     }
 
+    /**
+     * Returns char[] array .
+     */
+    public abstract char[] getCharsUsed();
 }
\ No newline at end of file
Index: java/org/apache/fop/fonts/MultiByteFont.java
===================================================================
--- java/org/apache/fop/fonts/MultiByteFont.java	(revision 356250)
+++ java/org/apache/fop/fonts/MultiByteFont.java	(working copy)
@@ -1,12 +1,12 @@
 /*
  * Copyright 1999-2004 The Apache Software Foundation.
- * 
+ *
  * Licensed 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.
@@ -125,7 +125,7 @@
      * @see org.apache.fop.fonts.FontDescriptor#isEmbeddable()
      */
     public boolean isEmbeddable() {
-        return !(getEmbedFileName() == null && embedResourceName == null); 
+        return !(getEmbedFileName() == null && embedResourceName == null);
     }
 
     /**
@@ -198,8 +198,8 @@
         for (int i = 0; (i < bfentries.length) && retIdx == 0; i++) {
             if (bfentries[i].getUnicodeStart() <= idx
                     && bfentries[i].getUnicodeEnd() >= idx) {
-                        
-                retIdx = bfentries[i].getGlyphStartIndex() 
+
+                retIdx = bfentries[i].getGlyphStartIndex()
                     + idx
                     - bfentries[i].getUnicodeStart();
             }
@@ -222,6 +222,8 @@
                                new Integer(usedGlyphsCount));
                 usedGlyphsIndex.put(new Integer(usedGlyphsCount),
                                     new Integer(retIdx));
+                usedCharsIndex.put(new Integer(usedGlyphsCount),
+                                    new Integer((int) c));
                 retIdx = usedGlyphsCount;
                 usedGlyphsCount++;
             } else {
@@ -299,5 +301,26 @@
         return usedGlyphs;
     }
 
+    /** The invalid Unicode character, suitable as a return value in methods
+     * that need to return an invalid character. */
+    public static final char INVALID_UNICODE_CHAR = 0xFFFF;
+
+    public char[] getCharsUsed() {
+        if (! isEmbeddable()) {
+            return null;
+        }
+        char[] charArray = new char[usedGlyphsCount];
+        for (int i = 0; i < usedGlyphsCount; i++) {
+            Integer mapValue = (Integer)usedCharsIndex.get(new Integer(i));
+            if(mapValue != null) {
+                char arrayItem = (char) mapValue.intValue();
+                charArray[i] = arrayItem;
+            }
+            else {
+                charArray[i] = INVALID_UNICODE_CHAR;
+            }
+        }
+        return charArray;
+    }
 }
 
Index: java/org/apache/fop/fonts/SingleByteFont.java
===================================================================
--- java/org/apache/fop/fonts/SingleByteFont.java	(revision 356250)
+++ java/org/apache/fop/fonts/SingleByteFont.java	(working copy)
@@ -1,12 +1,12 @@
 /*
  * Copyright 1999-2004 The Apache Software Foundation.
- * 
+ *
  * Licensed 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.
@@ -77,7 +77,7 @@
             return '#';
         }
     }
-    
+
     /**
      * @see org.apache.fop.fonts.Typeface#hasChar(char)
      */
@@ -99,5 +99,8 @@
         this.width[index] = width;
     }
 
+    public char[] getCharsUsed() {
+        return null;
+    }
 }
 
Index: java/org/apache/fop/pdf/PDFCMap.java
===================================================================
--- java/org/apache/fop/pdf/PDFCMap.java	(revision 356250)
+++ java/org/apache/fop/pdf/PDFCMap.java	(working copy)
@@ -1,12 +1,12 @@
 /*
  * Copyright 1999-2004 The Apache Software Foundation.
- * 
+ *
  * Licensed 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.
@@ -15,9 +15,13 @@
  */
 
 /* $Id$ */
- 
+
 package org.apache.fop.pdf;
 
+// Java
+import java.io.IOException;
+import java.io.OutputStream;
+
 /**
  * Class representing the CMap encodings.
  *
@@ -423,53 +427,91 @@
      * @param p the string buffer to add the pdf data to
      */
     public void fillInPDF(StringBuffer p) {
+        writePreStream(p);
+        writeStreamComments(p);
+        writeCIDInit(p);
+        writeCIDSystemInfo(p);
+        writeVersionTypeName(p);
+        writeCodeSpaceRange(p);
+        writeCIDRange(p);
+        writeBFEntries(p);
+        writeWrapUp(p);
+        writeStreamAfterComments(p);
+        writeUseCMap(p);
+        add(p.toString());
+    }
+
+    protected void writePreStream(StringBuffer p) {
         // p.append("/Type /CMap\n");
         // p.append(sysInfo.toPDFString());
-        // p.append("/CMapName /" + name);
-        // p.append("\n");
+        // p.append("/CMapName /" + name + EOL);
+    }
+
+    protected void writeStreamComments(StringBuffer p) {
         p.append("%!PS-Adobe-3.0 Resource-CMap\n");
         p.append("%%DocumentNeededResources: ProcSet (CIDInit)\n");
         p.append("%%IncludeResource: ProcSet (CIDInit)\n");
         p.append("%%BeginResource: CMap (" + name + ")\n");
         p.append("%%EndComments\n");
+    }
 
+    protected void writeCIDInit(StringBuffer p) {
         p.append("/CIDInit /ProcSet findresource begin\n");
         p.append("12 dict begin\n");
         p.append("begincmap\n");
+    }
 
+    protected void writeCIDSystemInfo(StringBuffer p) {
         p.append("/CIDSystemInfo 3 dict dup begin\n");
         p.append("  /Registry (Adobe) def\n");
         p.append("  /Ordering (Identity) def\n");
         p.append("  /Supplement 0 def\n");
         p.append("end def\n");
+    }
 
+    protected void writeVersionTypeName(StringBuffer p) {
         p.append("/CMapVersion 1 def\n");
         p.append("/CMapType 1 def\n");
         p.append("/CMapName /" + name + " def\n");
+    }
 
+    protected void writeCodeSpaceRange(StringBuffer p) {
         p.append("1 begincodespacerange\n");
         p.append("<0000> <FFFF>\n");
         p.append("endcodespacerange\n");
+    }
+
+    protected void writeCIDRange(StringBuffer p) {
         p.append("1 begincidrange\n");
         p.append("<0000> <FFFF> 0\n");
         p.append("endcidrange\n");
+    }
 
+    protected void writeBFEntries(StringBuffer p) {
         // p.append("1 beginbfrange\n");
         // p.append("<0020> <0100> <0000>\n");
         // p.append("endbfrange\n");
+    }
 
+    protected void writeWrapUp(StringBuffer p) {
         p.append("endcmap\n");
         p.append("CMapName currentdict /CMap defineresource pop\n");
         p.append("end\n");
         p.append("end\n");
+    }
+
+    protected void writeStreamAfterComments(StringBuffer p) {
         p.append("%%EndResource\n");
         p.append("%%EOF\n");
+    }
+
+    protected void writeUseCMap(StringBuffer p) {
         /*
-         * p.append(" /Type /CMap\n/CMapName /" + name);
-         * p.append("\n");
-         * p.append("\n/WMode "); p.append(wMode);
+         * p.append(" /Type /CMap");
+         * p.append("/CMapName /" + name + EOL);
+         * p.append("/WMode " + wMode + EOL);
          * if (base != null) {
-         * p.append("\n/UseCMap ");
+         *     p.append("/UseCMap ");
          * if (base instanceof String) {
          * p.append("/"+base);
          * } else {// base instanceof PDFStream
@@ -479,4 +521,8 @@
          */
     }
 
+    protected int output(OutputStream stream) throws IOException {
+        fillInPDF(new StringBuffer());
+        return super.output(stream);
+    }
 }
Index: java/org/apache/fop/pdf/PDFFactory.java
===================================================================
--- java/org/apache/fop/pdf/PDFFactory.java	(revision 356250)
+++ java/org/apache/fop/pdf/PDFFactory.java	(working copy)
@@ -1,12 +1,12 @@
 /*
  * Copyright 1999-2005 The Apache Software Foundation.
- * 
+ *
  * Licensed 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.
@@ -43,6 +43,8 @@
 import org.apache.fop.fonts.truetype.TTFSubSetFile;
 import org.apache.fop.fonts.type1.PFBData;
 import org.apache.fop.fonts.type1.PFBParser;
+import org.apache.fop.pdf.PDFCMap;
+import org.apache.fop.pdf.PDFToUnicodeCMap;
 
 /**
  * This class provides method to create and register PDF objects.
@@ -918,7 +920,7 @@
      * @return the new PDF outline object
      */
     public PDFOutline makeOutline(PDFOutline parent, String label,
-                                  String destination, float yoffset, 
+                                  String destination, float yoffset,
                                   boolean showSubItems) {
 
         String goToRef = getGoToReference(destination, yoffset);
@@ -1021,6 +1023,12 @@
                                    (PDFCIDFontDescriptor)pdfdesc);
                 getDocument().registerObject(cidFont);
 
+                PDFCMap cmap = new PDFToUnicodeCMap(cidMetrics, "fop-ucs-H",
+                    new PDFCIDSystemInfo("Adobe",
+                        "Identity",
+                        0));
+                getDocument().registerObject(cmap);
+                ((PDFFontType0)font).setCMAP(cmap);
                 ((PDFFontType0)font).setDescendantFonts(cidFont);
             } else {
                 int firstChar = 0;
Index: java/org/apache/fop/pdf/PDFToUnicodeCMap.java
===================================================================
--- java/org/apache/fop/pdf/PDFToUnicodeCMap.java	(revision 0)
+++ java/org/apache/fop/pdf/PDFToUnicodeCMap.java	(revision 0)
@@ -0,0 +1,322 @@
+/*
+ * $Id: PDFToUnicodeCMap.java,v 1.3.2.1 2005/12/01 12:00:00 ono Exp $
+ * ============================================================================
+ *                    The Apache Software License, Version 1.1
+ * ============================================================================
+ *
+ * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modifica-
+ * tion, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if any, must
+ *    include the following acknowledgment: "This product includes software
+ *    developed by the Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself, if
+ *    and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "FOP" and "Apache Software Foundation" must not be used to
+ *    endorse or promote products derived from this software without prior
+ *    written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache", nor may
+ *    "Apache" appear in their name, without prior written permission of the
+ *    Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
+ * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ============================================================================
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * on behalf of the Apache Software Foundation and was originally created by
+ * James Tauber <jtauber@jtauber.com>. For more information on the Apache
+ * Software Foundation, please see <http://www.apache.org/>.
+ */
+package org.apache.fop.pdf;
+
+/**
+ * Class representing ToUnicode CMaps.
+ * Here are some documentation resources:
+ * <ul>
+ * <li>PDF Reference, Second Edition, Section 5.6.4, for general information
+ * about CMaps in PDF Files.</li>
+ * <li>PDF Reference, Second Edition, Section 5.9, for specific information
+ * about ToUnicodeCMaps in PDF Files.</li>
+ * <li>
+ * <a href="http://partners.adobe.com/asn/developer/pdfs/tn/5411.ToUnicode.pdf">
+ * Adobe Technical Note #5411, "ToUnicode Mapping File Tutorial"</a>.
+ * </ul>
+ */
+import org.apache.fop.fonts.CIDFont;
+
+public class PDFToUnicodeCMap extends PDFCMap {
+
+    /**
+     * handle to read font
+     */
+    protected CIDFont cidFont;
+
+    /**
+     * Constructor.
+     *
+     * @param name One of the registered names found in Table 5.14 in PDF
+     * Reference, Second Edition.
+     * @param sysInfo The attributes of the character collection of the CIDFont.
+     */
+    public PDFToUnicodeCMap(CIDFont cidMetrics, String name, PDFCIDSystemInfo sysInfo) {
+        super(name, sysInfo);
+        cidFont = cidMetrics;
+    }
+
+    public void fillInPDF(StringBuffer p) {
+        writeCIDInit(p);
+        writeCIDSystemInfo(p);
+        writeVersionTypeName(p);
+        writeCodeSpaceRange(p);
+        writeBFEntries(p);
+        writeWrapUp(p);
+        add(p.toString());
+    }
+
+    protected void writeCIDSystemInfo(StringBuffer p) {
+        p.append("/CIDSystemInfo\n");
+        p.append("<< /Registry (Adobe)\n");
+        p.append("/Ordering (UCS)\n");
+        p.append("/Supplement 0\n");
+        p.append(">> def\n");
+    }
+
+    protected void writeVersionTypeName(StringBuffer p) {
+        p.append("/CMapName /Adobe-Identity-UCS def\n");
+        p.append("/CMapType 2 def\n");
+    }
+
+    /**
+     * Writes the character mappings for this font.
+     */
+    protected void writeBFEntries(StringBuffer p) {
+        if(cidFont == null) return;
+
+        char[] charArray = cidFont.getCharsUsed();
+
+        if(charArray != null) {
+            writeBFCharEntries(p, charArray);
+            writeBFRangeEntries(p, charArray);
+        }
+    }
+
+    protected void writeBFCharEntries(StringBuffer p, char[] charArray) {
+        int completedEntries = 0;
+        int totalEntries = 0;
+        for (int i = 0; i < charArray.length; i++) {
+            if (! partOfRange(charArray, i)) {
+                totalEntries ++;
+            }
+        }
+        if (totalEntries < 1) {
+            return;
+        }
+        int remainingEntries = totalEntries;
+        /* Limited to 100 entries in each section */
+        int entriesThisSection = Math.min(remainingEntries, 100);
+        int remainingEntriesThisSection = entriesThisSection;
+        p.append(entriesThisSection + " beginbfchar\n");
+        for (int i = 0; i < charArray.length; i++) {
+            if (partOfRange(charArray, i)) {
+                continue;
+            }
+            p.append("<" + padHexString(Integer.toHexString(i), 4)
+                    + "> ");
+            p.append("<" + padHexString(Integer.toHexString(charArray[i]), 4)
+                    + ">\n");
+            /* Compute the statistics. */
+            completedEntries ++;
+            remainingEntries = totalEntries - completedEntries;
+            remainingEntriesThisSection --;
+            if (remainingEntriesThisSection < 1) {
+                if (remainingEntries > 0) {
+                    p.append("endbfchar\n");
+                    entriesThisSection = Math.min(remainingEntries, 100);
+                    remainingEntriesThisSection = entriesThisSection;
+                    p.append(entriesThisSection + " beginbfchar\n");
+                }
+            }
+        }
+        p.append("endbfchar\n");
+    }
+
+    protected void writeBFRangeEntries(StringBuffer p, char[] charArray) {
+        int completedEntries = 0;
+        int totalEntries = 0;
+        for (int i = 0; i < charArray.length; i++) {
+            if (startOfRange(charArray, i)) {
+                totalEntries ++;
+            }
+        }
+        if (totalEntries < 1) {
+            return;
+        }
+        int remainingEntries = totalEntries;
+        int entriesThisSection = Math.min(remainingEntries, 100);
+        int remainingEntriesThisSection = entriesThisSection;
+        p.append(entriesThisSection + " beginbfrange\n");
+        for (int i = 0; i < charArray.length; i++) {
+            if (! startOfRange(charArray, i)) {
+                continue;
+            }
+            p.append("<"
+                    + padHexString(Integer.toHexString(i), 4)
+                    + "> ");
+            p.append("<"
+                    + padHexString(Integer.toHexString
+                            (endOfRange(charArray, i)), 4)
+                    + "> ");
+            p.append("<"
+                    + padHexString(Integer.toHexString(charArray[i]), 4)
+                    + ">\n");
+            /* Compute the statistics. */
+            completedEntries ++;
+            remainingEntries = totalEntries - completedEntries;
+            if (remainingEntriesThisSection < 1) {
+                if (remainingEntries > 0) {
+                    p.append("endbfrange\n");
+                    entriesThisSection = Math.min(remainingEntries, 100);
+                    remainingEntriesThisSection = entriesThisSection;
+                    p.append(entriesThisSection + " beginbfrange\n");
+                }
+            }
+        }
+        p.append("endbfrange\n");
+    }
+
+    /**
+     * Find the end of the current range.
+     * @param charArray The array which is being tested.
+     * @param startOfRange The index to the array element that is the start of
+     * the range.
+     * @return The index to the element that is the end of the range.
+     */
+    private int endOfRange(char[] charArray, int startOfRange) {
+        int endOfRange = -1;
+        for (int i = startOfRange; i < charArray.length - 1 && endOfRange < 0;
+                i++) {
+            if (! sameRangeEntryAsNext(charArray, i)) {
+                endOfRange = i;
+            }
+        }
+        return endOfRange;
+    }
+
+    /**
+     * Determine whether this array element should be part of a bfchar entry or
+     * a bfrange entry.
+     * @param charArray The array to be tested.
+     * @param arrayIndex The index to the array element to be tested.
+     * @return True if this array element should be included in a range.
+     */
+    private boolean partOfRange(char[] charArray, int arrayIndex) {
+        if (charArray.length < 2) {
+            return false;
+        }
+        if (arrayIndex == 0) {
+            return sameRangeEntryAsNext(charArray, 0);
+        }
+        if (arrayIndex == charArray.length - 1) {
+            return sameRangeEntryAsNext(charArray, arrayIndex - 1);
+        }
+        if (sameRangeEntryAsNext(charArray, arrayIndex - 1)) {
+            return true;
+        }
+        if (sameRangeEntryAsNext(charArray, arrayIndex)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Determine whether two bytes can be written in the same bfrange entry.
+     * @param charArray The array to be tested.
+     * @param firstItem The first of the two items in the array to be tested.
+     * The second item is firstItem + 1.
+     * @return True if both 1) the next item in the array is sequential with
+     * this one, and 2) the first byte of the character in the first position
+     * is equal to the first byte of the character in the second position.
+     */
+    private boolean sameRangeEntryAsNext(char[] charArray, int firstItem) {
+        if (charArray[firstItem] + 1 != charArray[firstItem + 1]) {
+            return false;
+        }
+        if (firstItem / 256 != (firstItem + 1) / 256) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Determine whether this array element should be the start of a bfrange
+     * entry.
+     * @param charArray The array to be tested.
+     * @param arrayIndex The index to the array element to be tested.
+     * @return True if this array element is the beginning of a range.
+     */
+    private boolean startOfRange(char[] charArray, int arrayIndex) {
+        // Can't be the start of a range if not part of a range.
+        if (! partOfRange(charArray, arrayIndex)) {
+            return false;
+        }
+        // If first element in the array, must be start of a range
+        if (arrayIndex == 0) {
+            return true;
+        }
+        // If last element in the array, cannot be start of a range
+        if (arrayIndex == charArray.length - 1) {
+            return false;
+        }
+        /*
+         * If part of same range as the previous element is, cannot be start
+         * of range.
+         */
+        if (sameRangeEntryAsNext(charArray, arrayIndex - 1)) {
+            return false;
+        }
+        // Otherwise, this is start of a range.
+        return true;
+    }
+
+    /**
+     * Prepends the input string with a sufficient number of "0" characters to
+     * get the returned string to be numChars length.
+     * @param input The input string.
+     * @param numChars The minimum characters in the output string.
+     * @return The padded string.
+     */
+    public static String padHexString(String input, int numChars) {
+        int length = input.length();
+        if (length >= numChars) {
+            return input;
+        }
+        StringBuffer returnString = new StringBuffer();
+        for (int i = 1; i <= numChars - length; i++) {
+            returnString.append("0");
+        }
+        returnString.append(input);
+        return returnString.toString();
+    }
+
+}
