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 33 import sun.font.FontConfigManager; 34 import sun.font.FontUtilities; 35 36 /** 37 * @author Shannon Hickey 38 * @author Leif Samuelsson 39 */ 40 class PangoFonts { 41 42 public static final String CHARS_DIGITS = "0123456789"; 43 44 /** 45 * Calculate a default scale factor for fonts in this L&F to match 46 * the reported resolution of the screen. 47 * Java 2D specified a default user-space scale of 72dpi. 48 * This is unlikely to correspond to that of the real screen. 49 * The Xserver reports a value which may be used to adjust for this. 50 * and Java 2D exposes it via a normalizing transform. 51 * However many Xservers report a hard-coded 90dpi whilst others report a 52 * calculated value based on possibly incorrect data. 53 * That is something that must be solved at the X11 level 54 * Note that in an X11 multi-screen environment, the default screen 55 * is the one used by the JRE so it is safe to use it here. 56 */ 57 private static double fontScale; 58 59 static { 60 fontScale = 1.0d; 61 GraphicsEnvironment ge = 62 GraphicsEnvironment.getLocalGraphicsEnvironment(); 63 64 if (!ge.isHeadless()) { 65 GraphicsConfiguration gc = 66 ge.getDefaultScreenDevice().getDefaultConfiguration(); 67 AffineTransform at = gc.getNormalizingTransform(); 68 fontScale = at.getScaleY(); 69 } 70 } 71 72 73 /** 74 * Parses a String containing a pango font description and returns 75 * a Font object. 76 * 77 * @param pangoName a String describing a pango font 78 * e.g. "Sans Italic 10" 79 * @return a Font object as a FontUIResource 80 * or null if no suitable font could be created. 81 */ 82 static Font lookupFont(String pangoName) { 83 String family = ""; 84 int style = Font.PLAIN; 85 int size = 10; 86 87 StringTokenizer tok = new StringTokenizer(pangoName); 88 89 while (tok.hasMoreTokens()) { 90 String word = tok.nextToken(); 91 92 if (word.equalsIgnoreCase("italic")) { 93 style |= Font.ITALIC; 94 } else if (word.equalsIgnoreCase("bold")) { 95 style |= Font.BOLD; 96 } else if (CHARS_DIGITS.indexOf(word.charAt(0)) != -1) { 97 try { 98 size = Integer.parseInt(word); 99 } catch (NumberFormatException ex) { 100 } 101 } else { 102 if (family.length() > 0) { 103 family += " "; 104 } 105 106 family += word; 107 } 108 } 109 110 /* 111 * Java 2D font point sizes are in a user-space scale of 72dpi. 112 * GTK allows a user to configure a "dpi" property used to scale 113 * the fonts used to match a user's preference. 114 * To match the font size of GTK apps we need to obtain this DPI and 115 * adjust as follows: 116 * Some versions of GTK use XSETTINGS if available to dynamically 117 * monitor user-initiated changes in the DPI to be used by GTK 118 * apps. This value is also made available as the Xft.dpi X resource. 119 * This is presumably a function of the font preferences API and/or 120 * the manner in which it requests the toolkit to update the default 121 * for the desktop. This dual approach is probably necessary since 122 * other versions of GTK - or perhaps some apps - determine the size 123 * to use only at start-up from that X resource. 124 * If that resource is not set then GTK scales for the DPI resolution 125 * reported by the Xserver using the formula 126 * DisplayHeight(dpy, screen) / DisplayHeightMM(dpy, screen) * 25.4 127 * (25.4mm == 1 inch). 128 * JDK tracks the Xft.dpi XSETTINGS property directly so it can 129 * dynamically change font size by tracking just that value. 130 * If that resource is not available use the same fall back formula 131 * as GTK (see calculation for fontScale). 132 * 133 * GTK's default setting for Xft.dpi is 96 dpi (and it seems -1 134 * apparently also can mean that "default"). However this default 135 * isn't used if there's no property set. The real default in the 136 * absence of a resource is the Xserver reported dpi. 137 * Finally this DPI is used to calculate the nearest Java 2D font 138 * 72 dpi font size. 139 * There are cases in which JDK behaviour may not exactly mimic 140 * GTK native app behaviour : 141 * 1) When a GTK app is not able to dynamically track the changes 142 * (does not use XSETTINGS), JDK will resize but other apps will 143 * not. This is OK as JDK is exhibiting preferred behaviour and 144 * this is probably how all later GTK apps will behave 145 * 2) When a GTK app does not use XSETTINGS and for some reason 146 * the XRDB property is not present. JDK will pick up XSETTINGS 147 * and the GTK app will use the Xserver default. Since its 148 * impossible for JDK to know that some other GTK app is not 149 * using XSETTINGS its impossible to account for this and in any 150 * case for it to be a problem the values would have to be different. 151 * It also seems unlikely to arise except when a user explicitly 152 * deletes the X resource database entry. 153 * 3) Because of rounding errors sizes may differ very slightly 154 * between JDK and GTK. To fix that would at the very least require 155 * Swing to specify floating pt font sizes. 156 * Eg "10 pts" for GTK at 96 dpi to get the same size at Java 2D's 157 * 72 dpi you'd need to specify exactly 13.33. 158 * There also some other issues to be aware of for the future: 159 * GTK specifies the Xft.dpi value as server-wide which when used 160 * on systems with 2 distinct X screens with different physical DPI 161 * the font sizes will inevitably appear different. It would have 162 * been a more user-friendly design to further adjust that one 163 * setting depending on the screen resolution to achieve perceived 164 * equivalent sizes. If such a change were ever to be made in GTK 165 * we would need to update for that. 166 */ 167 double dsize = size; 168 int dpi = 96; 169 Object value = 170 Toolkit.getDefaultToolkit().getDesktopProperty("gnome.Xft/DPI"); 171 if (value instanceof Integer) { 172 dpi = ((Integer)value).intValue() / 1024; 173 if (dpi == -1) { 174 dpi = 96; 175 } 176 if (dpi < 50) { /* 50 dpi is the minimum value gnome allows */ 177 dpi = 50; 178 } 179 /* The Java rasteriser assumes pts are in a user space of 180 * 72 dpi, so we need to adjust for that. 181 */ 182 dsize = ((double)(dpi * size)/ 72.0); 183 } else { 184 /* If there's no property, GTK scales for the resolution 185 * reported by the Xserver using the formula listed above. 186 * fontScale already accounts for the 72 dpi Java 2D space. 187 */ 188 dsize = size * fontScale; 189 } 190 191 /* Round size to nearest integer pt size */ 192 size = (int)(dsize + 0.5); 193 if (size < 1) { 194 size = 1; 195 } 196 197 String fcFamilyLC = family.toLowerCase(); 198 if (FontUtilities.mapFcName(fcFamilyLC) != null) { 199 /* family is a Fc/Pango logical font which we need to expand. */ 200 return FontUtilities.getFontConfigFUIR(fcFamilyLC, style, size); 201 } else { 202 /* It's a physical font which we will create with a fallback */ 203 Font font = new FontUIResource(family, style, size); 204 return FontUtilities.getCompositeFontUIResource(font); 205 } 206 } 207 208 /** 209 * Parses a String containing a pango font description and returns 210 * the (unscaled) font size as an integer. 211 * 212 * @param pangoName a String describing a pango font 213 * @return the size of the font described by pangoName (e.g. if 214 * pangoName is "Sans Italic 10", then this method returns 10) 215 */ 216 static int getFontSize(String pangoName) { 217 int size = 10; 218 219 StringTokenizer tok = new StringTokenizer(pangoName); 220 while (tok.hasMoreTokens()) { 221 String word = tok.nextToken(); 222 223 if (CHARS_DIGITS.indexOf(word.charAt(0)) != -1) { 224 try { 225 size = Integer.parseInt(word); 226 } catch (NumberFormatException ex) { 227 } 228 } 229 } 230 231 return size; 232 } 233 }