3

JFreeChartを使用してグラフをレンダリングするWebアプリケーションを開発しています。ただし、サーバーに中国語のフォントがインストールされていない場合、フォントを設定してもJFreeChartで漢字が表示されません。

次に、小さなテストコードを記述し、グラフを描画する前にこのコード行を追加することで問題を解決できることを確認します。

GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(font);

だから私の質問は-

  1. ファイルからフォントを作成しても、JVMにフォントを登録する必要があるのはなぜですか?それは、JFreeChartが私が直接設定したフォントを使用しないことを意味しますか?

  2. プログラムをサーバーにデプロイすると、このコード行を追加しても、漢字が表示されません。すべての環境で文字を正しく表示するために、設定したフォントを常に使用するようにするにはどうすればよいですか?

fallbackディレクトリを作成し$JAVA_HOME/jre/libてフォントを入れることができることはわかっています。しかし、それでは、JFreeChartが設定したフォントで表示できない理由を説明できません。

アップデート

フォントが正しくロードされたと確信しているのでregisterFont()、プログラムをTomcatにデプロイするとtrueが返されます。

更新2

JAVA 2D FAQによるとregisterFont()、自分のフォントをJVMに「インストール」するために呼び出す必要があることに気付きました。私のフォントは、Fontコンストラクターを介して使用できるようになります。

Java SE 6の時点で、メソッドGraphicsEnvironment.registerFont()があります。これにより、「作成された」フォントをフォントコンストラクターで使用できるようにし、フォント列挙APIを介して一覧表示することができます。Font.createFont()とこのメソッドを組み合わせて、実行中のJREにフォントを「インストール」する方法を提供し、O/Sでインストールされたフォントと同じように使用できるようにします。ただし、このフォントはJRE呼び出し間で保持されません。

しかし、私はすでにFontインスタンスを作成/派生してcreateFont()いるので、なぜ私のプログラムはまだ他のインスタンスを作成する必要がないのFontですか?


以下は私が使用したコードで、PNG形式でチャートを出力するだけです。コードを実行する場合は、必要に応じて出力場所とフォントを変更する必要があります。これは、コードで使用する中国語フォントのSourceForgeリンクです。

import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.io.File;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.StandardChartTheme;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.data.general.PieDataset;

public class Problem {

  public static void main(String[] args) throws Exception {
    setJFreeChartTheme();

    PieDataset dataset = createDataSet();
    JFreeChart chart = ChartFactory.createPieChart(
        "Chinese Testing", dataset, true, true, false);
    ChartUtilities.saveChartAsJPEG(new File("/tmp/output.png"), 
        chart, 800, 600);

    System.out.println("Done");
  }

  private static void setJFreeChartTheme() throws Exception {
    Font font = loadFont();
    //==================================================================
    GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(font);
    //==================================================================
    StandardChartTheme theme = new StandardChartTheme("Chinese font", true);
    theme.setExtraLargeFont(font.deriveFont(Font.BOLD, 20));
    theme.setLargeFont(font.deriveFont(Font.BOLD, 16));
    theme.setRegularFont(font.deriveFont(Font.PLAIN, 14));
    theme.setSmallFont(font.deriveFont(Font.PLAIN, 12));
    ChartFactory.setChartTheme(theme);
  }

  private static Font loadFont() throws Exception {
    File file = new File("/tmp/wqy-zenhei.ttc");
    return Font.createFont(Font.TRUETYPE_FONT, file);
  }

  private static PieDataset createDataSet() {
    DefaultPieDataset dataset = new DefaultPieDataset();
    dataset.setValue("種類1", Integer.valueOf(1));
    dataset.setValue("種類2", Integer.valueOf(2));
    dataset.setValue("種類3", Integer.valueOf(3));
    return dataset;
  }
}
4

3 に答える 3

4

TTFから直接作成する場合Font、Javaは明らかに、その単一のFontオブジェクト内からフォントファイル自体のコピーを取得する場所を認識しています。では、なぜフォントを使用するためにフォントも登録する必要があるのでしょうか。答えは、常に登録する必要はない、または少なくとも制御のチェーン全体が元のFontオブジェクトを直接使用している限りはそうではないということです。

Javaがフォントをレンダリングしようとすると、実際に何が起こりますか?

ニュアンスは、JFreeChartがテキストのレンダリングを要求する方法にあります。テキストのレンダリングはTextUtilities#drawRotatedString、jcommonのメソッドで実行されます。JDK7では、このメソッドはデフォルトで次のようになります。

  • AttributedString渡したフォントの「属性」に基づいて作成し、
  • Graphics2D#drawString属性付きの文字列を呼び出します。
  • 新しいTextLayoutオブジェクトを作成します。

TextLayoutに提供する実際のFontオブジェクトを選択するクラスですGraphics2DTextLayout自動フォント選択を使用して文字列の各部分に適切なフォントを見つけることにより、さまざまなフォントを使用した多言語テキストのレンダリングをサポートするように設計されています(同じ単一のソース文字列を複数のフォントでレンダリングする必要がある場合でも)。

上記の「属性」は、指定したフォントから派生したフォントに関する単純な事実(フォントのファミリ名、サイズなど)Fontです。指定したフォントで入力文字列のすべての文字をレンダリングできない場合は、属性を使用して、別のフォントを使用する必要のある一連のテキストに使用する類似のフォントを選択します。

JFreeChartがTextLayoutを呼び出すと、常に次のように機能します。

  • Font指定したオブジェクトから属性を抽出し、
  • static を呼び出してFont#getFont、提供された属性に一致するフォントを取得し(を参照TextLayout#singleFont)、
  • 返された(おそらく異なる)Fontオブジェクトを使用してテキストを描画します。

フォントをどこかに静的に登録していない場合(のようにGraphicsEnvironment#registerFont)、静的 Font#getFontメソッドが実行する必要があるのは、フォントファミリ名を含む属性文字列だけです。フォントを実際にレンダリングするために必要なデータは言うまでもなく、TTFへの参照を含むFontオブジェクトにアクセスする場所もわかりません。

OK、でもフォントを登録する必要はないとおっしゃっていたと思いますか?

フォントを登録したくない場合の秘訣は、指定したFontオブジェクトのみを使用してテキストがレンダリングされるようにすることです。フォントの検索に使用される属性のセットではなくTextLayout、オブジェクトを直接受け入れる別のコンストラクターが存在する場合があります。Font

便利なことに、JFreeChartは、代わりにこのコンストラクターを使用するように強制する方法も提供します。ではTextUtilities#drawRotatedString、特別な設定パラメータを使用して、指定したTextLayoutオブジェクトを使用してJFreeChartにオブジェクト自体を作成させることがFontできます。

これを行うには、次のようにjcommon.propertiesファイルを設定します。

  • jcommon.properties(クラスパス/ JARのルートレベルで終わるはずの)という名前のリソースファイルを作成し、
  • 次の行を追加します。

org.jfree.text.UseDrawRotatedStringWorkaround=true

または、静的関数を呼び出すだけです。

TextUtilities.setUseDrawRotatedStringWorkaround(true)

これにより、JFreeChartは、フォントを直接使用してテキストをレンダリングするように求められます。フォントを登録しなくても動作します。これは、JFreeChartを使用してテキストをラスター画像に直接レンダリングするという上記の質問のコンテキストでテストされました。ディスプレイデバイスにレンダリングしようとしている場合(私は試していません)、マイレージは異なる場合があります。

フォントを登録しないのが賢明ですか?

はっきりとは言えません。私のアプリケーションの1つはOSGiコンテナー内で実行され、登録を解除できないフォントを静的に登録することによってPermGenクラスローダーのリークが発生することを警戒していました。Fontオブジェクトを使用すると、その問題を直接回避できます。そのため、このルートを使用したかったのです。これを行うと、特定のJavaプラットフォームで問題が発生する可能性が常にあると思いますが、これは、少なくともWindows、Linux、およびOracleJDK7を使用するOSXホストでのテストでは正常に機能します。

于 2014-10-03T01:54:27.357 に答える
1

ファイルからフォントを作成しても、JVMにフォントを登録する必要があるのはなぜですか?

JVMは他にどのようにしてフォントが存在することを認識しますか?

JavaがJFreeChartがチャートのレンダリングに使用するグラフィックス環境でフォントを描画する方法を認識できるように、フォントをJVMに登録する必要があります。

すべての環境で文字を正しく表示するために、設定したフォントを常に使用するようにするにはどうすればよいですか?

registerFont()メソッドがtrueを返すことを確認する必要があります。falseが返された場合、フォントは使用できません。

フォントを正しく読み込んでいるようです。フォントのファイルパスがサーバー上で正しくない可能性があります。あなたは試してみたいかもしれません

getClass().getResource(fontPath);
于 2012-08-24T17:05:03.043 に答える
1

これは古い質問だと思いますが、自分で答えを探していて、上記の回答を見て、フォントを登録する目的がよくわかりませんでした。問題を調査した後、これは私が見つけたものです:

フォントをグラフィックス環境に登録する必要はありませんが、登録すると、登録されたフォントを「newFont()」コンストラクターで使用できるという利点があります。

次のコードを使用して、現在使用可能な(つまり、インストールされ、アプリケーションで使用できる状態になっている)すべてのフォントのリストを取得できます。

String fonts[] = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();

Windowsを使用していて、インストールされているフォントの1つがArialであるとすると、次のようにアプリケーションでこのフォントを使用できます。

JButton yesButton = new JButton ("Yes");
yesButton.setFont(new Font("Arial", Font.PLAIN,30));

ここで、ファイルから独自のカスタムフォントを読み込んで使用するとします。

Font robotoFont = Font.createFont(Font.TRUETYPE_FONT,getClass().getResourceAsStream("/res/fonts/Roboto/Roboto-Light.ttf"));

これをJButtonのフォントとして設定する場合は、次のコードを記述します。

JButton yesButton = new JButton("Yes");
yesButton.setFont(robotoFont.deriveFont(Font.PLAIN, 30f));

しかし、次のようなコードを書き込もうとした場合:

JButton yesButton = new JButton("Yes");
yesButton.setFont(new Font ("Roboto Light", Font.PLAIN,30));

グラフィック環境は「RobotoLight」という名前のフォントを認識しないため、JButtonにはデフォルトのフォントが与えられます。これに対する解決策は、フォントをグラフィック環境に登録することです

 GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
 genv.registerFont(robotoFont);

これで、次のような'new Font()'コンストラクターでこのフォントを使用できるようになります。

JButton yesButton = new JButton("Yes");
bestButton.setFont(new Font ("Roboto Light", Font.PLAIN,30));
于 2017-07-29T20:12:08.183 に答える