Lösungen für verstümmelte chinesische Zeichen in Java-Linux-Dateien: 1. Laden Sie den Sun-Quellcode von jdk1.8 herunter. 2. Ändern Sie die Schriftartenerstellung von physischen Schriftarten in logische Schriftarten.
Die Betriebsumgebung dieses Artikels: Linux5.9.8-System, JDK1.8, Dell G3-Computer.
Wie löst man das Problem verstümmelter chinesischer Zeichen in Java-Linux-Dateien?
Lösung für verstümmelte chinesische Java-Zeichen in einer Linux-Umgebung
Ich glaube, dass viele Freunde kürzlich auf das Problem verstümmelter Java-Zeichen gestoßen sind Ich habe auch das Problem „Chinesisch und Sonderzeichen sind verstümmelt“ beim „Prozess der Verwendung von Text zum Generieren von Bildern“ gelöst. Ich habe viel Zeit damit verbracht, verschiedene Quellcodes unter sun.font und sun.awt zu debuggen, und habe schließlich den Mechanismus verstanden und das aktuelle Problem gelöst Problem; jetzt werde ich Ihnen den Problemlösungsprozess erläutern und aufzeichnen, damit Sie in Zukunft nicht wieder darauf stoßen.
Der folgende Code ist der Code, den ich ausführen möchte (extrem vereinfacht, aber die Bedeutung bleibt gleich):
public static void main(String[] args) throws IOException { File file = new File("test.png"); Font font = new Font("宋体", Font.PLAIN, 10); BufferedImage bi = new BufferedImage(400, 200, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = (Graphics2D) bi.getGraphics(); g2.setBackground(Color.WHITE); g2.clearRect(0, 0, 400, 200); g2.setFont(font); g2.setColor(Color.BLACK); g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2.drawString("为什么没有(ꐚꌒꑿꆺ)(ꐚꌒꑿꆺ)这名字特殊不?@¥¥¥ 为什么没有(ꐚꌒꑿꆺ)(ꐚꌒꑿꆺ)这名字特 ", 0, 10); g2.dispose(); ImageIO.write(bi, PNG, file); }
Das Ziel ist natürlich, beim Öffnen von test.png die folgende Szene zu sehen:
Nachdem es beim lokalen Debuggen keine Probleme gab, habe ich es auf den Testcomputer (Linux) gestellt und ausgeführt. Die Ausführungsergebnisse waren erstaunlich:
Folgen Sie dem Konsequenter Stil des Programmierers: Da es ein Problem gibt, dann Debuggen!
Der Trick besteht darin, dass das aktuelle Quellcode-Paket nicht mehr den Code des Sun-Pakets enthält!
Glücklicherweise hat Java offiziell bestätigt, dass der OpenJDK-Code grundsätzlich mit dem JVM-Quellcode übereinstimmt. Sie können ihn direkt von OpenJDK8u herunterladen: jdk8u
Über die Verwendung des Quellcodes zum Debuggen werde ich nicht schreiben. . Das ist nicht grundlegend, also lesen Sie diesen Artikel nicht
Laden Sie den Quellcode direkt herunter, verwenden Sie Remote-Haltepunkte und führen Sie ihn auf dem Server aus die lokalen und Testserver:
Es stellt sich heraus, dass die JVM beim Erstellen einer Schriftart die FontManagerFactory verwendet, um diese abzurufen, und verschiedene Systeme unterschiedliche FontManager verwenden! Mac verwendet CFontManager, während Linux X11FontManager verwendet!
Was sind also die Unterschiede zwischen diesen beiden FontManagern?
CFontManager erstellt CFont als Font2D. Dies ist eine von JVM speziell für Mac erstellte Klasse. Wenn Sie sich die Kommentare der Klasse und Methode ansehen, können Sie erkennen, dass CFont in der Mac-Umgebung manchmal physische Schriftarten umschließt in nativem Code erstellt:
Font2D, erstellt von X11FontManager, ist eine Sammlung, die logische Schriftarten und physische Schriftarten enthält. X11FontManager erbt FcFontManager, und FcFontManager erbt SunFontManager. Schauen wir uns die Methode „loadFonts()“ von SunFontManager an. Die Methode „loadFonts()“ von SunFontManager implementiert die Methode „preferLocaleFonts()“. :
Das Code-Debuggen hier hat grundsätzlich bestätigt, dass es sich um ein Problem beim Laden von Schriftarten in verschiedenen Umgebungen handelt. Daher wurden die logischen Schriftarten beim Debuggen der Linux-Umgebung gefunden. Und was sind physische Schriftarten?
Physische Schriftarten sind tatsächliche Schriftartenbibliotheken, die Glyphendaten und Tabellen enthalten, die mithilfe von Schriftartentechnologien wie TrueType oder PostScript Type 1 Zeichensequenzen Glyphensequenzen zuordnen. Alle Implementierungen der Java-Plattform unterstützen TrueType-Schriftarten; die Unterstützung anderer Schriftartentechnologien ist von der Implementierung abhängig. Physische Schriftarten können Schriftartnamen wie Helvetica, Palatino, HonMincho oder eine beliebige Anzahl anderer Schriftartnamen verwenden. Normalerweise unterstützt jede physische Schriftart nur eine begrenzte Anzahl von Schriftsystemen, beispielsweise nur lateinische Zeichen oder nur Japanisch und einfaches Latein. Der Satz verfügbarer physischer Schriftarten variiert je nach Konfiguration. Anwendungen, die bestimmte Schriftarten benötigen, können die Methode „createFont“ verwenden, um diese Schriftarten zu bündeln und zu instanziieren.
Logische Schriftarten sind fünf von der Java-Plattform definierte Schriftfamilien, die von allen Java-Laufzeitumgebungen unterstützt werden müssen: Serif, SansSerif, Monospaced, Dialog und DialogInput. Bei diesen logischen Schriftarten handelt es sich nicht um tatsächliche Schriftartenbibliotheken. Darüber hinaus ist es die Java-Laufzeitumgebung, die logische Schriftartnamen physischen Schriftarten zuordnet. Zuordnungen sind von der Implementierung und im Allgemeinen vom Gebietsschema abhängig, sodass das Erscheinungsbild und die Spezifikationen, die sie bereitstellen, variieren. Typischerweise wird jeder logische Schriftartname mehreren physischen Schriftarten zugeordnet, um den großen Zeichenbereich abzudecken.
debug的源码很多,但是此次问题的关键点就在这里了,其它debug内容就不贴了。
既然已经确认了本地(mac环境)是native的代码帮我们做了物理字体的封装,转换成了CFont进行渲染,而Linux环境的X11FontManager只是帮我们加载了物理字体和逻辑字体,但是却需要我们自己进行选择,那么解决问题的第一步就显而易见了:将Font的创建从物理字体改为逻辑字体
1 // Serif、SansSerif、Monospaced、Dialog 和 DialogInput 随意选择 2 Font font = new Font("Serif", Font.PLAIN, 10);
改完以后执行代码,仍然是乱码!继续Debug,发现是Linux上逻辑字体Serif映射的物理字体没有中文字体和对应的特殊符号字体,这就很简单了,直接在Linux上安装中文字体(simsun.ttf),再安装特殊符号“ꐚꌒꑿꆺ”可显示的字体(mysi.ttf),将这两个字体也放到了jdk的fonts目录(JAVA_HOME/jre/lib/fonts)下。文章后面有Linux字体安装方法。
完成上面的改动之后,重启服务,再次执行成功显示!热烈庆祝~~~~
以上的改动已经可以解决中文和特殊字符乱码问题,但是我在Debug过程中发现在逻辑字体加载过程中,JVM会参考一个配置文件,代码在sun.awt.FontConfiguration中,这个配置类完成了逻辑字体和物理字体的映射,也指导了SunFontManager创建逻辑字体,而这个FontConfiguration读取的配置文件就是fontconfig.properties,这个配置文件目录是JAVA_HOME/jre/lib
查阅了一下资料,JVM字体配置文件的加载顺序如下:
JAVA_HOME/jre/lib/fontconfig.OS.Version.properties
JAVA_HOME/jre/lib/fontconfig.OS.Version.bfc
JAVA_HOME/jre/lib/fontconfig.OS.properties
JAVA_HOME/jre/lib/fontconfig.OS.bfc
JAVA_HOME/jre/lib/fontconfig.Version.properties
JAVA_HOME/jre/lib/fontconfig.Version.bfc
JAVA_HOME/jre/lib/fontconfig.properties
JAVA_HOME/jre/lib/fontconfig.bfc
OS是系统,例如:Linux、CentOs、RedHat等;Version是版本号
在这个配置文件中可以修改逻辑字体与物理字体的对应关系,也就是说可以手动的修改Serif、SansSerif、Monospaced、Dialog 和 DialogInput这五个逻辑字体在不同场景下所使用的真正物理字体。
举个栗子,下面的配置将serif.plain逻辑字体的中文使用simsun.ttf,拉丁文使用java自带字体:
# @(#)linux.fontconfig.SuSE.properties 1.2 03/10/17 # # Copyright 2003 Sun Microsystems, Inc. All rights reserved. # # Version version=1 # Component Font Mappings serif.plain.chinese=-misc-simsun-medium-r-normal--*-%d-*-*-c-*-iso10646-1 serif.plain.latin-1=-b&h-lucidabright-medium-r-normal--*-%d-*-*-p-*-iso8859-1 # Search Sequences sequence.allfonts=latin-1,chinese # Exclusion Ranges # Font File Names filename.-misc-simsun-medium-r-normal--*-%d-*-*-c-*-iso10646-1=/usr/share/fonts/myfonts/simsun.ttf
PS:以上所有操作基本都需要root权限
推荐学习:《linux视频教程》
Das obige ist der detaillierte Inhalt vonSo lösen Sie das Problem verstümmelter chinesischer Zeichen in Java-Linux-Dateien. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!