Changeset 8015 in josm for trunk


Ignore:
Timestamp:
2015-02-06T19:17:14+01:00 (5 years ago)
Author:
bastiK
Message:

fixed #10989 - I18n display support for major Asian scripts (Tamil, Bengali, ...)

adds more scripts for Windows, including Tibet, Khmer, Lao, Mongolian, Myanmar
Only works if corresponding font is installed, so basically starting
with a certain Windows version. See source code for details.

Advanced pref font.extended-unicode.added-items renamed to
font.extended-unicode.extra-items, type is now list-of-maps.

Location:
trunk/src/org/openstreetmap/josm
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/Main.java

    r7894 r8015  
    387387
    388388    /**
     389     * Prints a formatted trace message if logging is on. Calls {@link MessageFormat#format}
     390     * function to format text.
     391     * @param msg The formatted message to print.
     392     * @param objects The objects to insert into format string.
     393     */
     394    public static void trace(String msg, Object... objects) {
     395        trace(MessageFormat.format(msg, objects));
     396    }
     397
     398    /**
    389399     * Prints an error message for the given Throwable.
    390400     * @param t The throwable object causing the error
  • trunk/src/org/openstreetmap/josm/gui/MainApplication.java

    r7896 r8015  
    367367        processOffline(args);
    368368
     369        Main.platform.afterPrefStartupHook();
     370
    369371        FontsManager.initialize();
    370372
  • trunk/src/org/openstreetmap/josm/tools/PlatformHook.java

    r7834 r8015  
    2424      */
    2525    public void preStartupHook();
     26   
     27    /**
     28      * The afterPrefStartupHook will be called early, but after
     29      * the preferences have been loaded and basic processing of
     30      * command line arguments is finished.
     31      * It is guaranteed to be called before the GUI setup has started.
     32      */
     33    public void afterPrefStartupHook();
    2634
    2735    /**
  • trunk/src/org/openstreetmap/josm/tools/PlatformHookUnixoid.java

    r7834 r8015  
    99import java.awt.event.KeyEvent;
    1010import java.io.BufferedReader;
     11import java.io.BufferedWriter;
    1112import java.io.File;
     13import java.io.FileInputStream;
    1214import java.io.IOException;
    1315import java.io.InputStreamReader;
     16import java.io.OutputStream;
     17import java.io.OutputStreamWriter;
     18import java.io.Writer;
    1419import java.net.URI;
    1520import java.net.URISyntaxException;
    1621import java.nio.charset.StandardCharsets;
     22import java.nio.file.FileSystems;
    1723import java.nio.file.Files;
    1824import java.nio.file.Path;
     
    2228import java.security.NoSuchAlgorithmException;
    2329import java.security.cert.CertificateException;
     30import java.util.ArrayList;
    2431import java.util.Arrays;
     32import java.util.Collection;
     33import java.util.List;
     34import java.util.Properties;
    2535
    2636import javax.swing.JOptionPane;
     
    2939import org.openstreetmap.josm.gui.ExtendedDialog;
    3040import org.openstreetmap.josm.gui.util.GuiHelper;
     41import org.openstreetmap.josm.data.Preferences.pref;
     42import org.openstreetmap.josm.data.Preferences.writeExplicitly;
    3143
    3244/**
     
    3850public class PlatformHookUnixoid implements PlatformHook {
    3951
     52    /**
     53     * Simple data class to hold information about a font.
     54     *
     55     * Used for fontconfig.properties files.
     56     */
     57    public static class FontEntry {
     58        /**
     59         * The character subset. Basically a free identifier, but should
     60         * be unique.
     61         */
     62        @pref
     63        public String charset;
     64        /**
     65         * Platform font name.
     66         */
     67        @pref @writeExplicitly
     68        public String name = "";
     69        /**
     70         * File name.
     71         */
     72        @pref @writeExplicitly
     73        public String file = "";
     74
     75        public FontEntry() {
     76        }
     77
     78        public FontEntry(String charset, String name, String file) {
     79            this.charset = charset;
     80            this.name = name;
     81            this.file = file;
     82        }
     83    }
     84
    4085    private String osDescription;
    4186
    4287    @Override
    4388    public void preStartupHook() {
     89    }
     90
     91    @Override
     92    public void afterPrefStartupHook() {
    4493    }
    4594
     
    386435        return Main.pref.getPreferencesDirectory();
    387436    }
     437
     438    /**
     439     * Add more fallback fonts to the Java runtime, in order to get
     440     * support for more scripts.
     441     *
     442     * The font configuration in Java doesn't include some Indic scripts,
     443     * even though MS Windows ships with fonts that cover these unicode
     444     * ranges.
     445     *
     446     * To fix this, the fontconfig.properties template is copied to the JOSM
     447     * cache folder. Then, the additional entries are added to the font
     448     * configuration. Finally the system property "sun.awt.fontconfig" is set
     449     * to the customized fontconfig.properties file.
     450     *
     451     * This is a crude hack, but better than no font display at all for these
     452     * languages.
     453     * There is no guarantee, that the template file
     454     * ($JAVA_HOME/lib/fontconfig.properties.src) matches the default
     455     * configuration (which is in a binary format).
     456     * Furthermore, the system property "sun.awt.fontconfig" is undocumented and
     457     * may no longer work in future versions of Java.
     458     *
     459     * @param templateFileName file name of the fontconfig.properties template file
     460     */
     461    protected void extendFontconfig(String templateFileName) {
     462        String customFontconfigFile = Main.pref.get("fontconfig.properties", null);
     463        if (customFontconfigFile != null) {
     464            Utils.updateSystemProperty("sun.awt.fontconfig", customFontconfigFile);
     465            return;
     466        }
     467        if (!Main.pref.getBoolean("font.extended-unicode", true))
     468            return;
     469
     470        String javaLibPath = System.getProperty("java.home") + File.separator + "lib";
     471        Path templateFile = FileSystems.getDefault().getPath(javaLibPath, templateFileName);
     472        if (!Files.isReadable(templateFile)) {
     473            Main.warn("extended font config - unable to find font config template file "+templateFile.toString());
     474            return;
     475        }
     476        try {
     477            Properties props = new Properties();
     478            props.load(new FileInputStream(templateFile.toFile()));
     479            byte[] content = Files.readAllBytes(templateFile);
     480            File cachePath = Main.pref.getCacheDirectory();
     481            Path fontconfigFile = cachePath.toPath().resolve("fontconfig.properties");
     482            OutputStream os = Files.newOutputStream(fontconfigFile);
     483            os.write(content);
     484            try (Writer w = new BufferedWriter(new OutputStreamWriter(os))) {
     485                Collection<FontEntry> extrasPref = Main.pref.getListOfStructs(
     486                        "font.extended-unicode.extra-items", getAdditionalFonts(), FontEntry.class);
     487                Collection<FontEntry> extras = new ArrayList<>();
     488                w.append("\n\n# Added by JOSM to extend unicode coverage of Java font support:\n\n");
     489                List<String> allCharSubsets = new ArrayList<>();
     490                for (FontEntry entry: extrasPref) {
     491                    Collection<String> fontsAvail = getInstalledFonts();
     492                    if (fontsAvail != null && fontsAvail.contains(entry.file.toUpperCase())) {
     493                        if (!allCharSubsets.contains(entry.charset)) {
     494                            allCharSubsets.add(entry.charset);
     495                            extras.add(entry);
     496                        } else {
     497                            Main.trace("extended font config - already registered font for charset ''{0}'' - skipping ''{1}''",
     498                                    entry.charset, entry.name);
     499                        }
     500                    } else {
     501                        Main.trace("extended font config - Font ''{0}'' not found on system - skipping", entry.name);
     502                    }
     503                }
     504                for (FontEntry entry: extras) {
     505                    allCharSubsets.add(entry.charset);
     506                    if ("".equals(entry.name)) {
     507                        continue;
     508                    }
     509                    String key = "allfonts." + entry.charset;
     510                    String value = entry.name;
     511                    String prevValue = props.getProperty(key);
     512                    if (prevValue != null && !prevValue.equals(value)) {
     513                        Main.warn("extended font config - overriding ''{0}={1}'' with ''{2}''", key, prevValue, value);
     514                    }
     515                    w.append(key + "=" + value + "\n");
     516                }
     517                w.append("\n");
     518                for (FontEntry entry: extras) {
     519                    if ("".equals(entry.name) || "".equals(entry.file)) {
     520                        continue;
     521                    }
     522                    String key = "filename." + entry.name.replace(" ", "_");
     523                    String value = entry.file;
     524                    String prevValue = props.getProperty(key);
     525                    if (prevValue != null && !prevValue.equals(value)) {
     526                        Main.warn("extended font config - overriding ''{0}={1}'' with ''{2}''", key, prevValue, value);
     527                    }
     528                    w.append(key + "=" + value + "\n");
     529                }
     530                w.append("\n");
     531                String fallback = props.getProperty("sequence.fallback");
     532                if (fallback != null) {
     533                    w.append("sequence.fallback=" + fallback + "," + Utils.join(",", allCharSubsets) + "\n");
     534                } else {
     535                    w.append("sequence.fallback=" + Utils.join(",", allCharSubsets) + "\n");
     536                }
     537            }
     538            Utils.updateSystemProperty("sun.awt.fontconfig", fontconfigFile.toString());
     539        } catch (IOException ex) {
     540            Main.error(ex);
     541        }
     542    }
     543
     544    /**
     545     * Get a list of fonts that are installed on the system.
     546     *
     547     * Must be done without triggering the Java Font initialization.
     548     * (See {@link #extendFontconfig(java.lang.String)}, have to set system
     549     * property first, which is then read by sun.awt.FontConfiguration upon
     550     * initialization.)
     551     *
     552     * @return list of file names
     553     */
     554    public Collection<String> getInstalledFonts() {
     555        throw new UnsupportedOperationException();
     556    }
     557
     558    /**
     559     * Get default list of additional fonts to add to the configuration.
     560     *
     561     * Java will choose thee first font in the list that can render a certain
     562     * character.
     563     *
     564     * @return list of FontEntry objects
     565     */
     566    public Collection<FontEntry> getAdditionalFonts() {
     567        throw new UnsupportedOperationException();
     568    }
    388569}
  • trunk/src/org/openstreetmap/josm/tools/PlatformHookWindows.java

    r8014 r8015  
    2929
    3030import java.awt.GraphicsEnvironment;
    31 import java.io.BufferedWriter;
    3231import java.io.File;
    33 import java.io.FileInputStream;
    3432import java.io.IOException;
    35 import java.io.OutputStream;
    36 import java.io.OutputStreamWriter;
    37 import java.io.Writer;
     33import java.nio.file.DirectoryStream;
    3834import java.nio.file.FileSystems;
    3935import java.nio.file.Files;
     
    5147import java.security.spec.X509EncodedKeySpec;
    5248import java.util.ArrayList;
    53 import java.util.Arrays;
    5449import java.util.Collection;
    5550import java.util.Enumeration;
    5651import java.util.List;
    57 import java.util.Properties;
    5852
    5953import javax.swing.JOptionPane;
     54
    6055import org.openstreetmap.josm.Main;
    6156
     
    9691
    9792    @Override
    98     public void preStartupHook() {
    99         extendFontconfig();
    100     }
    101    
    102     /**
    103      * Add more fallback fonts to the Java runtime, in order to get wider
    104      * unicode support.
    105      *
    106      * The font configuration in Java doesn't include some Indic scripts,
    107      * even though MS Windows ships with fonts that cover these unicode
    108      * ranges.
    109      *
    110      * To fix this, the fontconfig.properties template is copied to the JOSM
    111      * cache folder. Then, the additional entries are added to the font
    112      * configuration. Finally the system property "sun.awt.fontconfig" is set
    113      * to the customized fontconfig.properties file.
    114      *
    115      * This is a crude hack, but better than no font display at all for these
    116      * languages.
    117      * There is no guarantee, that the template file
    118      * ($JAVA_HOME/lib/fontconfig.properties.src) matches the default
    119      * configuration (which is in a binary format).
    120      * Furthermore, the system property "sun.awt.fontconfig" is undocumented and
    121      * may no longer work in future versions of Java.
    122      */
    123     protected void extendFontconfig() {
    124         String customFontconfigFile = Main.pref.get("fontconfig.properties", null);
    125         if (customFontconfigFile != null) {
    126             Utils.updateSystemProperty("sun.awt.fontconfig", customFontconfigFile);
    127             return;
    128         }
    129         if (!Main.pref.getBoolean("font.extended-unicode", true))
    130             return;
    131         String javaLibPath = System.getProperty("java.home") + File.separator + "lib";
    132         Path templateFile = FileSystems.getDefault().getPath(javaLibPath, "fontconfig.properties.src");
    133         if (!Files.isReadable(templateFile)) {
    134             Main.warn("extended unicode - unable to find font config template file "+templateFile.toString());
    135             return;
    136         }
    137         try {
    138             Properties props = new Properties();
    139             props.load(new FileInputStream(templateFile.toFile()));
    140             byte[] content = Files.readAllBytes(templateFile);
    141             File cachePath = Main.pref.getCacheDirectory();
    142             Path fontconfigFile = cachePath.toPath().resolve("fontconfig.properties");
    143             OutputStream os = Files.newOutputStream(fontconfigFile);
    144             os.write(content);
    145             try (Writer w = new BufferedWriter(new OutputStreamWriter(os))) {
    146                 Collection<Collection<String>> def = new ArrayList<>();
    147                 def.add(Arrays.asList("devanagari", "", "")); // just include in fallback list
    148                                                               // no font definition needed
    149                 // all of the following fonts are available in Win XP and later
    150                 def.add(Arrays.asList("gujarati", "Shruti", "SHRUTI.TTF"));
    151                 def.add(Arrays.asList("kannada", "Tunga", "TUNGA.TTF"));
    152                 def.add(Arrays.asList("gurmuhi", "Raavi", "RAAVI.TTF"));
    153                 def.add(Arrays.asList("tamil", "Latha", "LATHA.TTF"));
    154                 def.add(Arrays.asList("telugu", "Gautami", "GAUTAMI.TTF"));
    155                 def.add(Arrays.asList("bengali", "Vrinda", "VRINDA.TTF"));
    156                 def.add(Arrays.asList("syriac", "Estrangelo Edessa", "ESTRE.TTF")); // ISO 639: arc
    157                 def.add(Arrays.asList("thaana", "MV Boli", "MVBOLI.TTF"));          // ISO 639: dv
    158                 def.add(Arrays.asList("malayalam", "Kartika", "KARTIKA.TTF"));      // ISO 639: ml; since XP SP2
    159                 Collection<Collection<String>> additions =
    160                         Main.pref.getArray("font.extended-unicode.added-items", def);
    161                 w.append("\n\n# Added by JOSM to extend unicode coverage of Java font support:\n\n");
    162                 List<String> allCharSubsets = new ArrayList<>();
    163                 for (Collection<String> entry: additions) {
    164                     List<String> lentry = new ArrayList<>(entry);
    165                     String charSubset = lentry.get(0);
    166                     allCharSubsets.add(charSubset);
    167                     String platformFontName = lentry.get(1);
    168                     if ("".equals(platformFontName)) {
    169                         continue;
    170                     }
    171                     String key = "allfonts." + charSubset;
    172                     String value = platformFontName;
    173                     String prevValue = props.getProperty(key);
    174                     if (prevValue != null && !prevValue.equals(value)) {
    175                         Main.warn("extended unicode - overriding " + key + "=" + prevValue + " with " + value);
    176                     }
    177                     w.append(key + "=" + value + "\n");
    178                 }
    179                 w.append("\n");
    180                 for (Collection<String> entry: additions) {
    181                     List<String> lentry = new ArrayList<>(entry);
    182                     String platformFontName = lentry.get(1);
    183                     String fontFile = lentry.get(2);
    184                     if ("".equals(platformFontName) || "".equals(fontFile)) {
    185                         continue;
    186                     }
    187                     String key = "filename." + platformFontName;
    188                     String value = fontFile;
    189                     String prevValue = props.getProperty(key);
    190                     if (prevValue != null && !prevValue.equals(value)) {
    191                         Main.warn("extended unicode - overriding " + key + "=" + prevValue + " with " + value);
    192                     }
    193                     w.append(key + "=" + value + "\n");
    194                 }
    195                 w.append("\n");
    196                 String fallback = props.getProperty("sequence.fallback");
    197                 if (fallback != null) {
    198                     w.append("sequence.fallback=" + fallback + "," + Utils.join(",", allCharSubsets) + "\n");
    199                 } else {
    200                     w.append("sequence.fallback=" + Utils.join(",", allCharSubsets) + "\n");
    201                 }
    202             }
    203             Utils.updateSystemProperty("sun.awt.fontconfig", fontconfigFile.toString());
    204         } catch (IOException ex) {
    205             Main.error(ex);
    206         }
    207     }
    208    
     93    public void afterPrefStartupHook() {
     94        extendFontconfig("fontconfig.properties.src");
     95    }
     96
    20997    @Override
    21098    public void openUrl(String url) throws IOException {
     
    420308        return new File(System.getenv("APPDATA"), "JOSM");
    421309    }
     310
     311    @Override
     312    public Collection<String> getInstalledFonts() {
     313        // Cannot use GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()
     314        // because we have to set the system property before Java initializes its fonts.
     315        // Use more low-level method to find the installed fonts.
     316        List<String> fontsAvail = new ArrayList<>();
     317        Path fontPath = FileSystems.getDefault().getPath(System.getenv("SYSTEMROOT"), "Fonts");
     318        try (DirectoryStream<Path> ds = Files.newDirectoryStream(fontPath)) {
     319            for (Path p : ds) {
     320                fontsAvail.add(p.getFileName().toString().toUpperCase());
     321            }
     322        } catch (IOException ex) {
     323            Main.warn("extended font config - failed to load available Fonts");
     324            fontsAvail = null;
     325        }
     326        fontsAvail.add(""); // for devanagari
     327        return fontsAvail;
     328    }
     329
     330    @Override
     331    public Collection<FontEntry> getAdditionalFonts() {
     332        Collection<FontEntry> def = new ArrayList<>();
     333        def.add(new FontEntry("devanagari", "", "")); // just include in fallback list
     334                                                      // font already defined in template
     335        // Win 8 (and later)
     336        def.add(new FontEntry("myanmar", "Myanmar Text", "MMRTEXT.TTF"));           // ISO 639: my
     337
     338        // Win 7 and later
     339        def.add(new FontEntry("nko_tifinagh_vai_osmanya", "Ebrima", "EBRIMA.TTF")); // ISO 639: ber
     340        def.add(new FontEntry("khmer1", "Khmer UI", "KHMERUI.TTF"));                // ISO 639: km
     341        def.add(new FontEntry("lao1", "Lao UI", "LAOUI.TTF"));                      // ISO 639: lo
     342
     343        // Win Vista and later:
     344        def.add(new FontEntry("ethiopic", "Nyala", "NYALA.TTF"));                   // ISO 639: am,gez,ti
     345        def.add(new FontEntry("tibetan", "Microsoft Himalaya", "HIMALAYA.TTF"));    // ISO 639: bo,dz
     346        def.add(new FontEntry("cherokee", "Plantagenet Cherokee", "PLANTC.TTF"));   // ISO 639: chr
     347        def.add(new FontEntry("unified_canadian", "Euphemia", "EUPHEMIA.TTF"));     // ISO 639: cr,in
     348        def.add(new FontEntry("khmer2", "DaunPenh", "DAUNPENH.TTF"));               // ISO 639: km
     349        def.add(new FontEntry("khmer3", "MoolBoran", "MOOLBOR.TTF"));               // ISO 639: km
     350        def.add(new FontEntry("lao_thai", "DokChampa", "DOKCHAMP.TTF"));            // ISO 639: lo
     351        def.add(new FontEntry("mongolian", "Mongolian Baiti", "MONBAITI.TTF"));     // ISO 639: mn
     352        def.add(new FontEntry("oriya", "Kalinga", "KALINGA.TTF"));                  // ISO 639: or
     353        def.add(new FontEntry("sinhala", "Iskoola Pota", "ISKPOTA.TTF"));           // ISO 639: si
     354
     355        // Win XP and later
     356        def.add(new FontEntry("gujarati", "Shruti", "SHRUTI.TTF"));
     357        def.add(new FontEntry("kannada", "Tunga", "TUNGA.TTF"));
     358        def.add(new FontEntry("gurmuhi", "Raavi", "RAAVI.TTF"));
     359        def.add(new FontEntry("tamil", "Latha", "LATHA.TTF"));
     360        def.add(new FontEntry("telugu", "Gautami", "GAUTAMI.TTF"));
     361        def.add(new FontEntry("bengali", "Vrinda", "VRINDA.TTF"));
     362        def.add(new FontEntry("syriac", "Estrangelo Edessa", "ESTRE.TTF"));         // ISO 639: arc
     363        def.add(new FontEntry("thaana", "MV Boli", "MVBOLI.TTF"));                  // ISO 639: dv
     364        def.add(new FontEntry("malayalam", "Kartika", "KARTIKA.TTF"));              // ISO 639: ml; since XP SP2
     365
     366        // Comes with MS Office & Outlook 2000. Good unicode coverage, so add if available.
     367        def.add(new FontEntry("arialuni", "Arial Unicode MS", "ARIALUNI.TTF"));
     368
     369        return def;
     370    }
     371
    422372}
Note: See TracChangeset for help on using the changeset viewer.