1 /* 2 * Copyright (c) 2011, 2014, 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.javafx.webkit.prism; 27 28 import com.sun.javafx.font.CharToGlyphMapper; 29 import com.sun.javafx.font.FontFactory; 30 import com.sun.javafx.font.FontResource; 31 import com.sun.javafx.font.FontStrike; 32 import com.sun.javafx.font.PGFont; 33 import com.sun.javafx.geom.BaseBounds; 34 import com.sun.javafx.geom.transform.BaseTransform; 35 import com.sun.javafx.scene.text.GlyphList; 36 import com.sun.javafx.scene.text.TextLayout; 37 import com.sun.javafx.text.TextRun; 38 import static com.sun.javafx.webkit.prism.TextUtilities.getLayoutWidth; 39 import static com.sun.javafx.webkit.prism.TextUtilities.getLayoutBounds; 40 import com.sun.prism.GraphicsPipeline; 41 import com.sun.webkit.graphics.WCFont; 42 import java.util.HashMap; 43 import java.util.logging.Level; 44 import java.util.logging.Logger; 45 46 final class WCFontImpl extends WCFont { 47 private final static Logger log = 48 Logger.getLogger(WCFontImpl.class.getName()); 49 50 private static final HashMap<String, String> FONT_MAP = new HashMap<String, String>(); 51 52 static WCFont getFont(String name, boolean bold, boolean italic, float size) { 53 FontFactory factory = GraphicsPipeline.getPipeline().getFontFactory(); 54 synchronized (FONT_MAP) { 55 if (FONT_MAP.isEmpty()) { 56 FONT_MAP.put("serif", "Serif"); 57 FONT_MAP.put("dialog", "SansSerif"); 58 FONT_MAP.put("helvetica", "SansSerif"); 59 FONT_MAP.put("sansserif", "SansSerif"); 60 FONT_MAP.put("sans-serif", "SansSerif"); 61 FONT_MAP.put("monospace", "Monospaced"); 62 FONT_MAP.put("monospaced", "Monospaced"); 63 for (String family : factory.getFontFamilyNames()) { 64 FONT_MAP.put(family.toLowerCase(), family); 65 } 66 } 67 } 68 String family = FONT_MAP.get(name.toLowerCase()); 69 if (log.isLoggable(Level.FINE)) { 70 StringBuilder sb = new StringBuilder("WCFontImpl.get("); 71 sb.append(name).append(", ").append(size); 72 if (bold) { 73 sb.append(", bold"); 74 } 75 if (italic) { 76 sb.append(", italic"); 77 } 78 log.fine(sb.append(") = ").append(family).toString()); 79 } 80 return (family != null) 81 ? new WCFontImpl(factory.createFont(family, bold, italic, size)) 82 : null; 83 } 84 85 private final PGFont font; 86 87 WCFontImpl(PGFont font) { 88 this.font = font; 89 } 90 91 @Override public WCFont deriveFont(float size) { 92 FontFactory factory = GraphicsPipeline.getPipeline().getFontFactory(); 93 return new WCFontImpl( 94 factory.deriveFont(font, 95 font.getFontResource().isBold(), 96 font.getFontResource().isItalic(), 97 size)); 98 } 99 100 @Override public int getOffsetForPosition(String str, float x) { 101 TextLayout layout = TextUtilities.createLayout(str, font); 102 GlyphList[] runs = layout.getRuns(); 103 TextRun run = (TextRun) runs[0]; 104 int offset = run.getOffsetAtX(x, null); 105 if (log.isLoggable(Level.FINE)) { 106 log.fine(String.format("str='%s' (length=%d), x=%.2f => %d", 107 str, str.length(), x, offset)); 108 } 109 return offset; 110 } 111 112 private FontStrike strike; 113 private FontStrike getFontStrike() 114 { 115 if (strike == null) { 116 strike = font.getStrike(BaseTransform.IDENTITY_TRANSFORM, FontResource.AA_LCD); 117 } 118 return strike; 119 } 120 121 @Override public double getGlyphWidth(int glyph) { 122 return getFontStrike().getFontResource().getAdvance(glyph, font.getSize()); 123 } 124 125 @Override public float getXHeight() { 126 return getFontStrike().getMetrics().getXHeight(); 127 } 128 129 @Override public int[] getGlyphCodes(char[] chars) { 130 int[] glyphs = new int[chars.length]; 131 CharToGlyphMapper mapper = getFontStrike().getFontResource().getGlyphMapper(); 132 mapper.charsToGlyphs(chars.length, chars, glyphs); 133 return glyphs; 134 } 135 136 @Override public double getStringWidth(String str) { 137 double result = getLayoutWidth(str, font); 138 if (log.isLoggable(Level.FINE)) { 139 log.fine(String.format("str='%s' (length=%d) => %.1f", 140 str, str.length(), result)); 141 } 142 return result; 143 } 144 145 @Override public double[] getStringBounds(String str, int from, int to, boolean rtl) { 146 float beforeWidth = getLayoutWidth(str.substring(0, from), font); 147 BaseBounds bounds = getLayoutBounds(str.substring(0, to), font); 148 double[] result = new double[] { 149 beforeWidth, // see RTL case below 150 0, // not really used 151 bounds.getWidth() - beforeWidth, 152 bounds.getHeight(), // not really used 153 }; 154 if (rtl) { 155 float totalWidth = getLayoutWidth(str, font); 156 result[0] = totalWidth - bounds.getWidth(); 157 } 158 if (log.isLoggable(Level.FINE)) { 159 log.fine(String.format( 160 "str='%s' (length=%d) [%d, %d], rtl=%b => [%.1f, %.1f + %.1f x %.1f]", 161 str, str.length(), from, to, rtl, 162 result[0], result[1], result[2], result[3])); 163 } 164 return result; 165 } 166 167 public float getAscent() { 168 // REMIND: This method needs to require a render context. 169 float res = - getFontStrike().getMetrics().getAscent(); 170 if (log.isLoggable(Level.FINER)) { 171 log.log(Level.FINER, "getAscent({0}, {1}) = {2}", 172 new Object[] {font.getName(), font.getSize(), 173 res}); 174 } 175 return res; 176 } 177 178 public float getDescent() { 179 // REMIND: This method needs to require a render context. 180 float res = getFontStrike().getMetrics().getDescent(); 181 if (log.isLoggable(Level.FINER)) { 182 log.log(Level.FINER, "getDescent({0}, {1}) = {2}", 183 new Object[] {font.getName(), font.getSize(), 184 res}); 185 } 186 return res; 187 } 188 189 public float getLineSpacing() { 190 // REMIND: This method needs to require a render context. 191 float res = getFontStrike().getMetrics().getLineHeight(); 192 if (log.isLoggable(Level.FINER)) { 193 log.log(Level.FINER, "getLineSpacing({0}, {1}) = {2}", 194 new Object[] {font.getName(), font.getSize(), 195 res}); 196 } 197 return res; 198 } 199 200 public float getLineGap() { 201 // REMIND: This method needs to require a render context. 202 float res = getFontStrike().getMetrics().getLineGap(); 203 if (log.isLoggable(Level.FINER)) { 204 log.log(Level.FINER, "getLineGap({0}, {1}) = {2}", 205 new Object[] {font.getName(), font.getSize(), 206 res }); 207 } 208 return res; 209 } 210 211 public boolean hasUniformLineMetrics() { 212 return false; 213 } 214 215 public Object getPlatformFont() { 216 return font; 217 } 218 }