Path: blob/master/src/java.desktop/share/classes/sun/font/CompositeFont.java
41154 views
/*1* Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.font;2627import java.awt.Font;2829/* Remind: need to enhance to extend component list with a fallback30* list, which is not used in metrics or queries on the composite, but31* is used in drawing primitives and queries which supply an actual string.32* ie for a codepoint that is only in a fallback, font-wide queries such33* as FontMetrics.getHeight() will not take it into account.34* But getStringBounds(..) would take it into account.35* Its fuzzier for queries such as "canDisplay". If this does not include36* the fallback, then we probably want to add "canDisplayFallback()"37* But its probably OK to include it so long as only composites include38* fallbacks. If physicals do then it would be really confusing ..39*/40public final class CompositeFont extends Font2D {4142private boolean[] deferredInitialisation;43String[] componentFileNames;44String[] componentNames;45/* because components can be lazily initialised the components field is46* private, to ensure all clients call getSlotFont()47*/48private PhysicalFont[] components;49int numSlots;50int numMetricsSlots;51int[] exclusionRanges;52int[] maxIndices;53int numGlyphs = 0;54int localeSlot = -1; // primary slot for this locale.5556/* See isStdComposite() for when/how this is used */57boolean isStdComposite = true;5859public CompositeFont(String name, String[] compFileNames,60String[] compNames, int metricsSlotCnt,61int[] exclRanges, int[] maxIndexes,62boolean defer, SunFontManager fm) {6364handle = new Font2DHandle(this);65fullName = name;66componentFileNames = compFileNames;67componentNames = compNames;68if (compNames == null) {69numSlots = componentFileNames.length;70} else {71numSlots = componentNames.length;72}73/* We will limit the number of slots to 254.74* We store the slot for a glyph id in a byte and we may use one slot75* for an EUDC font, and we may also create a composite76* using this composite as a backup for a physical font.77* So we want to leave space for the two additional slots.78*/79numSlots = (numSlots <= 254) ? numSlots : 254;8081/* Only the first "numMetricsSlots" slots are used for font metrics.82* the rest are considered "fallback" slots".83*/84numMetricsSlots = metricsSlotCnt;85exclusionRanges = exclRanges;86maxIndices = maxIndexes;8788/*89* See if this is a windows locale which has a system EUDC font.90* If so add it as the final fallback component of the composite.91* The caller could be responsible for this, but for now it seems92* better that it is handled internally to the CompositeFont class.93*/94if (fm.getEUDCFont() != null) {95int msCnt = numMetricsSlots;96int fbCnt = numSlots - msCnt;97numSlots++;98if (componentNames != null) {99componentNames = new String[numSlots];100System.arraycopy(compNames, 0, componentNames, 0, msCnt);101componentNames[msCnt] = fm.getEUDCFont().getFontName(null);102System.arraycopy(compNames, msCnt,103componentNames, msCnt+1, fbCnt);104}105if (componentFileNames != null) {106componentFileNames = new String[numSlots];107System.arraycopy(compFileNames, 0,108componentFileNames, 0, msCnt);109System.arraycopy(compFileNames, msCnt,110componentFileNames, msCnt+1, fbCnt);111}112components = new PhysicalFont[numSlots];113components[msCnt] = fm.getEUDCFont();114deferredInitialisation = new boolean[numSlots];115if (defer) {116for (int i=0; i<numSlots-1; i++) {117deferredInitialisation[i] = true;118}119}120} else {121components = new PhysicalFont[numSlots];122deferredInitialisation = new boolean[numSlots];123if (defer) {124for (int i=0; i<numSlots; i++) {125deferredInitialisation[i] = true;126}127}128}129130fontRank = Font2D.FONT_CONFIG_RANK;131132int index = fullName.indexOf('.');133if (index>0) {134familyName = fullName.substring(0, index);135/* composites don't call setStyle() as parsing the style136* takes place at the same time as parsing the family name.137* Do I really have to parse the style from the name?138* Need to look into having the caller provide this. */139if (index+1 < fullName.length()) {140String styleStr = fullName.substring(index+1);141if ("plain".equals(styleStr)) {142style = Font.PLAIN;143} else if ("bold".equals(styleStr)) {144style = Font.BOLD;145} else if ("italic".equals(styleStr)) {146style = Font.ITALIC;147} else if ("bolditalic".equals(styleStr)) {148style = Font.BOLD | Font.ITALIC;149}150}151} else {152familyName = fullName;153}154}155156/*157* Build a composite from a set of individual slot fonts.158*/159CompositeFont(PhysicalFont[] slotFonts) {160161isStdComposite = false;162handle = new Font2DHandle(this);163fullName = slotFonts[0].fullName;164familyName = slotFonts[0].familyName;165style = slotFonts[0].style;166167numMetricsSlots = 1; /* Only the physical Font */168numSlots = slotFonts.length;169170components = new PhysicalFont[numSlots];171System.arraycopy(slotFonts, 0, components, 0, numSlots);172deferredInitialisation = new boolean[numSlots]; // all false.173}174175/* This method is currently intended to be called only from176* FontManager.getCompositeFontUIResource(Font)177* It creates a new CompositeFont with the contents of the Physical178* one pre-pended as slot 0.179*/180CompositeFont(PhysicalFont physFont, CompositeFont compFont) {181182isStdComposite = false;183handle = new Font2DHandle(this);184fullName = physFont.fullName;185familyName = physFont.familyName;186style = physFont.style;187188numMetricsSlots = 1; /* Only the physical Font */189numSlots = compFont.numSlots+1;190191/* Ugly though it is, we synchronize here on the FontManager class192* because it is the lock used to do deferred initialisation.193* We need to ensure that the arrays have consistent information.194* But it may be possible to dispense with the synchronisation if195* it is harmless that we do not know a slot is already initialised196* and just need to discover that and mark it so.197*/198synchronized (FontManagerFactory.getInstance()) {199components = new PhysicalFont[numSlots];200components[0] = physFont;201System.arraycopy(compFont.components, 0,202components, 1, compFont.numSlots);203204if (compFont.componentNames != null) {205componentNames = new String[numSlots];206componentNames[0] = physFont.fullName;207System.arraycopy(compFont.componentNames, 0,208componentNames, 1, compFont.numSlots);209}210if (compFont.componentFileNames != null) {211componentFileNames = new String[numSlots];212componentFileNames[0] = null;213System.arraycopy(compFont.componentFileNames, 0,214componentFileNames, 1, compFont.numSlots);215}216deferredInitialisation = new boolean[numSlots];217deferredInitialisation[0] = false;218System.arraycopy(compFont.deferredInitialisation, 0,219deferredInitialisation, 1, compFont.numSlots);220}221}222223/* This is used for deferred initialisation, so that the components of224* a logical font are initialised only when the font is used.225* This can have a positive impact on start-up of most UI applications.226* Note that this technique cannot be used with a TTC font as it227* doesn't know which font in the collection is needed. The solution to228* this is that the initialisation checks if the returned font is229* really the one it wants by comparing the name against the name that230* was passed in (if none was passed in then you aren't using a TTC231* as you would have to specify the name in such a case).232* Assuming there's only two or three fonts in a collection then it233* may be sufficient to verify the returned name is the expected one.234* But half the time it won't be. However since initialisation of the235* TTC will initialise all its components then just do a findFont2D call236* to locate the right one.237* This code allows for initialisation of each slot on demand.238* There are two issues with this.239* 1) All metrics slots probably may be initialised anyway as many240* apps will query the overall font metrics. However this is not an241* absolute requirement242* 2) Some font configuration files on Solaris reference two versions243* of a TT font: a Latin-1 version, then a Pan-European version.244* One from /usr/openwin/lib/X11/fonts/TrueType, the other from245* a euro_fonts directory which is symlinked from numerous locations.246* This is difficult to avoid because the two do not share XLFDs so247* both will be consequently mapped by separate XLFDs needed by AWT.248* The difficulty this presents for lazy initialisation is that if249* all the components are not mapped at once, the smaller version may250* have been used only to be replaced later, and what is the consequence251* for a client that displayed the contents of this font already.252* After some thought I think this will not be a problem because when253* client tries to display a glyph only in the Euro font, the composite254* will ask all components of this font for that glyph and will get255* the euro one. Subsequent uses will all come from the 100% compatible256* euro one.257*/258private void doDeferredInitialisation(int slot) {259if (deferredInitialisation[slot] == false) {260return;261}262263/* Synchronize on FontManager so that is the global lock264* to update its static set of deferred fonts.265* This global lock is rarely likely to be an issue as there266* are only going to be a few calls into this code.267*/268SunFontManager fm = SunFontManager.getInstance();269synchronized (fm) {270if (componentNames == null) {271componentNames = new String[numSlots];272}273if (components[slot] == null) {274/* Warning: it is possible that the returned component is275* not derived from the file name argument, this can happen if:276* - the file can't be found277* - the file has a bad font278* - the font in the file is superseded by a more complete one279* This should not be a problem for composite font as it will280* make no further use of this file, but code debuggers/281* maintainers need to be conscious of this possibility.282*/283if (componentFileNames != null &&284componentFileNames[slot] != null) {285components[slot] =286fm.initialiseDeferredFont(componentFileNames[slot]);287}288289if (components[slot] == null) {290components[slot] = fm.getDefaultPhysicalFont();291}292String name = components[slot].getFontName(null);293if (componentNames[slot] == null) {294componentNames[slot] = name;295} else if (!componentNames[slot].equalsIgnoreCase(name)) {296/* If a component specifies the file with a bad font,297* the corresponding slot will be initialized by298* default physical font. In such case findFont2D may299* return composite font which cannot be casted to300* physical font.301*/302try {303components[slot] =304(PhysicalFont) fm.findFont2D(componentNames[slot],305style,306FontManager.PHYSICAL_FALLBACK);307} catch (ClassCastException cce) {308/* Assign default physical font to the slot */309components[slot] = fm.getDefaultPhysicalFont();310}311}312}313deferredInitialisation[slot] = false;314}315}316317/* To called only by FontManager.replaceFont */318void replaceComponentFont(PhysicalFont oldFont, PhysicalFont newFont) {319if (components == null) {320return;321}322for (int slot=0; slot<numSlots; slot++) {323if (components[slot] == oldFont) {324components[slot] = newFont;325if (componentNames != null) {326componentNames[slot] = newFont.getFontName(null);327}328}329}330}331332public boolean isExcludedChar(int slot, int charcode) {333334if (exclusionRanges == null || maxIndices == null ||335slot >= numMetricsSlots) {336return false;337}338339int minIndex = 0;340int maxIndex = maxIndices[slot];341if (slot > 0) {342minIndex = maxIndices[slot - 1];343}344int curIndex = minIndex;345while (maxIndex > curIndex) {346if ((charcode >= exclusionRanges[curIndex])347&& (charcode <= exclusionRanges[curIndex+1])) {348return true; // excluded349}350curIndex += 2;351}352return false;353}354355public void getStyleMetrics(float pointSize, float[] metrics, int offset) {356PhysicalFont font = getSlotFont(0);357if (font == null) { // possible?358super.getStyleMetrics(pointSize, metrics, offset);359} else {360font.getStyleMetrics(pointSize, metrics, offset);361}362}363364public int getNumSlots() {365return numSlots;366}367368public PhysicalFont getSlotFont(int slot) {369/* This is essentially the runtime overhead for deferred font370* initialisation: a boolean test on obtaining a slot font,371* which will happen per slot, on initialisation of a strike372* (as that is the only frequent call site of this method.373*/374if (deferredInitialisation[slot]) {375doDeferredInitialisation(slot);376}377SunFontManager fm = SunFontManager.getInstance();378try {379PhysicalFont font = components[slot];380if (font == null) {381try {382font = (PhysicalFont) fm.383findFont2D(componentNames[slot], style,384FontManager.PHYSICAL_FALLBACK);385components[slot] = font;386} catch (ClassCastException cce) {387font = fm.getDefaultPhysicalFont();388}389}390return font;391} catch (Exception e) {392return fm.getDefaultPhysicalFont();393}394}395396FontStrike createStrike(FontStrikeDesc desc) {397return new CompositeStrike(this, desc);398}399400/* This is set false when the composite is created using a specified401* physical font as the first slot and called by code which402* selects composites by locale preferences to know that this403* isn't a font which should be adjusted.404*/405public boolean isStdComposite() {406return isStdComposite;407}408409/* This isn't very efficient but its infrequently used.410* StandardGlyphVector uses it when the client assigns the glyph codes.411* These may not be valid. This validates them substituting the missing412* glyph elsewhere.413*/414protected int getValidatedGlyphCode(int glyphCode) {415int slot = glyphCode >>> 24;416if (slot >= numSlots) {417return getMapper().getMissingGlyphCode();418}419420int slotglyphCode = glyphCode & CompositeStrike.SLOTMASK;421PhysicalFont slotFont = getSlotFont(slot);422if (slotFont.getValidatedGlyphCode(slotglyphCode) ==423slotFont.getMissingGlyphCode()) {424return getMapper().getMissingGlyphCode();425} else {426return glyphCode;427}428}429430public CharToGlyphMapper getMapper() {431if (mapper == null) {432mapper = new CompositeGlyphMapper(this);433}434return mapper;435}436437public boolean hasSupplementaryChars() {438for (int i=0; i<numSlots; i++) {439if (getSlotFont(i).hasSupplementaryChars()) {440return true;441}442}443return false;444}445446public int getNumGlyphs() {447if (numGlyphs == 0) {448numGlyphs = getMapper().getNumGlyphs();449}450return numGlyphs;451}452453public int getMissingGlyphCode() {454return getMapper().getMissingGlyphCode();455}456457public boolean canDisplay(char c) {458return getMapper().canDisplay(c);459}460461public boolean useAAForPtSize(int ptsize) {462/* Find the first slot that supports the default encoding and use463* that to decide the "gasp" behaviour of the composite font.464* REMIND "default encoding" isn't applicable to a Unicode locale465* and we need to replace this with a better mechanism for deciding466* if a font "supports" the user's language. See TrueTypeFont.java467*/468if (localeSlot == -1) {469/* Ordinarily check numMetricsSlots, but non-standard composites470* set that to "1" whilst not necessarily supporting the default471* encoding with that first slot. In such a case check all slots.472*/473int numCoreSlots = numMetricsSlots;474if (numCoreSlots == 1 && !isStdComposite()) {475numCoreSlots = numSlots;476}477for (int slot=0; slot<numCoreSlots; slot++) {478if (getSlotFont(slot).supportsEncoding(null)) {479localeSlot = slot;480break;481}482}483if (localeSlot == -1) {484localeSlot = 0;485}486}487return getSlotFont(localeSlot).useAAForPtSize(ptsize);488}489490public String toString() {491String ls = System.lineSeparator();492String componentsStr = "";493for (int i=0; i<numSlots; i++) {494componentsStr += " Slot["+i+"]="+getSlotFont(i)+ls;495}496return "** Composite Font: Family=" + familyName +497" Name=" + fullName + " style=" + style + ls + componentsStr;498}499}500501502