--- old/src/java.desktop/share/classes/sun/font/Font2D.java 2015-01-23 13:11:36.000000000 -0800 +++ new/src/java.desktop/share/classes/sun/font/Font2D.java 2015-01-23 13:11:35.000000000 -0800 @@ -157,6 +157,21 @@ } } + public static final int FWIDTH_NORMAL = 5; // OS/2 usWidthClass + public static final int FWEIGHT_NORMAL = 400; // OS/2 usWeightClass + public static final int FWEIGHT_BOLD = 700; // OS/2 usWeightClass + + public int getWidth() { + return FWIDTH_NORMAL; + } + + public int getWeight() { + if ((style & Font.BOLD) !=0) { + return FWEIGHT_BOLD; + } else { + return FWEIGHT_NORMAL; + } + } int getRank() { return fontRank; --- old/src/java.desktop/share/classes/sun/font/TrueTypeFont.java 2015-01-23 13:11:38.000000000 -0800 +++ new/src/java.desktop/share/classes/sun/font/TrueTypeFont.java 2015-01-23 13:11:37.000000000 -0800 @@ -963,6 +963,18 @@ setStyle(getTableBuffer(os_2Tag)); } + private int fontWidth = 0; + @Override + public int getWidth() { + return (fontWidth > 0) ? fontWidth : super.getWidth(); + } + + private int fontWeight = 0; + @Override + public int getWeight() { + return (fontWeight > 0) ? fontWeight : super.getWeight(); + } + /* TrueTypeFont can use the fsSelection fields of OS/2 table * to determine the style. In the unlikely case that doesn't exist, * can use macStyle in the 'head' table but simpler to @@ -978,8 +990,15 @@ private static final int fsSelectionBoldBit = 0x00020; private static final int fsSelectionRegularBit = 0x00040; private void setStyle(ByteBuffer os_2Table) { + if (os_2Table == null) { + return; + } + if (os_2Table.capacity() >= 8) { + fontWeight = os_2Table.getChar(4) & 0xffff; + fontWidth = os_2Table.getChar(6) & 0xffff; + } /* fsSelection is unsigned short at buffer offset 62 */ - if (os_2Table == null || os_2Table.capacity() < 64) { + if (os_2Table.capacity() < 64) { super.setStyle(); return; } --- old/src/java.desktop/share/classes/sun/font/FontFamily.java 2015-01-23 13:11:40.000000000 -0800 +++ new/src/java.desktop/share/classes/sun/font/FontFamily.java 2015-01-23 13:11:39.000000000 -0800 @@ -27,6 +27,7 @@ import java.io.File; import java.awt.Font; +import java.util.Collection; import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.Locale; @@ -134,7 +135,98 @@ return java.util.Objects.equals(newDir, existDir); } + /* + * We want a family to be of the same width and prefer medium/normal width. + * Once we find a particular width we accept more of the same width + * until we find one closer to normal when we 'evict' all existing fonts. + * So once we see a 'normal' width font we evict all members that are not + * normal width and then accept only new ones that are normal width. + * + * Once a font passes the width test we subject it to the weight test. + * For Plain we target the weight the closest that is <= NORMAL (400) + * For Bold we target the weight that is closest to BOLD (700). + * + * In the future, rather than discarding these fonts, we should + * extend the family to include these so lookups on these properties + * can locate them, as presently they will only be located by full name + * based lookup. + */ + + private int familyWidth = 0; + private boolean preferredWidth(Font2D font) { + + int newWidth = font.getWidth(); + + if (familyWidth == 0) { + familyWidth = newWidth; + return true; + } + + if (newWidth == familyWidth) { + return true; + } + + if (Math.abs(Font2D.FWIDTH_NORMAL - newWidth) < + Math.abs(Font2D.FWIDTH_NORMAL - familyWidth)) + { + if (FontUtilities.debugFonts()) { + FontUtilities.getLogger().info( + "Found more preferred width. New width = " + newWidth + + " Old width = " + familyWidth + " in font " + font + + " nulling out fonts plain: " + plain + " bold: " + bold + + " italic: " + italic + " bolditalic: " + bolditalic); + } + familyWidth = newWidth; + plain = bold = italic = bolditalic = null; + return true; + } else if (FontUtilities.debugFonts()) { + FontUtilities.getLogger().info( + "Family rejecting font " + font + + " of less preferred width " + newWidth); + } + return false; + } + + private boolean closerWeight(Font2D currFont, Font2D font, int style) { + if (familyWidth != font.getWidth()) { + return false; + } + + if (currFont == null) { + return true; + } + + if (FontUtilities.debugFonts()) { + FontUtilities.getLogger().info( + "New weight for style " + style + ". Curr.font=" + currFont + + " New font="+font+" Curr.weight="+ + currFont.getWeight()+ + " New weight="+font.getWeight()); + } + + int newWeight = font.getWeight(); + switch (style) { + case Font.PLAIN: + case Font.ITALIC: + return (newWeight <= Font2D.FWEIGHT_NORMAL && + newWeight > currFont.getWeight()); + + case Font.BOLD: + case Font.BOLD|Font.ITALIC: + return (Math.abs(newWeight - Font2D.FWEIGHT_BOLD) < + Math.abs(currFont.getWeight() - Font2D.FWEIGHT_BOLD)); + + default: + return false; + } + } + public void setFont(Font2D font, int style) { + + if (FontUtilities.isLogging()) { + FontUtilities.getLogger().info( + "Request to add " + font + " with style " + style + + " to family " + this); + } /* Allow a lower-rank font only if its a file font * from the exact same source as any previous font. */ @@ -152,19 +244,27 @@ switch (style) { case Font.PLAIN: - plain = font; + if (preferredWidth(font) && closerWeight(plain, font, style)) { + plain = font; + } break; case Font.BOLD: - bold = font; + if (preferredWidth(font) && closerWeight(bold, font, style)) { + bold = font; + } break; case Font.ITALIC: - italic = font; + if (preferredWidth(font) && closerWeight(italic, font, style)) { + italic = font; + } break; case Font.BOLD|Font.ITALIC: - bolditalic = font; + if (preferredWidth(font) && closerWeight(bolditalic, font, style)) { + bolditalic = font; + } break; default: @@ -316,6 +416,11 @@ return allLocaleNames.get(name.toLowerCase()); } + public static FontFamily[] getAllFontFamilies() { + Collection families = familyNameMap.values(); + return families.toArray(new FontFamily[0]); + } + public String toString() { return "Font family: " + familyName + --- old/src/java.desktop/macosx/classes/sun/font/CFont.java 2015-01-23 13:11:42.000000000 -0800 +++ new/src/java.desktop/macosx/classes/sun/font/CFont.java 2015-01-23 13:11:41.000000000 -0800 @@ -77,14 +77,72 @@ } private static native long createNativeFont(final String nativeFontName, - final int style, - final boolean isFakeItalic); + final int style); private static native void disposeNativeFont(final long nativeFontPtr); private boolean isFakeItalic; private String nativeFontName; private long nativeFontPtr; + private native float getWidthNative(final long nativeFontPtr); + private native float getWeightNative(final long nativeFontPtr); + + private int fontWidth = -1; + private int fontWeight = -1; + + @Override + public int getWidth() { + if (fontWidth == -1) { + // Apple use a range of -1 -> +1, where 0.0 is normal + // OpenType uses a % range from 50% -> 200% where 100% is normal + // and maps these onto the integer values 1->9. + // Since that is what Font2D.getWidth() expects, remap to that. + float fw = getWidthNative(getNativeFontPtr()); + if (fw == 0.0) { // short cut the common case + fontWidth = Font2D.FWIDTH_NORMAL; + return fontWidth; + } + fw += 1.0; fw *= 100.0; + if (fw <= 50.0) { + fontWidth = 1; + } else if (fw <= 62.5) { + fontWidth = 2; + } else if (fw <= 75.0) { + fontWidth = 3; + } else if (fw <= 87.5) { + fontWidth = 4; + } else if (fw <= 100.0) { + fontWidth = 5; + } else if (fw <= 112.5) { + fontWidth = 6; + } else if (fw <= 125.0) { + fontWidth = 7; + } else if (fw <= 150.0) { + fontWidth = 8; + } else { + fontWidth = 9; + } + } + return fontWidth; + } + + @Override + public int getWeight() { + if (fontWeight == -1) { + // Apple use a range of -1 -> +1, where 0 is medium/regular + // Map this on to the OpenType range of 100->900 where + // 500 is medium/regular. + // We'll actually map to 0->1000 but that's close enough. + float fw = getWeightNative(getNativeFontPtr()); + if (fw == 0) { + return Font2D.FWEIGHT_NORMAL; + } + fw += 1.0; fw *= 500; + fontWeight = (int)fw; + } + return fontWeight; + } + // this constructor is called from CFontWrapper.m public CFont(String name) { this(name, name); @@ -94,10 +152,11 @@ handle = new Font2DHandle(this); fullName = name; familyName = inFamilyName; - nativeFontName = inFamilyName; + nativeFontName = fullName; setStyle(); } + /* Called from CFontManager too */ public CFont(CFont other, String logicalFamilyName) { handle = new Font2DHandle(this); fullName = logicalFamilyName; @@ -109,6 +168,7 @@ public CFont createItalicVariant() { CFont font = new CFont(this, familyName); + font.nativeFontName = fullName; font.fullName = fullName + (style == Font.BOLD ? "" : "-") + "Italic-Derived"; font.style |= Font.ITALIC; @@ -118,7 +178,7 @@ protected synchronized long getNativeFontPtr() { if (nativeFontPtr == 0L) { - nativeFontPtr = createNativeFont(nativeFontName, style, isFakeItalic); + nativeFontPtr = createNativeFont(nativeFontName, style); } return nativeFontPtr; } --- old/src/java.desktop/macosx/classes/sun/font/CFontManager.java 2015-01-23 13:11:44.000000000 -0800 +++ new/src/java.desktop/macosx/classes/sun/font/CFontManager.java 2015-01-23 13:11:43.000000000 -0800 @@ -252,13 +252,42 @@ final CFont font = new CFont(fontName, fontFamilyName); registerGenericFont(font); + } - if ((font.getStyle() & Font.ITALIC) == 0) { - registerGenericFont(font.createItalicVariant(), true); + void registerItalicDerived() { + FontFamily[] famArr = FontFamily.getAllFontFamilies(); + for (int i=0; i() { public Object run() { - loadNativeFonts(); + if (!loadedAllFonts) { + loadNativeFonts(); + registerItalicDerived(); + loadedAllFonts = true; + } return null; } } --- old/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m 2015-01-23 13:11:45.000000000 -0800 +++ new/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m 2015-01-23 13:11:45.000000000 -0800 @@ -35,15 +35,11 @@ #import "AWTStrike.h" #import "CoreTextSupport.h" - -#define DEBUG - @implementation AWTFont -- (id) initWithFont:(NSFont *)font isFakeItalic:(BOOL)isFakeItalic { +- (id) initWithFont:(NSFont *)font { self = [super init]; if (self) { - fIsFakeItalic = isFakeItalic; fFont = [font retain]; fNativeCGFont = CTFontCopyGraphicsFont((CTFontRef)font, NULL); } @@ -72,7 +68,6 @@ + (AWTFont *) awtFontForName:(NSString *)name style:(int)style - isFakeItalic:(BOOL)isFakeItalic { // create font with family & size NSFont *nsFont = [NSFont fontWithName:name size:1.0]; @@ -95,7 +90,7 @@ nsFont = [[NSFontManager sharedFontManager] convertFont:nsFont toHaveTrait:NSBoldFontMask]; } - return [[[AWTFont alloc] initWithFont:nsFont isFakeItalic:isFakeItalic] autorelease]; + return [[[AWTFont alloc] initWithFont:nsFont] autorelease]; } + (NSFont *) nsFontForJavaFont:(jobject)javaFont env:(JNIEnv *)env { @@ -354,7 +349,7 @@ JNIEXPORT jlong JNICALL Java_sun_font_CFont_createNativeFont (JNIEnv *env, jclass clazz, - jstring nativeFontName, jint style, jboolean isFakeItalic) + jstring nativeFontName, jint style) { AWTFont *awtFont = nil; @@ -362,8 +357,7 @@ awtFont = [AWTFont awtFontForName:JNFJavaToNSString(env, nativeFontName) - style:style - isFakeItalic:isFakeItalic]; // autoreleased + style:style]; // autoreleased if (awtFont) { CFRetain(awtFont); // GC @@ -376,6 +370,52 @@ /* * Class: sun_font_CFont + * Method: getWidthNative + * Signature: (J)F + */ +JNIEXPORT jfloat JNICALL +Java_sun_font_CFont_getWidthNative + (JNIEnv *env, jobject cfont, jlong awtFontPtr) +{ + float widthVal; +JNF_COCOA_ENTER(env); + + AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr); + NSFont* nsFont = awtFont->fFont; + NSFontDescriptor *fontDescriptor = nsFont.fontDescriptor; + NSDictionary *fontTraits = [fontDescriptor objectForKey : NSFontTraitsAttribute]; + NSNumber *width = [fontTraits objectForKey : NSFontWidthTrait]; + widthVal = (float)[width floatValue]; + +JNF_COCOA_EXIT(env); + return (jfloat)widthVal; +} + +/* + * Class: sun_font_CFont + * Method: getWeightNative + * Signature: (J)F + */ +JNIEXPORT jfloat JNICALL +Java_sun_font_CFont_getWeightNative + (JNIEnv *env, jobject cfont, jlong awtFontPtr) +{ + float weightVal; +JNF_COCOA_ENTER(env); + + AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr); + NSFont* nsFont = awtFont->fFont; + NSFontDescriptor *fontDescriptor = nsFont.fontDescriptor; + NSDictionary *fontTraits = [fontDescriptor objectForKey : NSFontTraitsAttribute]; + NSNumber *weight = [fontTraits objectForKey : NSFontWeightTrait]; + weightVal = (float)[weight floatValue]; + +JNF_COCOA_EXIT(env); + return (jfloat)weightVal; +} + +/* + * Class: sun_font_CFont * Method: disposeNativeFont * Signature: (J)V */ --- /dev/null 2015-01-23 13:11:48.000000000 -0800 +++ new/test/java/awt/FontClass/HelvLtOblTest.java 2015-01-23 13:11:47.000000000 -0800 @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8064833 + * @summary Test correct font is obtained via famil+style + * @run main HelvLtOblTest + */ + +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; +import java.awt.RenderingHints; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.image.BufferedImage; + +public class HelvLtOblTest extends JComponent { + + static Font helvFont = null; + + static int[] codes = { 0x23, 0x4a, 0x48, 0x3, 0x4a, 0x55, 0x42, 0x4d, + 0x4a, 0x44, 0x3, + 0x53, 0x46, 0x45, 0x3, 0x55, 0x46, 0x59, 0x55, }; + + static String str = "Big italic red text"; + + public static void main(String[] args) throws Exception { + String os = System.getProperty("os.name"); + if (!os.startsWith("Mac")) { + return; + } + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + Font[] fonts = ge.getAllFonts(); + for (int i=0; i { + JFrame f = new JFrame(); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.add("Center", test); + f.pack(); + f.setVisible(true); + }); + test.compareImages(); + } + + public Dimension getPreferredSize() { + return new Dimension(400,400); + } + + public void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D)g; + FontRenderContext frc = new FontRenderContext(null, true, true); + Font f = helvFont.deriveFont(Font.PLAIN, 40); + System.out.println("font = " +f.getFontName()); + GlyphVector gv = f.createGlyphVector(frc, codes); + g.setFont(f); + g.setColor(Color.white); + g.fillRect(0,0,400,400); + g.setColor(Color.black); + g2.drawGlyphVector(gv, 5,200); + g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); + g2.drawString(str, 5, 250); + } + + void compareImages() { + BufferedImage bi0 = drawText(false); + BufferedImage bi1 = drawText(true); + compare(bi0, bi1); + } + + BufferedImage drawText(boolean doGV) { + int w = 400; + int h = 50; + BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); + Graphics2D g = bi.createGraphics(); + g.setColor(Color.white); + g.fillRect(0,0,w,h); + g.setColor(Color.black); + Font f = helvFont.deriveFont(Font.PLAIN, 40); + g.setFont(f); + int x = 5; + int y = h - 10; + if (doGV) { + FontRenderContext frc = new FontRenderContext(null, true, true); + GlyphVector gv = f.createGlyphVector(frc, codes); + g.drawGlyphVector(gv, 5, y); + } else { + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); + g.drawString(str, x, y); + } + return bi; + } + + // Need to allow for minimal rounding error, so allow each component + // to differ by 1. + void compare(BufferedImage bi0, BufferedImage bi1) { + int wid = bi0.getWidth(); + int hgt = bi0.getHeight(); + for (int x=0; x> 16; + int r1 = (rgb1 & 0xff0000) >> 16; + int rdiff = r0-r1; if (rdiff<0) rdiff = -rdiff; + int g0 = (rgb0 & 0x00ff00) >> 8; + int g1 = (rgb1 & 0x00ff00) >> 8; + int gdiff = g0-g1; if (gdiff<0) gdiff = -gdiff; + int b0 = (rgb0 & 0x0000ff); + int b1 = (rgb1 & 0x0000ff); + int bdiff = b0-b1; if (bdiff<0) bdiff = -bdiff; + if (rdiff > 1 || gdiff > 1 || bdiff > 1) { + throw new RuntimeException( + "Images differ at x=" + x + " y="+ y + " " + + Integer.toHexString(rgb0) + " vs " + + Integer.toHexString(rgb1)); + } + } + } + } + +}