mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-13 07:17:13 -06:00
Fonts: HiDPIUtils: improved vertical position correction of text (on Windows) for various fonts
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.util;
|
package com.formdev.flatlaf.util;
|
||||||
|
|
||||||
|
import java.awt.Font;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.font.GlyphVector;
|
import java.awt.font.GlyphVector;
|
||||||
@@ -141,37 +142,101 @@ public class HiDPIUtils
|
|||||||
if( !useTextYCorrection() || !SystemInfo.isWindows )
|
if( !useTextYCorrection() || !SystemInfo.isWindows )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if( !SystemInfo.isJava_9_orLater )
|
if( !SystemInfo.isJava_9_orLater ) {
|
||||||
return UIScale.getUserScaleFactor() > 1 ? -UIScale.scale( 0.625f ) : 0;
|
// Java 8
|
||||||
|
float scaleFactor = getUserScaleFactor();
|
||||||
|
if( scaleFactor > 1 ) {
|
||||||
|
switch( g.getFont().getFamily() ) {
|
||||||
|
case "Segoe UI":
|
||||||
|
case "Segoe UI Light":
|
||||||
|
case "Segoe UI Semibold":
|
||||||
|
return -((scaleFactor == 2.25f || scaleFactor == 4f ? 0.875f : 0.625f) * scaleFactor);
|
||||||
|
|
||||||
AffineTransform t = g.getTransform();
|
case "Noto Sans":
|
||||||
double scaleY = t.getScaleY();
|
case "Open Sans":
|
||||||
if( scaleY < 1.25 )
|
return -(0.3f * scaleFactor);
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Text is painted at slightly different Y positions depending on scale factor
|
case "Verdana":
|
||||||
// and Y position of component.
|
return -((scaleFactor < 2 ? 0.4f : 0.3f) * scaleFactor);
|
||||||
// The exact reason is not yet known (to me), but there are several factors:
|
}
|
||||||
// - fractional scale factors result in fractional component Y device coordinates
|
}
|
||||||
// - fractional text Y device coordinates are rounded for horizontal lines of characters
|
} else {
|
||||||
// - maybe different rounding methods for drawing primitives (e.g. rectangle) and text
|
// Java 9 and later
|
||||||
// - Java adds 0.5 to X/Y positions before drawing string in BufferedTextPipe.enqueueGlyphList()
|
|
||||||
|
|
||||||
// this is not the optimal solution, but works very good in most cases
|
// Text is painted at slightly different Y positions depending on scale factor
|
||||||
// (tested with class FlatPaintingStringTest on Windows 10 with font "Segoe UI")
|
// and Y position of component.
|
||||||
if( scaleY <= 1.25 )
|
// The exact reason is not yet known (to me), but there are several factors:
|
||||||
return -0.875f;
|
// - fractional scale factors result in fractional component Y device coordinates
|
||||||
if( scaleY <= 1.5 )
|
// - fractional text Y device coordinates are rounded for horizontal lines of characters
|
||||||
return -0.625f;
|
// - maybe different rounding methods for drawing primitives (e.g. rectangle) and text
|
||||||
if( scaleY <= 1.75 )
|
// - Java adds 0.5 to X/Y positions before drawing string in BufferedTextPipe.enqueueGlyphList()
|
||||||
return -0.875f;
|
|
||||||
if( scaleY <= 2.0 )
|
// this is not the optimal solution, but works very good in most cases
|
||||||
return -0.75f;
|
// (tested with class FlatPaintingStringTest on Windows 11)
|
||||||
if( scaleY <= 2.25 )
|
|
||||||
return -0.875f;
|
switch( g.getFont().getFamily() ) {
|
||||||
if( scaleY <= 3.5 )
|
case "Segoe UI":
|
||||||
return -0.75f;
|
case "Segoe UI Light":
|
||||||
return -0.875f;
|
case "Segoe UI Semibold":
|
||||||
|
case "Verdana":
|
||||||
|
case Font.DIALOG:
|
||||||
|
case Font.SANS_SERIF:
|
||||||
|
return correctionForScaleY( g, CORRECTION_SEGOE_UI );
|
||||||
|
|
||||||
|
case "Tahoma":
|
||||||
|
return correctionForScaleY( g, CORRECTION_TAHOMA );
|
||||||
|
|
||||||
|
case "Inter":
|
||||||
|
case "Inter Light":
|
||||||
|
case "Inter Semi Bold":
|
||||||
|
case "Roboto":
|
||||||
|
return correctionForScaleY( g, CORRECTION_INTER );
|
||||||
|
|
||||||
|
case "Noto Sans":
|
||||||
|
case "Open Sans":
|
||||||
|
return correctionForScaleY( g, CORRECTION_OPEN_SANS );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final float[]
|
||||||
|
SCALE_FACTORS = { 1.25f, 1.5f, 1.75f, 2f, 2.25f, 2.5f, 3f, 3.5f, 4f },
|
||||||
|
|
||||||
|
CORRECTION_SEGOE_UI = { -0.5f, -0.5f, -0.625f, -0.75f, -0.75f, -0.75f, -0.75f, -0.75f, -0.875f },
|
||||||
|
CORRECTION_TAHOMA = { -0.25f, -0.25f, -0.25f, -0f, -0.125f, -0.125f, -0.125f, -0.125f, -0f },
|
||||||
|
CORRECTION_INTER = { -0.25f, -0.25f, -0.25f, -0f, -0.125f, -0.125f, -0f, -0.25f, -0f },
|
||||||
|
CORRECTION_OPEN_SANS = { -0.5f, -0.25f, -0.25f, -0f, -0.25f, -0.25f, -0f, -0.25f, -0.25f };
|
||||||
|
|
||||||
|
private static float correctionForScaleY( Graphics2D g, float[] correction ) {
|
||||||
|
if( correction.length != 9 )
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
|
||||||
|
double scaleY = g.getTransform().getScaleY();
|
||||||
|
return (scaleY < 1.25) ? 0 : correction[scaleFactor2index( (float) scaleY )];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int scaleFactor2index( float scaleFactor ) {
|
||||||
|
for( int i = 0; i < SCALE_FACTORS.length; i++ ) {
|
||||||
|
if( scaleFactor <= SCALE_FACTORS[i] )
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return SCALE_FACTORS.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Boolean useDebugScaleFactor;
|
||||||
|
|
||||||
|
private static boolean useDebugScaleFactor() {
|
||||||
|
if( useDebugScaleFactor == null )
|
||||||
|
useDebugScaleFactor = FlatSystemProperties.getBoolean( "FlatLaf.debug.HiDPIUtils.useDebugScaleFactor", false );
|
||||||
|
return useDebugScaleFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getUserScaleFactor() {
|
||||||
|
return !useDebugScaleFactor()
|
||||||
|
? UIScale.getUserScaleFactor()
|
||||||
|
: Float.parseFloat( System.getProperty( "FlatLaf.debug.HiDPIUtils.debugScaleFactor", "1" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ class DemoFrame
|
|||||||
fontMenu.addSeparator();
|
fontMenu.addSeparator();
|
||||||
ArrayList<String> families = new ArrayList<>( Arrays.asList(
|
ArrayList<String> families = new ArrayList<>( Arrays.asList(
|
||||||
"Arial", "Cantarell", "Comic Sans MS", "DejaVu Sans",
|
"Arial", "Cantarell", "Comic Sans MS", "DejaVu Sans",
|
||||||
"Dialog", "Inter", "Liberation Sans", "Noto Sans", "Roboto",
|
"Dialog", "Inter", "Liberation Sans", "Noto Sans", "Open Sans", "Roboto",
|
||||||
"SansSerif", "Segoe UI", "Serif", "Tahoma", "Ubuntu", "Verdana" ) );
|
"SansSerif", "Segoe UI", "Serif", "Tahoma", "Ubuntu", "Verdana" ) );
|
||||||
if( !families.contains( currentFamily ) )
|
if( !families.contains( currentFamily ) )
|
||||||
families.add( currentFamily );
|
families.add( currentFamily );
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ import javax.swing.*;
|
|||||||
import javax.swing.text.StyleContext;
|
import javax.swing.text.StyleContext;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
import com.formdev.flatlaf.FlatSystemProperties;
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
|
import com.formdev.flatlaf.fonts.inter.FlatInterFont;
|
||||||
|
import com.formdev.flatlaf.fonts.jetbrains_mono.FlatJetBrainsMonoFont;
|
||||||
import com.formdev.flatlaf.util.Graphics2DProxy;
|
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.JavaCompatibility;
|
import com.formdev.flatlaf.util.JavaCompatibility;
|
||||||
@@ -45,8 +47,12 @@ public class FlatPaintingStringTest
|
|||||||
public static void main( String[] args ) {
|
public static void main( String[] args ) {
|
||||||
System.setProperty( FlatSystemProperties.UI_SCALE, "1x" );
|
System.setProperty( FlatSystemProperties.UI_SCALE, "1x" );
|
||||||
System.setProperty( "sun.java2d.uiScale", "1x" );
|
System.setProperty( "sun.java2d.uiScale", "1x" );
|
||||||
|
System.setProperty( "FlatLaf.debug.HiDPIUtils.useDebugScaleFactor", "true" );
|
||||||
|
|
||||||
SwingUtilities.invokeLater( () -> {
|
SwingUtilities.invokeLater( () -> {
|
||||||
|
FlatInterFont.install();
|
||||||
|
FlatJetBrainsMonoFont.install();
|
||||||
|
|
||||||
FlatTestFrame frame = FlatTestFrame.create( args, "FlatPaintingStringTest" );
|
FlatTestFrame frame = FlatTestFrame.create( args, "FlatPaintingStringTest" );
|
||||||
|
|
||||||
ToolTipManager.sharedInstance().setInitialDelay( 0 );
|
ToolTipManager.sharedInstance().setInitialDelay( 0 );
|
||||||
@@ -70,30 +76,32 @@ public class FlatPaintingStringTest
|
|||||||
String[] families = {
|
String[] families = {
|
||||||
// regular
|
// regular
|
||||||
"Arial", "Cantarell", "DejaVu Sans",
|
"Arial", "Cantarell", "DejaVu Sans",
|
||||||
"Dialog", "Helvetica Neue", "Inter", "Liberation Sans", "Noto Sans", "Open Sans", "Roboto",
|
"Dialog", "Helvetica Neue", "Liberation Sans", "Noto Sans", "Open Sans", "Roboto",
|
||||||
"SansSerif", "Segoe UI", "Tahoma", "Ubuntu", "Verdana", ".SF NS Text",
|
"SansSerif", "Segoe UI", "Tahoma", "Ubuntu", "Verdana", ".SF NS Text",
|
||||||
|
FlatInterFont.FAMILY,
|
||||||
|
|
||||||
// light, semibold
|
// light, semibold
|
||||||
"Segoe UI Light", "Segoe UI Semibold",
|
"Segoe UI Light", "Segoe UI Semibold",
|
||||||
"HelveticaNeue-Thin", "HelveticaNeue-Medium",
|
"HelveticaNeue-Thin", "HelveticaNeue-Medium",
|
||||||
"Lato Light", "Ubuntu Light", "Cantarell Light",
|
"Lato Light", "Ubuntu Light", "Cantarell Light",
|
||||||
"Lato Semibold", "Ubuntu Medium", "Montserrat SemiBold",
|
"Lato Semibold", "Ubuntu Medium", "Montserrat SemiBold",
|
||||||
|
FlatInterFont.FAMILY_LIGHT, FlatInterFont.FAMILY_SEMIBOLD,
|
||||||
|
|
||||||
// monospaced
|
// monospaced
|
||||||
"Monospaced", "Consolas", "Courier New", "Menlo", "Liberation Mono", "Ubuntu Mono",
|
"Monospaced", "Consolas", "Courier New", "Menlo", "Liberation Mono", "Ubuntu Mono",
|
||||||
|
FlatJetBrainsMonoFont.FAMILY,
|
||||||
};
|
};
|
||||||
Arrays.sort( families, String.CASE_INSENSITIVE_ORDER );
|
Arrays.sort( families, String.CASE_INSENSITIVE_ORDER );
|
||||||
DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>();
|
DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>();
|
||||||
model.addElement( currentFamily );
|
|
||||||
for( String family : families ) {
|
for( String family : families ) {
|
||||||
if( !family.equals( currentFamily ) && Arrays.binarySearch( availableFontFamilyNames, family ) >= 0 )
|
if( Arrays.binarySearch( availableFontFamilyNames, family ) >= 0 )
|
||||||
model.addElement( family );
|
model.addElement( family );
|
||||||
}
|
}
|
||||||
fontField.setModel( model );
|
fontField.setModel( model );
|
||||||
fontField.setSelectedItem( currentFamily );
|
fontField.setSelectedItem( currentFamily );
|
||||||
updateFontMetricsLabel();
|
updateFontMetricsLabel();
|
||||||
|
|
||||||
add( new JLabel() );
|
add( new JLabel(), "newLine" );
|
||||||
add( new JLabel( "none" ) );
|
add( new JLabel( "none" ) );
|
||||||
add( new JLabel( "flatlaf" ) );
|
add( new JLabel( "flatlaf" ) );
|
||||||
add( new JLabel() );
|
add( new JLabel() );
|
||||||
@@ -120,9 +128,8 @@ public class FlatPaintingStringTest
|
|||||||
|
|
||||||
YCorrectionFunction none = (g, scaleFactor) -> 0;
|
YCorrectionFunction none = (g, scaleFactor) -> 0;
|
||||||
YCorrectionFunction flatlaf = (g, scaleFactor) -> {
|
YCorrectionFunction flatlaf = (g, scaleFactor) -> {
|
||||||
return SystemInfo.isJava_9_orLater
|
System.setProperty( "FlatLaf.debug.HiDPIUtils.debugScaleFactor", Float.toString( scaleFactor ) );
|
||||||
? HiDPIUtils.computeTextYCorrection( g )
|
return HiDPIUtils.computeTextYCorrection( g );
|
||||||
: (scaleFactor > 1 ? -(0.625f * scaleFactor) : 0);
|
|
||||||
};
|
};
|
||||||
YCorrectionFunction ty = (g, scaleFactor) -> {
|
YCorrectionFunction ty = (g, scaleFactor) -> {
|
||||||
// Based on translateY, which is the scaled Y coordinate translation of the graphics context.
|
// Based on translateY, which is the scaled Y coordinate translation of the graphics context.
|
||||||
@@ -214,8 +221,8 @@ public class FlatPaintingStringTest
|
|||||||
// columns
|
// columns
|
||||||
"[fill]",
|
"[fill]",
|
||||||
// rows
|
// rows
|
||||||
"[top]unrel" +
|
"[shrink 0,top]unrel" +
|
||||||
"[top]"));
|
"[shrink 0,top]"));
|
||||||
|
|
||||||
//======== panel1 ========
|
//======== panel1 ========
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
JFDML JFormDesigner: "7.0.5.0.404" Java: "17.0.2" encoding: "UTF-8"
|
JFDML JFormDesigner: "8.0.0.0.194" Java: "17.0.2" encoding: "UTF-8"
|
||||||
|
|
||||||
new FormModel {
|
new FormModel {
|
||||||
contentType: "form/swing"
|
contentType: "form/swing"
|
||||||
@@ -9,7 +9,7 @@ new FormModel {
|
|||||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||||
"$layoutConstraints": "insets dialog,hidemode 3"
|
"$layoutConstraints": "insets dialog,hidemode 3"
|
||||||
"$columnConstraints": "[fill]"
|
"$columnConstraints": "[fill]"
|
||||||
"$rowConstraints": "[top]unrel[top]"
|
"$rowConstraints": "[shrink 0,top]unrel[shrink 0,top]"
|
||||||
} ) {
|
} ) {
|
||||||
name: "this"
|
name: "this"
|
||||||
"border": sfield com.jformdesigner.model.FormObject NULL_VALUE
|
"border": sfield com.jformdesigner.model.FormObject NULL_VALUE
|
||||||
|
|||||||
Reference in New Issue
Block a user