1 /* 2 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * 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 * There also some other issues to be aware of for the future: 154 * GTK specifies the Xft.dpi value as server-wide which when used 155 * on systems with 2 distinct X screens with different physical DPI 156 * the font sizes will inevitably appear different. It would have 157 * been a more user-friendly design to further adjust that one 158 * setting depending on the screen resolution to achieve perceived 159 * equivalent sizes. If such a change were ever to be made in GTK 160 * we would need to update for that. 161 */ 162 double dsize = size; 163 int dpi = 96; 164 Object value = 165 Toolkit.getDefaultToolkit().getDesktopProperty("gnome.Xft/DPI"); 166 if (value instanceof Integer) { 167 dpi = ((Integer)value).intValue() / 1024; 168 if (dpi == -1) { 169 dpi = 96; 170 } 171 if (dpi < 50) { /* 50 dpi is the minimum value gnome allows */ 172 dpi = 50; 173 } 174 /* The Java rasteriser assumes pts are in a user space of 175 * 72 dpi, so we need to adjust for that. 176 */ 177 dsize = ((double)(dpi * size)/ 72.0); 178 } else { 179 /* If there's no property, GTK scales for the resolution 180 * reported by the Xserver using the formula listed above. 181 * fontScale already accounts for the 72 dpi Java 2D space. 182 */ 183 dsize = size * fontScale; 184 } 185 186 /* Round size to nearest integer pt size */ 187 size = (int)(dsize + 0.5); 188 if (size < 1) { 189 size = 1; 190 } 191 192 String fcFamilyLC = family.toLowerCase(); 193 if (FontUtilities.mapFcName(fcFamilyLC) != null) { 194 /* family is a Fc/Pango logical font which we need to expand. */ 195 Font font = FontUtilities.getFontConfigFUIR(fcFamilyLC, style, size); 196 font = font.deriveFont(style, (float)dsize); 197 return new FontUIResource(font); 198 } else { 199 /* It's a physical font which we will create with a fallback */ 200 Font font = new Font(family, style, size); 201 /* a roundabout way to set the font size in floating points */ 202 font = font.deriveFont(style, (float)dsize); 203 FontUIResource fuir = new FontUIResource(font); 204 return FontUtilities.getCompositeFontUIResource(fuir); 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 }