>From c344cb9219269f68c535422d556e1e10644a9f21 Mon Sep 17 00:00:00 2001
From: Rafal Kolanski <xs@xaph.net>
Date: Fri, 28 Aug 2015 13:20:39 +1000
Subject: [PATCH] Allow specification of sizes for fallback fonts.

Not all fonts are the same pixel size for a given point size and
presumably the user wants the final metrics to match.

Given that now the user can specify preferred fallback font sizes, that
leaves system-fallback fonts, which don't come with size info. Since
java.awt.GraphicsEnvironment.getAllFonts returns a font size of 1, we
scale those fonts up to the main font point size.

Font replacement also preserves any affine transforms on the original
font.

So if you know what you're doing, you can set up fonts precisely the way
you like, and if you don't, you can just select the system font fallback
behavior and get the previous outcome.
---
 org/gjt/sp/jedit/options/TextAreaOptionPane.java |   3 +-
 org/gjt/sp/jedit/syntax/Chunk.java               | 110 ++++++++++++++++-------
 2 files changed, 79 insertions(+), 34 deletions(-)

diff --git a/org/gjt/sp/jedit/options/TextAreaOptionPane.java b/org/gjt/sp/jedit/options/TextAreaOptionPane.java
index 7fb0277..202097c 100644
--- a/org/gjt/sp/jedit/options/TextAreaOptionPane.java
+++ b/org/gjt/sp/jedit/options/TextAreaOptionPane.java
@@ -410,7 +410,6 @@ public class TextAreaOptionPane extends AbstractOptionPane
 
 				if (selected != null)
 				{
-					selected = selected.deriveFont(Font.PLAIN, 12);
 					fontsModel.addElement(selected);
 					fonts.setSelectedIndex(fontsModel.size() - 1);
 				}
@@ -490,7 +489,7 @@ public class TextAreaOptionPane extends AbstractOptionPane
 								   index,
 								   isSelected,
 								   cellHasFocus);
-				setText(f.getFamily());
+				setText(f.getFamily() + " " + f.getSize());
 				return this;
 			}
 
diff --git a/org/gjt/sp/jedit/syntax/Chunk.java b/org/gjt/sp/jedit/syntax/Chunk.java
index c8fd33f..ebd3eff 100644
--- a/org/gjt/sp/jedit/syntax/Chunk.java
+++ b/org/gjt/sp/jedit/syntax/Chunk.java
@@ -34,6 +34,7 @@ import java.util.LinkedHashMap;
 import java.util.Map;
 import java.lang.ref.SoftReference;
 
+import org.gjt.sp.jedit.jEdit;
 import org.gjt.sp.jedit.Debug;
 import org.gjt.sp.jedit.IPropertyManager;
 //}}}
@@ -241,9 +242,9 @@ public class Chunk extends Token
 				 * doesn't match any installed fonts. The following
 				 * check skips fonts that don't exist.
 				 */
-				Font f = new Font(family, Font.PLAIN, 12);
-				if (!"dialog".equalsIgnoreCase(f.getFamily()) ||
-					"dialog".equalsIgnoreCase(family))
+				Font f = jEdit.getFontProperty("view.fontSubstList." + i);
+				if (f != null && (!"dialog".equalsIgnoreCase(f.getFamily()) ||
+						"dialog".equalsIgnoreCase(f.getFamily())))
 					userFonts.add(f);
 				i++;
 			}
@@ -256,6 +257,67 @@ public class Chunk extends Token
 		glyphCache = null;
 	} //}}}
 
+	//{{{ getSubstFont() method
+	/**
+	 * Returns the first font which can display a character from
+	 * configured substitution candidates, or null if there is no
+	 * such font.
+	 */
+	public static Font getSubstFont(int codepoint)
+	{
+		// Workaround for a problem reported in SF.net patch #3480246
+		// > If font substitution with system fonts is enabled,
+		// > I get for inserted control characters strange mathematical
+		// > symbols from a non-unicode font in my system.
+		if (Character.isISOControl(codepoint))
+			return null;
+
+		for (Font candidate: getFontSubstList())
+		{
+			if (candidate.canDisplay(codepoint))
+			{
+				return candidate;
+			}
+		}
+		return null;
+	} //}}}
+
+	//{{{ deriveSubstFont() method
+	/**
+	 * Derives a font to match the main font for purposes of
+	 * font substitution.
+	 * Preserves any transformations from main font.
+	 * For system-fallback fonts, derives size and style from main font.
+	 *
+	 * @param mainFont Font to derive from
+	 * @param candidateFont Font to transform
+	 */
+	public static Font deriveSubstFont(Font mainFont, Font candidateFont)
+	{
+		// adopt subst font family and size, but preserve any transformations
+		// i.e. if font is squashed/sheared, subst font glyphs should be squashed
+		Font substFont = candidateFont.deriveFont(mainFont.getTransform());
+
+		// scale up system fonts (point size 1) to size of main font
+		if (substFont.getSize() == 1)
+			substFont = substFont.deriveFont(mainFont.getStyle(),
+				mainFont.getSize());
+
+		return substFont;
+	} //}}}
+
+	//{{{ usedFontSubstitution() method
+	/**
+	 * Returns true if font substitution was used in the layout of this chunk.
+	 * If substitution was not used, the chunk may be assumed to be composed
+	 * of one glyph using a single font.
+	 */
+	public boolean usedFontSubstitution()
+	{
+		return (fontSubstEnabled && glyphs != null && glyphs.length > 1);
+	}
+	//}}}
+
 	//{{{ Package private members
 
 	//{{{ Instance variables
@@ -514,6 +576,14 @@ public class Chunk extends Token
 	//}}}
 
 	//{{{ getFontSubstList() method
+	/**
+	 * Obtain a list of preferred fallback fonts as specified by the user
+	 * (see Text Area in Global Options), as well as a list of all fonts
+	 * specified in the system.
+	 * Note that preferred fonts are returned with sizes as specified by the
+	 * user, but system fonts all have a point size of 1. These should be
+	 * scaled up once the main font is known (see layoutGlyphs()).
+	 */
 	private static Font[] getFontSubstList()
 	{
 		if (fontSubstList == null)
@@ -543,31 +613,6 @@ public class Chunk extends Token
 		return fontSubstList;
 	} //}}}
 
-	//{{{ getSubstFont() method
-	/**
-	 * Returns the first font which can display a character from
-	 * configured substitution candidates, or null if there is no
-	 * such font.
-	 */
-	private static Font getSubstFont(int codepoint)
-	{
-		// Workaround for a problem reported in SF.net patch #3480246
-		// > If font substitution with system fonts is enabled,
-		// > I get for inserted control characters strange mathematical
-		// > symbols from a non-unicode font in my system.
-		if (Character.isISOControl(codepoint))
-			return null;
-
-		for (Font candidate: getFontSubstList())
-		{
-			if (candidate.canDisplay(codepoint))
-			{
-				return candidate;
-			}
-		}
-		return null;
-	} //}}}
-
 	//{{{ drawGlyphs() method
 	/**
 	 * Draws the internal list of glyph vectors into the given
@@ -663,8 +708,10 @@ public class Chunk extends Token
 			int charCount = Character.charCount(nextChar);
 			assert !mainFont.canDisplay(nextChar);
 			Font substFont = getSubstFont(nextChar);
+
 			if (substFont != null)
 			{
+				substFont = deriveSubstFont(mainFont, substFont);
 				subst.addRange(substFont, charCount);
 			}
 			else
@@ -758,10 +805,9 @@ public class Chunk extends Token
 			{
 				return;
 			}
-			Font font = (rangeFont == null) ?
-				mainFont :
-				rangeFont.deriveFont(mainFont.getStyle(),
-					mainFont.getSize());
+
+			Font font = (rangeFont == null) ? mainFont : rangeFont;
+
 			GlyphVector gv = layoutGlyphVector(font, frc,
 				text, rangeStart, rangeStart + rangeLength);
 			glyphs.add(gv);
-- 
2.1.0

