私は、かなり単純でわかりやすいと思ったアプリケーションに取り組んでいます。BufferedImage
文字列を特定のフォント (システムにインストールされていないファイルから) にレンダリングするだけです。私はある程度の進歩を遂げていますが、数日間苦労した後、Java はフォント メトリックに関して非常に気まぐれなようです。明確な理由もなく、適切なフォント メトリックを提供したい場合もあれば、(フォントのサイズに関係なく) すべてゼロとすべて 240 の組み合わせを返したい場合もあります。
いくつかのケースでは、呼び出すだけf = f.deriveFont(myFloat)
でフォント メトリックが完全に失敗し、有効な数値がすべてゼロになるのに対し、呼び出しでf = f.deriveFont(Font.PLAIN, (int) myInt)
はメトリックが保持されるため、新しいフォント インスタンスが適切に機能することがわかりました。(はい、フロートであることはわかっていますが、何らかの理由で、フロートで呼び出すと、フォント メトリックが再び破られる場合がありました。)
TextAttributes
フォントとオブジェクトを組み合わせることでRenderingHints
、Graphics2D
ほとんどのフォントを機能させることができました...しかし、Java ( JSE /JDK 7 ) Windows 7のフォント ビューアーでは問題なく表示されます。
そのため、これらのフォントの 1 つを選択し、アプリケーションからすべてを取り除き、Main メソッドで可能なフォントの最も生のビューを取得しています。このコードのecho()
メソッドは、System.out.println()
. (また、このコードがずさんで申し訳ありません。この時点で新しい情報が得られる可能性のあるものは何でも入れています。)
BufferedImage img = new BufferedImage(730, 70, TYPE_INT_ARGB);
Graphics2D g = img.createGraphics();
FontRenderContext frc = g.getFontRenderContext();
String text = "Hello World";
File media = new File("z:\\path\\to\\fonts\\LondonDoodles.otf");
Font f7 = Font.createFont(Font.TRUETYPE_FONT, media);
f7 = f7.deriveFont(Font.PLAIN, (int) 20);
TextLayout lay = new TextLayout(text, f7, frc);
Rectangle2D r = lay.getPixelBounds(frc, 0, 0);
echo("NumGlyphs=" + f7.getNumGlyphs());
echo("bounds=" + r); // these bounds are all zeros
// use an array of GlyphCodes instead of a String
// to try and get at the glyphs directly without going through the CMAP
GlyphVector gv = f7.createGlyphVector(frc, new int[] { 1, 2, 3, 4, 5 });
echo("Glyph0Logical=" + gv.getGlyphLogicalBounds(0).getBounds2D()); // all 240s, go figure
echo("Glyph0Pixel=" + gv.getGlyphPixelBounds(0, frc, 0, 0)); // all zeros
echo("GlyphVectorLogical=" + gv.getLogicalBounds()); // random assortment of zeros & 240s
echo("GlyphVectorPixel=" + gv.getPixelBounds(frc, 0, 0)); // all zeros
echo(g.getFontMetrics(f7)); // 240s
LineMetrics lm = f7.getLineMetrics(text, frc);
echo("LineMetrics=" + lm.getAscent() + "/" + lm.getDescent() + "/" + lm.getHeight() + "/" + lm.getLeading()); // 240s
このコードからの出力は次のようになります。
NumGlyphs=33
bounds=java.awt.Rectangle[x=0,y=0,width=0,height=0]
Glyph0Logical=java.awt.geom.Rectangle2D$Float[x=240.0,y=240.0,w=240.0,h=240.0]
Glyph0Pixel=java.awt.Rectangle[x=0,y=0,width=0,height=0]
GlyphVectorLogical=java.awt.geom.Rectangle2D$Float[x=0.0,y=240.0,w=0.0,h=240.0]
GlyphVectorPixel=java.awt.Rectangle[x=0,y=0,width=0,height=0]
sun.font.FontDesignMetrics[font=java.awt.Font[family=London Doodles,name=LondonDoodles,style=plain,size=20]ascent=-239, descent=240, height=241]
LineMetrics=-240.0/240.0/240.0/240.0
このフォントが入った .otf ファイルから正しいフォント メトリックを取得することは明らかに可能です。これは、Windows 7 フォント ビューアーと C で記述された別のアプリを使用して、ここで行おうとしているように PNG にレンダリングできるためです。なぜJavaがそれを好まないのか理解できません。
IOException
または をスローしていないFontFormatException
ため、ファイルを読み取り、表面上は有効なFont
オブジェクトを作成していることがわかります。- 上記のコードでは
GlyphVector
、int 配列を使用して作成しました。これは、GlyphCodes
1 からフォント内のグリフの数 (この場合は 33) までのインデックスが付けられているため 、Unicode CodePointsからそれらの文字が見つからない可能性があります。 - また
FontMetrics
、フォントが 20pts のサイズを報告していることをエコーで確認できるため、サイズ = 0 などに設定されているわけではありません。フォントに設定したサイズに関係なく、出力の 240 は変わりません。
Google の検索用語を考え出そうと数日間試みた後でも、この特定の問題についての言及を見つけることができません。本当に奇妙に思えます。フォント メトリクスがランダムに左右に分かれているように見える (実際にはランダムではないことは確かですが) と思われるかもしれませんが、特に私がテストしたすべてのフォントが常にゼロと 240 の組み合わせを報告します。後者の部分では、すべてが一貫して 240 を報告していますが、これは奇妙に、Google で簡単に調べられない問題に特化しているように思えます。
とにかく、Java で不適切なフォント メトリックを引き起こす可能性のある項目 (テキスト属性、グラフィック設定、JRE フラグ、OS 環境変数、フォント作成プログラム、フォント ファイル内のフラグなど) のリストを誰かが持っているかどうか疑問に思っています。それとも壊れたフォント メトリクスの回避策のリストでしょうか? それとも、Java ではフォントがひどいので、LaTeX を調べる必要がありますか? (正直なところ、私はこの時点で何も知りません。)
ありがとう!
Font
編集:まあ...これはちょっとイライラします...OpenJDKの「オープンソース」を掘り下げれば、これらのメトリックの問題を解決できる派生実装を作成できるのではないかと思いました...しかし、どうやらそれは必要なすべての内部を非公開、静的、および最終的にすることで、意図的に防止されます。参照:フォント、それらを拡張する方法... 実行時にコア クラスを置き換えることができません - 参照: Java クラスを置き換えますか? ... したがって、メトリクスをそのように修正したい場合の唯一の代替手段は、Java フォント アーキテクチャ全体を 、 などの完全なコピーで再作成しjava.awt.Font
、sun.font.Font2D
グリフをGraphics2D
オブジェクトに手動でGraphics.drawGlyphVector()
描画するGraphics.drawString()
ことです。Font
または派生物Font
Font が実装するインターフェイスを宣言する代わりに、Font
クラスを引数として使用したためです。Graphics.setFont()
...その2番目のスタックオーバーフローリファレンスの答えを誤解していない限り? 私はそれを誤解していますか?Font
クラスを変更されたバージョンに置き換えるカスタム クラス ローダーを作成できますか?
編集:最後の編集を投稿する前にもっと調査を行うべきだったかもしれませんが、この質問を読んで疑問に思っている人は、はい、変更されたバージョンのコア Java クラスを JVM 引数に置き換えることができます。http://media.techtarget.com/tss/static/articles/content/CovertJava/Sams-CovertJava-15.pdfを参照メトリクスはフォント ファイルから読み込まれます。それが見つかれば、壊れたメトリックを修正する修正バージョンでそのクラスを置き換えることができます。