]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Fonts database for Java renderer
authorPeter Bernard West <pbwest@apache.org>
Sun, 23 May 2004 10:37:43 +0000 (10:37 +0000)
committerPeter Bernard West <pbwest@apache.org>
Sun, 23 May 2004 10:37:43 +0000 (10:37 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/FOP_0-20-0_Alt-Design@197615 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/fop/render/awt/Fonts.java [new file with mode: 0644]

diff --git a/src/java/org/apache/fop/render/awt/Fonts.java b/src/java/org/apache/fop/render/awt/Fonts.java
new file mode 100644 (file)
index 0000000..8951af7
--- /dev/null
@@ -0,0 +1,514 @@
+/*
+ *
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Created on 23/05/2004
+ * $Id$
+ */
+package org.apache.fop.render.awt;
+
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.GraphicsEnvironment;
+import java.awt.font.FontRenderContext;
+import java.awt.image.BufferedImage;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Java font selection is based on family names.  It seems that Java
+ * handles font mapping something like this:<br>
+ * Given a set of physical fonts like, e.g., Arial, Java reports them as
+ * <pre>
+ * font face: Arial
+ *     logical:Arial
+ *     family:Arial
+ *     PSName:ArialMT
+ *     Style:
+ *         PLAIN
+ *     FAMILY
+ *     WEIGHT
+ *     POSTURE
+ *     SIZE
+ *     TRANSFORM
+ * font face: Arial Cursiva
+ *     logical:Arial Cursiva
+ *     family:Arial
+ *     PSName:Arial-ItalicMT
+ *     Style:
+ *         PLAIN
+ *     FAMILY
+ *     WEIGHT
+ *     POSTURE
+ *     SIZE
+ *     TRANSFORM
+ * font face: Arial Negreta
+ *     logical:Arial Negreta
+ *     family:Arial
+ *     PSName:Arial-BoldMT
+ *     Style:
+ *         PLAIN
+ *     FAMILY
+ *     WEIGHT
+ *     POSTURE
+ *     SIZE
+ *     TRANSFORM
+ * font face: Arial Negreta cursiva
+ *     logical:Arial Negreta cursiva
+ *     family:Arial
+ *     PSName:Arial-BoldItalicMT
+ *     Style:
+ *         PLAIN
+ *     FAMILY
+ *     WEIGHT
+ *     POSTURE
+ *     SIZE
+ *     TRANSFORM
+ * </pre>
+ * There are other Arial forms, e.g. Arial Black and Arial Narrow, but
+ * they fall into different families, as indicated by the font name.
+ * java.awt.font.TextAttribute defines a number of TextAttribute
+ * constants, and querying a Font object via getAvailableAttributes()
+ * will provide an array of attributes available on the Font.
+ * <p>It seems there is a common set available on both Type1 and TrueType
+ * fonts in 1.4.2; viz FAMILY, WEIGHT, POSTURE, SIZE and TRANSFORM.
+ * Note that style is reported as PLAIN on all fonts, irrespective of
+ * the actual style according to the font name.
+ * <p>SIZE works as one might expect.  WEIGHT is supported directly only
+ * for the weights provided in the set of family fonts.  In the case of
+ * Arial, only REGULAR and BOLD.  The same is true of POSTURE: REGULAR
+ * and OBLIQUE.  There seems to be room here to experiment with
+ * virtual fonts.  A virtual Arial font might be constructed from the
+ * Arial, Arial Narrow, Arial Black and Arial Black MT fonts.
+ * Another area where virtual fonts might be handy is for small caps.
+ * 
+ * <p>In the case of the set of <i>logical</i> fonts defined for all Java
+ * implementations, the characteristics are reported like this:
+ * <pre>
+ * font face: serif.plain
+ *     logical:serif
+ *     family:serif
+ *     PSName:serif
+ *     PLAIN              
+ *     FAMILY
+ *     WEIGHT
+ *     POSTURE
+ *     SIZE
+ *     TRANSFORM
+ * font face: serif.bold
+ *     logical:serif.bold
+ *     family:serif
+ *     PSName:serif.bold
+ *     PLAIN              
+ *     FAMILY
+ *     WEIGHT
+ *     POSTURE
+ *     SIZE
+ *     TRANSFORM
+ * font face: serif.bolditalic
+ *     logical:serif.bolditalic
+ *     family:serif
+ *     PSName:serif.bolditalic
+ *     PLAIN              
+ *     FAMILY
+ *     WEIGHT
+ *     POSTURE
+ *     SIZE
+ *     TRANSFORM
+ * font face: serif.italic
+ *     logical:serif.italic
+ *     family:serif
+ *     PSName:serif.italic
+ *     PLAIN              
+ *     FAMILY
+ *     WEIGHT
+ *     POSTURE
+ *     SIZE
+ *     TRANSFORM
+ * </pre>
+ * Note that in this case, the logical name of the serif.plain font is
+ * <i>serif</i>.  This correspondence only seems to occur with the logical
+ * fonts.
+ * 
+ * <p>Three names are available for each <code>Font</code> object:
+ * <dl>
+ * <dt>Font Face Name</dt>
+ *   <dd>Aka Font Name. Corresponds to a phsical font in the underlying
+ *       system.</dd>
+ * <dt>Family Name</dt>
+ *   <dd>the name of the font family that determines the typographic design
+ *       across several faces.</dd>
+ * <dt>Logical Name</dt>
+ *   <dd>The name that was used to construct the font.  Each font has
+ *       such a name, irrespective of whether it is a <i>logical</i> or
+ *       <i>physical</i> font.  The logical name only differs from the
+ *       font face name in the case of <i>logical</i> fonts.  <i>E.g.</i>
+ *       the logical name of <code>serif.plain</code> is
+ *       <code>serif</code>, which is also the name of the pre-defined
+ *       logical font <i>serif</i>.</dd>
+ * <dt>Postscript Name</dt>
+ *   <dd>The Postscript name of the font.  Derivation and significance
+ *       unknown.<dd>
+ * </dl>
+ * 
+ * Initial font mapping is based on the names available to the font.
+ * 
+ * <h4>XSL-FO/CSS2 system fonts</h4>
+ * The CSS2 system fonts are:
+ * <ul>
+ * <li>caption</li>
+ * <li>icon</li>
+ * <li>menu</li>
+ * <li>message-box</li>
+ * <li>small-caption</li>
+ * <li>status-bar</li>
+ * </ul>
+ * The situation on linux systems is that there are no system fonts as
+ * such.  Individual GUI environments like Gnome, KDE, CDE and the like
+ * may define such fonts, but determining them will depend on the
+ * individual system's GUI environment.  The closest parallel in Java is
+ * the set of <i>logical</i> fonts defined in every Java implementation,
+ * <i>viz.</i>
+ * <ul>
+ * <li>Serif</li>
+ * <li>SansSerif</li>
+ * <li>Monospaced</li>
+ * <li>Dialog</li>
+ * <li>DialogInput</li>.
+ * </ul>
+ * The most obvious mapping from Java logical fonts to XSL-FO/CSS2
+ * system fonts is
+ * <dl>
+ * <dt>caption</dt><dd>SansSerif at size A</dd>
+ * <dt>icon</dt><dd>SansSerif at size B</dd>
+ * <dt>menu</dt><dd>SansSerif at size C</dd>
+ * <dt>message-box</dt><dd>Dialog</dd>
+ * <dt>small-caption</dt><dd>caption at size A/1.2</dd>
+ * <dt>status-bar</dt><dd>SansSerif at size D</dd>
+ * </dl>
+ * where sizes A, B, C and D are UserAgent prerogatives determined in
+ * consultation with the underlying JVM font system.  I.e., the fonts
+ * must support fractional metrics and dynamic sizing, which, in default
+ * Java implementations, they do, as far as I know.
+ * 
+ * <h4>XSL-FO/CSS2 Generic Font Families</h4>
+ * The generic families in the Recommendation are:
+ * <ul>
+ * <li>serif</li>
+ * <li>sans-serif</li>
+ * <li>cursive</li>
+ * <li>fantasy</li>
+ * <li>monospace</li>.
+ * </ul> 
+ * The mapping of the CSS2 generics <code>serif</code>,
+ * <code>sans-serif</code> and <code>monospace</code> is a straightforward
+ * name translation.  There is no such convenient correspondence between
+ * the Java font system and <code>cursive</code> and <code>fantasy</code>
+ * fonts.  This mapping must be determined by the UserAgent by
+ * interrogating the JVM.
+ * 
+ * @author pbw
+ * @version $Revision$ $Name$
+ */
+public class Fonts {
+
+//    public static final int
+//                   NO_ATTR = 0
+//                   ,BACKGROUND = 1
+//                   ,BIDI_EMBEDDING = 2
+//                   ,CHAR_REPLACEMENT = 4
+//                   ,FAMILY = 8
+//                   ,FONT = 16
+//                   ,FOREGROUND = 32
+//                   ,INPUT_METHOD_HIGHLIGHT = 64
+//                   ,INPUT_METHOD_UNDERLINE = 128
+//                   ,JUSTIFICATION = 256
+//                   ,NUMERIC_SHAPING = 512
+//                   ,POSTURE = 1024
+//                   ,RUN_DIRECTION = 2048
+//                   ,SIZE = 4096
+//                   ,STRIKETHROUGH = 8192
+//                   ,SUPERSCRIPT = 16384
+//                   ,SWAP_COLORS = 32768
+//                   ,TRANSFORM = 65536
+//                   ,UNDERLINE = 131072
+//                   ,WEIGHT = 262144
+//                   ,WIDTH = 524288
+//                   ;
+
+    //private HashMap fontAttributes = null;
+
+    private HashMap fontFamilies = null;
+    private HashSet serif = new HashSet();
+    private HashSet sansserif = new HashSet();
+    private HashSet monospace = new HashSet();
+    private HashSet cursive = new HashSet();
+    private HashSet fantasy = new HashSet();
+    private HashSet symbols = new HashSet();
+
+    /**
+     */
+    private void setupFonts() {
+        // Set up the graphics environment
+        BufferedImage fontImage =
+            new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
+        Graphics2D g2D = fontImage.createGraphics();
+        FontRenderContext frcontext = g2D.getFontRenderContext();
+        // Set up the fonts environment
+        // TODO Check whether this is needed to provide better mapping between
+        // requested fonts and those available on the system
+        GraphicsEnvironment gEnv =
+            GraphicsEnvironment.getLocalGraphicsEnvironment();
+        Font[] fonts = gEnv.getAllFonts();
+        String[] families = gEnv.getAvailableFontFamilyNames();
+        Locale locale = Locale.getDefault();
+        fontFamilies =
+            new HashMap((int)(families.length + fonts.length * 4.5));
+        //fontAttributes = new HashMap((int)(fonts.length / 0.7));
+        // Enter all of the family names, keyed on themselves, and keyed on 
+        // the lower-case version of the family name, if different.
+        // N.B. If there are two font family names which differ only in that
+        // one is the locale-specific lower-case version of the first, they
+        // will be recorded in availabelFonsts as two separate font families.
+        for (int i = 0; i < families.length; i++) {
+            if (fontFamilies.get(families[i]) == null) {
+                fontFamilies.put(families[i], families[i]);
+            }
+        }
+        for (int i = 0; i < families.length; i++) {
+            String lcase = families[i].toLowerCase(locale);
+            if (fontFamilies.get(lcase) == null) {
+                fontFamilies.put(lcase, families[i]);
+            }
+        }
+        String[] fontNames = new String[fonts.length];
+        String[] psNames = new String[fonts.length];
+        for (int i = 0; i < fonts.length; i++) {
+            Font f = fonts[i];
+            String family = f.getFamily();
+            String lcfamily = family.toLowerCase(locale);
+            String font = f.getFontName();
+            String lcfont = font.toLowerCase(locale);
+            String psname = f.getPSName();
+            String lcpsname = psname.toLowerCase(locale);
+            String logical = f.getName();
+            String lclogical = logical.toLowerCase(locale);
+            // Map each of the font names to the family name
+            if (fontFamilies.get(font) == null) {
+                fontFamilies.put(font, family);
+            }
+            if (fontFamilies.get(psname) == null) {
+                fontFamilies.put(psname, family);
+            }
+            if (fontFamilies.get(logical) == null) {
+                fontFamilies.put(logical, family);
+            }
+            // Collect styles for possible intelligent font substitution
+            // TODO if this is not used, delete
+            checkMonospace(monospace, family, lcfamily);
+            checkSerif(serif, family, lcfamily);
+            checkSansSerif(sansserif, family, lcfamily);
+            checkCursive(cursive, family, lcfamily);
+            checkFantasy(fantasy, family, lcfamily);
+            checkSymbols(symbols, family, lcfamily);
+            // Add mappings for some of the CSS2 generic font families
+            setupCSSGenericMapping(fontFamilies);
+            // Add mappings for the CSS2 system fonts
+            setupCSSSystemFontMapping(fontFamilies);
+        }
+    }
+
+    public void setupCSSGenericMapping(Map fontFamilies) {
+        // Add mappings for some of the CSS2 generic font families
+        // TODO set up mappings for "cursive" and "fantasy"
+        if (fontFamilies.get("serif") == null) {
+            fontFamilies.put("serif", "Serif");
+        }
+        if (fontFamilies.get("sans-serif") == null) {
+            fontFamilies.put("sans-serif", "SansSerif");
+        }
+        if (fontFamilies.get("monospace") == null) {
+            fontFamilies.put("monospace", "Monospaced");
+        }
+    }
+
+    public void setupCSSSystemFontMapping(Map fontFamilies) {
+        // TODO
+    }
+
+    private void checkMonospace(Set monospace, String family, String lcfamily) {
+        int mono = lcfamily.lastIndexOf("mono");
+        if (mono >= 0) {
+            if (lcfamily.indexOf("monotype") != mono) {
+                // Didn't find "Monotype"
+                monospace.add(family);
+            }
+        }
+        if (lcfamily.indexOf("courier") >= 0) {
+            monospace.add(family);
+        }
+        if (lcfamily.indexOf("console") >= 0) {
+            monospace.add(family);
+        }
+        if (lcfamily.indexOf("typewriter") >= 0) {
+            monospace.add(family);
+        }
+    }
+
+    private void checkSerif(Set serif, String family, String lcfamily) {
+        int ser = lcfamily.indexOf("serif");
+        if (ser >= 0) {
+            if (lcfamily.indexOf("sans") < 0) {
+                // Didn't find "sans serif"
+                serif.add(family);
+            }
+        }
+        if (lcfamily.indexOf("roman") >= 0) {
+            serif.add(family);
+        }
+        if (lcfamily.indexOf("times") >= 0) {
+            serif.add(family);
+        }
+        if (lcfamily.indexOf("bookman") >= 0) {
+            serif.add(family);
+        }
+        if (lcfamily.indexOf("utopia") >= 0) {
+            serif.add(family);
+        }
+        if (lcfamily.indexOf("palatino") >= 0) {
+            serif.add(family);
+        }
+        if (lcfamily.indexOf("palladio") >= 0) {
+            serif.add(family);
+        }
+        if (lcfamily.indexOf("bright") >= 0) {
+            serif.add(family);
+        }
+        if (lcfamily.indexOf("georgia") >= 0) {
+            serif.add(family);
+        }
+        if (lcfamily.indexOf("schoolbook") >= 0) {
+            serif.add(family);
+        }
+        if (lcfamily.indexOf("charter") >= 0) {
+            serif.add(family);
+        }
+        if (lcfamily.indexOf("antiqua") >= 0) {
+            serif.add(family);
+        }
+        if (lcfamily.indexOf("footlight") >= 0) {
+            serif.add(family);
+        }
+    }
+
+    private void checkSansSerif(Set sansserif, String family, String lcfamily) {
+        if (lcfamily.indexOf("sans") >= 0) {
+            if (lcfamily.indexOf("comic") < 0) {
+                sansserif.add(family);
+            }
+        }
+        if (lcfamily.indexOf("helvetica") >= 0) {
+            sansserif.add(family);
+        }
+        if (lcfamily.indexOf("arial") >= 0) {
+            sansserif.add(family);
+        }
+        if (lcfamily.indexOf("avantgarde") >= 0) {
+            sansserif.add(family);
+        }
+        if (lcfamily.indexOf("gothic") >= 0) {
+            sansserif.add(family);
+        }
+        if (lcfamily.indexOf("tahoma") >= 0) {
+            sansserif.add(family);
+        }
+        if (lcfamily.indexOf("thonburi") >= 0) {
+            sansserif.add(family);
+        }
+        if (lcfamily.indexOf("trebuchet") >= 0) {
+            sansserif.add(family);
+        }
+        if (lcfamily.indexOf("verdana") >= 0) {
+            sansserif.add(family);
+        }
+    }
+
+    private void checkCursive(Set cursive, String family, String lcfamily) {
+        if (lcfamily.indexOf("chancery") >= 0) {
+            cursive.add(family);
+        }
+        if (lcfamily.indexOf("brush") >= 0) {
+            cursive.add(family);
+        }
+        if (lcfamily.indexOf("script") >= 0) {
+            cursive.add(family);
+        }
+        if (lcfamily.indexOf("naskh") >= 0) {
+            cursive.add(family);
+        }
+        if (lcfamily.indexOf("shuwiefat") >= 0) {
+            cursive.add(family);
+        }
+    }
+
+    private void checkFantasy(Set fantasy, String family, String lcfamily) {
+        if (lcfamily.indexOf("algerian") >= 0) {
+            fantasy.add(family);
+        }
+        if (lcfamily.indexOf("americantext") >= 0) {
+            fantasy.add(family);
+        }
+        if (lcfamily.indexOf("braggadocio") >= 0) {
+            fantasy.add(family);
+        }
+        if (lcfamily.indexOf("colonna") >= 0) {
+            fantasy.add(family);
+        }
+        if (lcfamily.indexOf("comic") >= 0) {
+            fantasy.add(family);
+        }
+        if (lcfamily.indexOf("desdemona") >= 0) {
+            fantasy.add(family);
+        }
+        if (lcfamily.indexOf("kino") >= 0) {
+            fantasy.add(family);
+        }
+        if (lcfamily.indexOf("playbill") >= 0) {
+            fantasy.add(family);
+        }
+    }
+
+    private void checkSymbols(Set symbols, String family, String lcfamily) {
+        if (lcfamily.indexOf("symbol") >= 0) {
+            symbols.add(family);
+        }
+        if (lcfamily.indexOf("dingbats") >= 0) {
+            symbols.add(family);
+        }
+        if (lcfamily.indexOf("webdings") >= 0) {
+            symbols.add(family);
+        }
+        if (lcfamily.indexOf("wingdings") >= 0) {
+            symbols.add(family);
+        }
+        if (lcfamily.indexOf("sorts") >= 0) {
+            symbols.add(family);
+        }
+    }
+
+}