/* * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.font; /* remember that the API requires a Font use a * consistent glyph id. for a code point, and this is a * problem if a particular strike uses native scaler sometimes * and T2K others. That needs to be dealt with somewhere, but * here we can just always get the same glyph code without * needing a strike. * * The C implementation would cache the results of anything up * to the maximum surrogate pair code point. * This implementation will not cache as much, since the storage * requirements are not justifiable. Even so it still can use up * to 216*256*4 bytes of storage per composite font. If an app * calls canDisplay on this range for all 20 composite fonts that's * over 1Mb of cached data. May need to employ WeakReferences if * this appears to cause problems. */ public class CompositeGlyphMapper extends CharToGlyphMapper { public static final int SLOTMASK = 0xff000000; public static final int GLYPHMASK = 0x00ffffff; public static final int NBLOCKS = 216; public static final int BLOCKSZ = 256; public static final int MAXUNICODE = NBLOCKS*BLOCKSZ; CompositeFont font; CharToGlyphMapper slotMappers[]; int[][] glyphMaps; private boolean hasExcludes; public CompositeGlyphMapper(CompositeFont compFont) { font = compFont; initMapper(); /* This is often false which saves the overhead of a * per-mapped char method call. */ hasExcludes = compFont.exclusionRanges != null && compFont.maxIndices != null; } public int compositeGlyphCode(int slot, int glyphCode) { return (slot << 24 | (glyphCode & GLYPHMASK)); } private void initMapper() { if (missingGlyph == CharToGlyphMapper.UNINITIALIZED_GLYPH) { if (glyphMaps == null) { glyphMaps = new int[NBLOCKS][]; } slotMappers = new CharToGlyphMapper[font.numSlots]; /* This requires that slot 0 is never empty. */ missingGlyph = font.getSlotFont(0).getMissingGlyphCode(); missingGlyph = compositeGlyphCode(0, missingGlyph); } } private int getCachedGlyphCode(int unicode) { if (unicode >= MAXUNICODE) { return UNINITIALIZED_GLYPH; // don't cache surrogates } int[] gmap; if ((gmap = glyphMaps[unicode >> 8]) == null) { return UNINITIALIZED_GLYPH; } return gmap[unicode & 0xff]; } private void setCachedGlyphCode(int unicode, int glyphCode) { if (unicode >= MAXUNICODE) { return; // don't cache surrogates } int index0 = unicode >> 8; if (glyphMaps[index0] == null) { glyphMaps[index0] = new int[BLOCKSZ]; for (int i=0;i= 0) { CharToGlyphMapper mapper = getSlotMapper(prefSlot); int glyphCode = mapper.charToGlyph(unicode); if (glyphCode != mapper.getMissingGlyphCode()) { return compositeGlyphCode(prefSlot, glyphCode); } } return charToGlyph(unicode); } public int charToGlyph(char unicode) { int glyphCode = getCachedGlyphCode(unicode); if (glyphCode == UNINITIALIZED_GLYPH) { glyphCode = convertToGlyph(unicode); } return glyphCode; } /* This variant checks if shaping is needed and immediately * returns true if it does. A caller of this method should be expecting * to check the return type because it needs to know how to handle * the character data for display. */ public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) { for (int i=0; i= HI_SURROGATE_START && code <= HI_SURROGATE_END && i < count - 1) { char low = unicodes[i + 1]; if (low >= LO_SURROGATE_START && low <= LO_SURROGATE_END) { code = (code - HI_SURROGATE_START) * 0x400 + low - LO_SURROGATE_START + 0x10000; glyphs[i + 1] = INVISIBLE_GLYPH_ID; step = 2; } } if (i < count - step && isVariationSelector(unicodes[i+step]) && isBaseChar(code)) { variationSelector = unicodes[i+step]; glyphs[i] = convertToGlyph(code, variationSelector); glyphs[i+step] = INVISIBLE_GLYPH_ID; i += 1; } else if (i < count - step -1 && isVariationSelector(unicodes[i+step], unicodes[i+step+1]) && isBaseChar(code)) { variationSelector = (unicodes[i+step] - HI_SURROGATE_START) * 0x400 + unicodes[i+step+1] - LO_SURROGATE_START + 0x10000; glyphs[i] = convertToGlyph(code, variationSelector); glyphs[i+step] = INVISIBLE_GLYPH_ID; glyphs[i+step+1] = INVISIBLE_GLYPH_ID; i += 2; } if (variationSelector == 0) { int gc = glyphs[i] = getCachedGlyphCode(code); if (gc == UNINITIALIZED_GLYPH) { glyphs[i] = convertToGlyph(code); } } if (code < FontUtilities.MIN_LAYOUT_CHARCODE) { continue; } else if (FontUtilities.isComplexCharCode(code)) { return true; } else if (code >= 0x10000) { i += 1; // Empty glyph slot after surrogate continue; } } return false; } /* The conversion is not very efficient - looping as it does, converting * one char at a time. However the cache should fill very rapidly. */ public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { for (int i=0; i= HI_SURROGATE_START && code <= HI_SURROGATE_END && i < count - 1) { char low = unicodes[i + 1]; if (low >= LO_SURROGATE_START && low <= LO_SURROGATE_END) { code = (code - HI_SURROGATE_START) * 0x400 + low - LO_SURROGATE_START + 0x10000; glyphs[i+1] = INVISIBLE_GLYPH_ID; step = 2; } } if (i < count - step && isVariationSelector(unicodes[i+step]) && isBaseChar(code)) { variationSelector = unicodes[i+step]; glyphs[i] = convertToGlyph(code, variationSelector); glyphs[i+step] = INVISIBLE_GLYPH_ID; i += 1; } else if (i < count - step -1 && isVariationSelector(unicodes[i+step], unicodes[i+step+1]) && isBaseChar(code)) { variationSelector = (unicodes[i+step] - HI_SURROGATE_START) * 0x400 + unicodes[i+step+1] - LO_SURROGATE_START + 0x10000; glyphs[i] = convertToGlyph(code, variationSelector); glyphs[i+step] = INVISIBLE_GLYPH_ID; glyphs[i+step+1] = INVISIBLE_GLYPH_ID; i += 2; } if (variationSelector == 0) { int gc = glyphs[i] = getCachedGlyphCode(code); if (gc == UNINITIALIZED_GLYPH) { glyphs[i] = convertToGlyph(code); } } if (code >= 0x10000) { i++; } } } public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { for (int i=0; i