J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. |
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | * |
| 5 | * This code is free software; you can redistribute it and/or modify it |
| 6 | * under the terms of the GNU General Public License version 2 only, as |
| 7 | * published by the Free Software Foundation. Sun designates this |
| 8 | * particular file as subject to the "Classpath" exception as provided |
| 9 | * by Sun in the LICENSE file that accompanied this code. |
| 10 | * |
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 14 | * version 2 for more details (a copy is included in the LICENSE file that |
| 15 | * accompanied this code). |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public License version |
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 20 | * |
| 21 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 23 | * have any questions. |
| 24 | */ |
| 25 | |
| 26 | package com.sun.java.swing.plaf.gtk; |
| 27 | |
| 28 | import java.awt.*; |
| 29 | import java.awt.geom.AffineTransform; |
| 30 | import javax.swing.plaf.FontUIResource; |
| 31 | import java.util.StringTokenizer; |
| 32 | import sun.font.FontManager; |
| 33 | |
| 34 | /** |
| 35 | * @author Shannon Hickey |
| 36 | * @author Leif Samuelsson |
| 37 | */ |
| 38 | class PangoFonts { |
| 39 | |
| 40 | public static final String CHARS_DIGITS = "0123456789"; |
| 41 | |
| 42 | /** |
| 43 | * Calculate a default scale factor for fonts in this L&F to match |
| 44 | * the reported resolution of the screen. |
| 45 | * Java 2D specified a default user-space scale of 72dpi. |
| 46 | * This is unlikely to correspond to that of the real screen. |
| 47 | * The Xserver reports a value which may be used to adjust for this. |
| 48 | * and Java 2D exposes it via a normalizing transform. |
| 49 | * However many Xservers report a hard-coded 90dpi whilst others report a |
| 50 | * calculated value based on possibly incorrect data. |
| 51 | * That is something that must be solved at the X11 level |
| 52 | * Note that in an X11 multi-screen environment, the default screen |
| 53 | * is the one used by the JRE so it is safe to use it here. |
| 54 | */ |
| 55 | private static double fontScale; |
| 56 | |
| 57 | static { |
| 58 | fontScale = 1.0d; |
| 59 | GraphicsEnvironment ge = |
| 60 | GraphicsEnvironment.getLocalGraphicsEnvironment(); |
| 61 | |
| 62 | if (!ge.isHeadless()) { |
| 63 | GraphicsConfiguration gc = |
| 64 | ge.getDefaultScreenDevice().getDefaultConfiguration(); |
| 65 | AffineTransform at = gc.getNormalizingTransform(); |
| 66 | fontScale = at.getScaleY(); |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | |
| 71 | /** |
| 72 | * Parses a String containing a pango font description and returns |
| 73 | * a Font object. |
| 74 | * |
| 75 | * @param pangoName a String describing a pango font |
| 76 | * e.g. "Sans Italic 10" |
| 77 | * @return a Font object as a FontUIResource |
| 78 | * or null if no suitable font could be created. |
| 79 | */ |
| 80 | static Font lookupFont(String pangoName) { |
| 81 | String family = ""; |
| 82 | int style = Font.PLAIN; |
| 83 | int size = 10; |
| 84 | |
| 85 | StringTokenizer tok = new StringTokenizer(pangoName); |
| 86 | |
| 87 | while (tok.hasMoreTokens()) { |
| 88 | String word = tok.nextToken(); |
| 89 | |
| 90 | if (word.equalsIgnoreCase("italic")) { |
| 91 | style |= Font.ITALIC; |
| 92 | } else if (word.equalsIgnoreCase("bold")) { |
| 93 | style |= Font.BOLD; |
| 94 | } else if (CHARS_DIGITS.indexOf(word.charAt(0)) != -1) { |
| 95 | try { |
| 96 | size = Integer.parseInt(word); |
| 97 | } catch (NumberFormatException ex) { |
| 98 | } |
| 99 | } else { |
| 100 | if (family.length() > 0) { |
| 101 | family += " "; |
| 102 | } |
| 103 | |
| 104 | family += word; |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | /* |
| 109 | * Java 2D font point sizes are in a user-space scale of 72dpi. |
| 110 | * GTK allows a user to configure a "dpi" property used to scale |
| 111 | * the fonts used to match a user's preference. |
| 112 | * To match the font size of GTK apps we need to obtain this DPI and |
| 113 | * adjust as follows: |
| 114 | * Some versions of GTK use XSETTINGS if available to dynamically |
| 115 | * monitor user-initiated changes in the DPI to be used by GTK |
| 116 | * apps. This value is also made available as the Xft.dpi X resource. |
| 117 | * This is presumably a function of the font preferences API and/or |
| 118 | * the manner in which it requests the toolkit to update the default |
| 119 | * for the desktop. This dual approach is probably necessary since |
| 120 | * other versions of GTK - or perhaps some apps - determine the size |
| 121 | * to use only at start-up from that X resource. |
| 122 | * If that resource is not set then GTK scales for the DPI resolution |
| 123 | * reported by the Xserver using the formula |
| 124 | * DisplayHeight(dpy, screen) / DisplayHeightMM(dpy, screen) * 25.4 |
| 125 | * (25.4mm == 1 inch). |
| 126 | * JDK tracks the Xft.dpi XSETTINGS property directly so it can |
| 127 | * dynamically change font size by tracking just that value. |
| 128 | * If that resource is not available use the same fall back formula |
| 129 | * as GTK (see calculation for fontScale). |
| 130 | * |
| 131 | * GTK's default setting for Xft.dpi is 96 dpi (and it seems -1 |
| 132 | * apparently also can mean that "default"). However this default |
| 133 | * isn't used if there's no property set. The real default in the |
| 134 | * absence of a resource is the Xserver reported dpi. |
| 135 | * Finally this DPI is used to calculate the nearest Java 2D font |
| 136 | * 72 dpi font size. |
| 137 | * There are cases in which JDK behaviour may not exactly mimic |
| 138 | * GTK native app behaviour : |
| 139 | * 1) When a GTK app is not able to dynamically track the changes |
| 140 | * (does not use XSETTINGS), JDK will resize but other apps will |
| 141 | * not. This is OK as JDK is exhibiting preferred behaviour and |
| 142 | * this is probably how all later GTK apps will behave |
| 143 | * 2) When a GTK app does not use XSETTINGS and for some reason |
| 144 | * the XRDB property is not present. JDK will pick up XSETTINGS |
| 145 | * and the GTK app will use the Xserver default. Since its |
| 146 | * impossible for JDK to know that some other GTK app is not |
| 147 | * using XSETTINGS its impossible to account for this and in any |
| 148 | * case for it to be a problem the values would have to be different. |
| 149 | * It also seems unlikely to arise except when a user explicitly |
| 150 | * deletes the X resource database entry. |
| 151 | * 3) Because of rounding errors sizes may differ very slightly |
| 152 | * between JDK and GTK. To fix that would at the very least require |
| 153 | * Swing to specify floating pt font sizes. |
| 154 | * Eg "10 pts" for GTK at 96 dpi to get the same size at Java 2D's |
| 155 | * 72 dpi you'd need to specify exactly 13.33. |
| 156 | * There also some other issues to be aware of for the future: |
| 157 | * GTK specifies the Xft.dpi value as server-wide which when used |
| 158 | * on systems with 2 distinct X screens with different physical DPI |
| 159 | * the font sizes will inevitably appear different. It would have |
| 160 | * been a more user-friendly design to further adjust that one |
| 161 | * setting depending on the screen resolution to achieve perceived |
| 162 | * equivalent sizes. If such a change were ever to be made in GTK |
| 163 | * we would need to update for that. |
| 164 | */ |
| 165 | double dsize = size; |
| 166 | int dpi = 96; |
| 167 | Object value = |
| 168 | Toolkit.getDefaultToolkit().getDesktopProperty("gnome.Xft/DPI"); |
| 169 | if (value instanceof Integer) { |
| 170 | dpi = ((Integer)value).intValue() / 1024; |
| 171 | if (dpi == -1) { |
| 172 | dpi = 96; |
| 173 | } |
| 174 | if (dpi < 50) { /* 50 dpi is the minimum value gnome allows */ |
| 175 | dpi = 50; |
| 176 | } |
| 177 | /* The Java rasteriser assumes pts are in a user space of |
| 178 | * 72 dpi, so we need to adjust for that. |
| 179 | */ |
| 180 | dsize = ((double)(dpi * size)/ 72.0); |
| 181 | } else { |
| 182 | /* If there's no property, GTK scales for the resolution |
| 183 | * reported by the Xserver using the formula listed above. |
| 184 | * fontScale already accounts for the 72 dpi Java 2D space. |
| 185 | */ |
| 186 | dsize = size * fontScale; |
| 187 | } |
| 188 | |
| 189 | /* Round size to nearest integer pt size */ |
| 190 | size = (int)(dsize + 0.5); |
| 191 | if (size < 1) { |
| 192 | size = 1; |
| 193 | } |
| 194 | |
| 195 | String fcFamilyLC = family.toLowerCase(); |
| 196 | if (FontManager.mapFcName(fcFamilyLC) != null) { |
| 197 | /* family is a Fc/Pango logical font which we need to expand. */ |
| 198 | return FontManager.getFontConfigFUIR(fcFamilyLC, style, size); |
| 199 | } else { |
| 200 | /* It's a physical font which we will create with a fallback */ |
| 201 | Font font = new FontUIResource(family, style, size); |
| 202 | return FontManager.getCompositeFontUIResource(font); |
| 203 | } |
| 204 | } |
| 205 | |
| 206 | /** |
| 207 | * Parses a String containing a pango font description and returns |
| 208 | * the (unscaled) font size as an integer. |
| 209 | * |
| 210 | * @param pangoName a String describing a pango font |
| 211 | * @return the size of the font described by pangoName (e.g. if |
| 212 | * pangoName is "Sans Italic 10", then this method returns 10) |
| 213 | */ |
| 214 | static int getFontSize(String pangoName) { |
| 215 | int size = 10; |
| 216 | |
| 217 | StringTokenizer tok = new StringTokenizer(pangoName); |
| 218 | while (tok.hasMoreTokens()) { |
| 219 | String word = tok.nextToken(); |
| 220 | |
| 221 | if (CHARS_DIGITS.indexOf(word.charAt(0)) != -1) { |
| 222 | try { |
| 223 | size = Integer.parseInt(word); |
| 224 | } catch (NumberFormatException ex) { |
| 225 | } |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | return size; |
| 230 | } |
| 231 | } |