private boolean readUnicodeCmap()

in fop-core/src/main/java/org/apache/fop/fonts/truetype/OpenFont.java [451:720]


    private boolean readUnicodeCmap(long cmapUniOffset, int encodingID)
            throws IOException {
        //Read CMAP table and correct mtxTab.index
        int mtxPtr = 0;

        // Read unicode cmap
        seekTab(fontFile, OFTableName.CMAP, cmapUniOffset);
        int cmapFormat = fontFile.readTTFUShort();

        if (cmapFormat < 8) {
            fontFile.readTTFUShort(); //skip cmap length
            fontFile.readTTFUShort(); //skip cmap version
        } else {
            fontFile.readTTFUShort(); //skip 2 bytes to read a Fixed32
            fontFile.readTTFULong(); //skip cmap length
            fontFile.readTTFULong(); //skip cmap version
        }

        if (log.isDebugEnabled()) {
            log.debug("CMAP format: " + cmapFormat);
        }

        if (cmapFormat == 4) {
            int cmapSegCountX2 = fontFile.readTTFUShort();
            int cmapSearchRange = fontFile.readTTFUShort();
            int cmapEntrySelector = fontFile.readTTFUShort();
            int cmapRangeShift = fontFile.readTTFUShort();

            if (log.isDebugEnabled()) {
                log.debug("segCountX2   : " + cmapSegCountX2);
                log.debug("searchRange  : " + cmapSearchRange);
                log.debug("entrySelector: " + cmapEntrySelector);
                log.debug("rangeShift   : " + cmapRangeShift);
            }


            int[] cmapEndCounts = new int[cmapSegCountX2 / 2];
            int[] cmapStartCounts = new int[cmapSegCountX2 / 2];
            int[] cmapDeltas = new int[cmapSegCountX2 / 2];
            int[] cmapRangeOffsets = new int[cmapSegCountX2 / 2];

            for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
                cmapEndCounts[i] = fontFile.readTTFUShort();
            }

            fontFile.skip(2);    // Skip reservedPad

            for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
                cmapStartCounts[i] = fontFile.readTTFUShort();
            }

            for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
                cmapDeltas[i] = fontFile.readTTFShort();
            }

            //int startRangeOffset = in.getCurrentPos();

            for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
                cmapRangeOffsets[i] = fontFile.readTTFUShort();
            }

            int glyphIdArrayOffset = fontFile.getCurrentPos();

            BitSet eightBitGlyphs = new BitSet(256);

            // Insert the unicode id for the glyphs in mtxTab
            // and fill in the cmaps ArrayList
            for (int i = 0; i < cmapStartCounts.length; i++) {

                if (log.isTraceEnabled()) {
                    log.trace(i + ": " + cmapStartCounts[i]
                                                         + " - " + cmapEndCounts[i]);
                }
                if (log.isDebugEnabled()) {
                    if (isInPrivateUseArea(cmapStartCounts[i], cmapEndCounts[i])) {
                        log.debug("Font contains glyphs in the Unicode private use area: "
                                + Integer.toHexString(cmapStartCounts[i]) + " - "
                                + Integer.toHexString(cmapEndCounts[i]));
                    }
                }

                for (int j = cmapStartCounts[i]; j <= cmapEndCounts[i]; j++) {

                    // Update lastChar
                    if (j < 256 && j > lastChar) {
                        lastChar = (short)j;
                    }

                    if (j < 256) {
                        eightBitGlyphs.set(j);
                    }

                    if (mtxPtr < mtxTab.length) {
                        int glyphIdx;
                        // the last character 65535 = .notdef
                        // may have a range offset
                        if (cmapRangeOffsets[i] != 0 && j != 65535) {
                            int glyphOffset = glyphIdArrayOffset
                                + ((cmapRangeOffsets[i] / 2)
                                    + (j - cmapStartCounts[i])
                                    + (i)
                                    - cmapSegCountX2 / 2) * 2;
                            fontFile.seekSet(glyphOffset);
                            glyphIdx = (fontFile.readTTFUShort() + cmapDeltas[i])
                                       & 0xffff;
                            //mtxTab[glyphIdx].setName(mtxTab[glyphIdx].getName() + " - "+(char)j);
                            unicodeMappings.add(new UnicodeMapping(this, glyphIdx, j));
                            mtxTab[glyphIdx].getUnicodeIndex().add(j);

                            mapSymbol(encodingID, j, eightBitGlyphs, glyphIdx);

                            // Also add winAnsiWidth
                            List<Integer> v = ansiIndex.get(j);
                            if (v != null) {
                                for (Integer aIdx : v) {
                                    ansiWidth[aIdx]
                                        = mtxTab[glyphIdx].getWx();

                                    if (log.isTraceEnabled()) {
                                        log.trace("Added width "
                                                + mtxTab[glyphIdx].getWx()
                                                + " uni: " + j
                                                + " ansi: " + aIdx);
                                    }
                                }
                            }

                            if (log.isTraceEnabled()) {
                                log.trace("Idx: "
                                        + glyphIdx
                                        + " Delta: " + cmapDeltas[i]
                                        + " Unicode: " + j
                                        + " name: " + mtxTab[glyphIdx].getName());
                            }
                        } else {
                            glyphIdx = (j + cmapDeltas[i]) & 0xffff;

                            if (glyphIdx < mtxTab.length) {
                                mtxTab[glyphIdx].getUnicodeIndex().add(j);
                            } else {
                                log.debug("Glyph " + glyphIdx
                                                   + " out of range: "
                                                   + mtxTab.length);
                            }

                            unicodeMappings.add(new UnicodeMapping(this, glyphIdx, j));
                            if (glyphIdx < mtxTab.length) {
                                mtxTab[glyphIdx].getUnicodeIndex().add(j);
                            } else {
                                log.debug("Glyph " + glyphIdx
                                                   + " out of range: "
                                                   + mtxTab.length);
                            }

                            mapSymbol(encodingID, j, eightBitGlyphs, glyphIdx);

                            // Also add winAnsiWidth
                            List<Integer> v = ansiIndex.get(j);
                            if (v != null) {
                                for (Integer aIdx : v) {
                                    ansiWidth[aIdx] = mtxTab[glyphIdx].getWx();
                                }
                            }

                            //getLogger().debug("IIdx: " +
                            //    mtxPtr +
                            //    " Delta: " + cmap_deltas[i] +
                            //    " Unicode: " + j +
                            //    " name: " +
                            //    mtxTab[(j+cmap_deltas[i]) & 0xffff].name);

                        }
                        if (glyphIdx < mtxTab.length) {
                            if (mtxTab[glyphIdx].getUnicodeIndex().size() < 2) {
                                mtxPtr++;
                            }
                        }
                    }
                }
            }
        } else if (cmapFormat == 12) {
            long nGroups = fontFile.readTTFULong();

            for (long i = 0; i < nGroups; ++i) {
                long startCharCode = fontFile.readTTFULong();
                long endCharCode = fontFile.readTTFULong();
                long startGlyphCode = fontFile.readTTFULong();

                if (startCharCode < 0 || startCharCode > 0x10FFFFL) {
                    log.warn("startCharCode outside Unicode range");
                    continue;
                }

                if (startCharCode >= 0xD800 && startCharCode <= 0xDFFF) {
                    log.warn("startCharCode is a surrogate pair: " + startCharCode);
                }

                //endCharCode outside unicode range or is surrogate pair.
                if (endCharCode > 0 && endCharCode < startCharCode || endCharCode > 0x10FFFFL) {
                    log.warn("startCharCode outside Unicode range");
                    continue;
                }

                if (endCharCode >= 0xD800 && endCharCode <= 0xDFFF) {
                    log.warn("endCharCode is a surrogate pair: " + startCharCode);
                }

                for (long offset = 0; offset <= endCharCode - startCharCode; ++offset) {
                    long glyphIndexL = startGlyphCode + offset;
                    long charCodeL = startCharCode + offset;

                    if (glyphIndexL >= numberOfGlyphs) {
                        log.warn("Format 12 cmap contains an invalid glyph index");
                        break;
                    }

                    if (charCodeL > 0x10FFFFL) {
                        log.warn("Format 12 cmap contains character beyond UCS-4");
                    }

                    if (glyphIndexL > Integer.MAX_VALUE) {
                        log.error("glyphIndex > Integer.MAX_VALUE");
                        continue;
                    }

                    if (charCodeL > Integer.MAX_VALUE) {
                        log.error("startCharCode + j > Integer.MAX_VALUE");
                        continue;
                    }

                    // Update lastChar
                    if (charCodeL < 0xFF && charCodeL > lastChar) {
                        lastChar = (short) charCodeL;
                    }

                    int charCode = (int) charCodeL;
                    int glyphIndex = (int) glyphIndexL;

                    // Also add winAnsiWidth.
                    List<Integer> ansiIndexes = null;

                    if (charCodeL <= Character.MAX_VALUE) {
                        ansiIndexes = ansiIndex.get((int) charCodeL);
                    }

                    unicodeMappings.add(new UnicodeMapping(this, glyphIndex, charCode));
                    mtxTab[glyphIndex].getUnicodeIndex().add(charCode);

                    if (ansiIndexes == null) {
                        continue;
                    }

                    for (Integer aIdx : ansiIndexes) {
                        ansiWidth[aIdx] = mtxTab[glyphIndex].getWx();

                        if (log.isTraceEnabled()) {
                            log.trace("Added width "
                                    + mtxTab[glyphIndex].getWx()
                                    + " uni: " + offset
                                    + " ansi: " + aIdx);
                        }
                    }
                }
            }
        } else {
            log.error("Cmap format not supported: " + cmapFormat);
            return false;
        }
        return true;
    }