目次|前|次 |
テキスト文字列では、Java 2D APIの変形および描画メカニズムを使用できます。また、Java 2D APIは、細かなフォント制御と洗練されたテキスト・レイアウトをサポートするテキスト関連クラスを提供します。これには、機能の強化されたFont
クラスと新しいTextLayout
クラスが含まれます。
この章では、java.awt
およびjava.awt.font
のインタフェースおよびクラスを介してサポートされる、新しいフォントおよびテキスト・レイアウト機能に焦点を当てて説明します。これらの機能の使用法についての詳細は、Javaチュートリアルの「Working with Text APIs」レッスンを参照してください。
テキスト分析と国際化については、java.text
のドキュメントとJavaチュートリアルの「Writing Global Programs」を参照してください。Swingに実装されているテキスト・レイアウト・メカニズムの使用法についての詳細は、java.awt.swing.text
のドキュメントとJavaチュートリアルの「Using the JFC/Swing Packages」を参照してください。
次の表は、フォントおよびテキスト・レイアウト関連の主なインタフェースとクラスです。これらのインタフェースとクラスのほとんどは、java.awt.font
パッケージに含まれています。Font
など、いくつかのインタフェースとクラスは、以前のバージョンのJDKとの下位互換性を維持するために、java.awt
パッケージの一部になっています。
Font
クラスは、詳細なフォント情報の特定と高度な文字体裁機能の使用を可能にするために強化されています。
Font
オブジェクトは、システム上で使用できるフォント・フェースのコレクションからのフォント・フェースのインスタンスを表します。一般的なフォント・フェースの例として、Helvetica Bold
、Courier Bold Italic
などがあります。
Font
に関連付けられる名前には、論理名、ファミリ名、およびフォント・フェース名の3つがあります。
Font
オブジェクトの論理名は、プラットフォーム上で使用可能な具体的なフォントの1つにマップされる名前です。Font
を指定する場合は、論理名ではなくフォント・フェース名を使用する必要があります。getName
を呼び出すと、Font
から論理名を取得できます。プラットフォーム上で使用可能な具体的なフォントにマップされた論理名のリストを取得するには、java.awt.Toolkit.getFontList
を呼び出してください。Font
オブジェクトのファミリ名は、Helveticaなど、複数のフェースにまたがって文字体裁のデザインを決定するフォント・ファミリの名前です。ファミリ名は、getFamily
メソッドを通じて取得します。Font
オブジェクトのフォント・フェース名は、システムにインストールされている実際のフォントを指します。JDKでフォントを指定するときは、このフォント・フェース名を使用する必要があります。フォント・フェース名は、単にフォント名と呼ばれることがよくあります。getFontName
を呼び出すことによってフォント名を取得できます。システム上で使用可能なフォント・フェース名を調べるには、GraphicsEnvironment.getAllFonts
を呼び出します。Font
に関する情報には、getAttributes
メソッドを通じてアクセスできます。Font
の属性には、名前、サイズ、変形、および、ウエイトやポスチャなどのフォント機能があります。
LineMetrics
オブジェクトは、アセント、ディセント、レディングといった、Font
に関連付けられた測定情報をカプセル化します。
これらの情報は、行に沿って文字を適切に配置し、複数の行を互いの位置関係によって配置するために使われます。これらの行メトリックスには、getAscent
、getDescent
、およびgetLeading
メソッドを通じてアクセスできます。また、LineMetrics
を通じて、Font
の高さ、ベースライン、下線、および取消し線に関する情報にアクセスすることもできます。
テキストを表示するには、適切なグリフと合字を使ってテキストの形状を決定し、配置しなければなりません。このプロセスのことを、テキスト・レイアウトと呼びます。テキスト・レイアウトのプロセスには、次のものが含まれます。
テキストをレイアウトするのに使われる情報は、キャレットの配置、ヒット検出、強調表示などのテキスト操作でも必要になります。
国際市場に展開できるソフトウェアを開発するには、適切な書記法の規則に従ってさまざまな言語でテキストをレイアウトしなければなりません。
グリフとは、1つまたは複数の文字の視覚的な表現です。グリフの形状、サイズ、および位置は、そのグリフが置かれたコンテキストに依存します。フォントとスタイルによっては、単一の文字または複数の文字の組み合わせを表すのに、多くの異なるグリフが使われることがあります。
たとえば、手書きの筆記体によるテキストでは、隣接する文字とどのように結び付くかによって、特定の文字がさまざまな形状を取ることがあります。
一部の書記法、特にアラビア語では、グリフのコンテキストを常に考慮しなければなりません。英語の場合と異なり、アラビア語では筆記体の使用は不可欠であり、筆記体を使用せずにテキストを表示することはできません。
これらの筆記体は、コンテキストによって大幅に形状が変わる可能性があります。たとえば、アラビア文字のhehには、次の図4-2に示すように4つの筆記体があります。
これら4つの形は、それぞれ非常に異なっています。このように形状が変化していることは、英語の筆記体の場合でも基本的に同じです。
コンテキストによっては、2つのグリフが形状を大きく変化させ、融合して単一のグリフを形成することもあります。この種の融合されたグリフは、合字と呼ばれます。たとえば、ほとんどの英語フォントには、図4-3に示すような合字fiがあります。この融合されたグリフでは、単に2つの文字を並べるのではなく、文字fの突き出た部分を考慮し、次の「i」と並べたときに自然に見えるように、2つの文字を結合しています。
合字は、アラビア語でも使われており、一部の合字の使用は不可欠です。つまり、適切な合字を使用せずに、特定の文字の組合せを表示することはできません。アラビア文字から形成される合字は、英語の場合よりもさらに形状が大きく変化しています。たとえば、図4-4は、となり合った2つのアラビア文字が1つに組み合わされてどのような合字を形成するかを示したものです。
Javaプログラミング言語では、テキストはUnicode文字エンコーディングを使用してエンコードされます。Unicode文字エンコーディングを使うテキストは、論理的順序に従ってメモリーに格納されます。論理的順序とは、文字や単語を読み書きする順序のことです。論理的順序は、対応するグリフを表示する順序である視覚的順序と必ずしも同じではありません。
ある特定の書記法(筆記)でのグリフの視覚的順序は、筆記順序と呼ばれます。たとえば、ローマ字テキストの筆記順序は左から右で、アラビア語とヘブライ語の筆記順序は右から左です。
書記法によっては、筆記順序に加えて、テキスト行にグリフや単語を配列するための規則を持つものがあります。たとえば、アラビア語とヘブライ語では、文字は右から左へと並べられますが、数字は左から右へと並べられます。したがって、英語のテキストが埋め込まれていない場合でも、アラビア語とヘブライ語は本当の意味で双方向言語であると言えます。
書記法の視覚的順序は、複数の言語が混在する場合でも維持しなければなりません。このことを示しているのが、英語の文の中にアラビア語の語句が埋め込まれた図4-5です。
注: 次の例とそのあとのいくつかの例では、アラビア語とヘブライ語のテキストを大文字で表し、空白はアンダースコアで表しています。各図には、メモリーに格納されている文字の表現(論理的順序の文字)と、実際に表示される文字の表現(視覚的順序の文字)の2つの部分があります。文字ボックスの下の数字は、挿入オフセットを表します。
アラビア語の単語は英語の文の一部ですが、アラビア語の筆記順序である右から左へと記述されています。イタリック体のアラビア語の単語は、プレーン・テキストのアラビア語の単語より論理的にあとにあるので、視覚的にはプレーン・テキストのアラビア語の左側にあります。
左から右に記述するテキストと右から左に記述するテキストが混在する行を表示する場合は、基準方向が重要です。基準方向とは、主要な書記法の筆記順序のことです。たとえば、テキストが主に英語で書かれていて、その中にアラビア語がいくつか埋め込まれている場合、基準方向は左から右になります。一方、テキストが主にアラビア語で書かれていて、いくつかの英語や数字が埋め込まれている場合は、基準方向は右から左になります。
通常の方向のテキストの一部を表示するときの順序は、基準方向によって決まります。図4-5に示した例では、基準方向は左から右です。この例には3つの方向があり、文頭の英語のテキストは左から右へ、アラビア語のテキストは右から左へ、ピリオドは左から右へ記述されています。
テキストの流れの中にグラフィックが埋め込まれることがよくあります。テキストの流れと行の折返しに与える影響という点では、これらのインライン・グラフィックは、グリフと同じように動作します。インライン・グラフィックが文字の流れの中で適切な場所に表示されるようにするには、グリフと同じ双方向レイアウト・アルゴリズムを使って、インライン・グラフィックを配置する必要があります。
1行中のグリフの順序を決定するために使われる正確なアルゴリズムの詳細は、『The Unicode Standard, Version 2.0』のセクション3.11「Bidirectional Algorithm」の説明を参照してください。
モノスペース・フォントを使っている場合は別ですが、1つのフォントでも文字によって幅は異なります。したがって、テキストの配置と寸法決定では、使われている文字数ではなく、どの文字が使われているかを正確に把握する必要があります。たとえば、プロポーショナル・フォントで表示される数字の列を右揃えする場合、空白をいくつか追加することによってテキストを配置することはできません。列を適切にそろえるには、各数字の正確な幅を調べ、その幅に応じて適切に調整を行う必要があります。
テキストは、複数のフォントや、太字、イタリックなどのさまざまな字体を使って表示されることがあります。この場合は、どのような字体が使われているかによって、同じ文字でも形状や幅が異なる可能性があります。テキストの適切な配置、寸法測定、およびレンダリングを行うには、各文字とその文字に適用される字体の両方を把握する必要があります。TextLayout
は、この処理をユーザーに代わって行います。
ヘブライ語やアラビア語などの言語でテキストを適切に表示するには、各文字の寸法を測定し、隣接する文字のコンテキストの中で文字を配置する必要があります。文字の形状と位置はコンテキストによって変わることがあるので、コンテキストを考慮せずにこれらのテキストの寸法決定と配置を行うと、得られる結果は不適切なものになります。
表示されているテキストを編集できるようにするには、次のことが可能でなければなりません。
編集可能なテキストでは、現在の挿入ポイントをグラフィカルに表すためにキャレットが使われます。挿入ポイントとは、テキスト内で新しい文字が挿入される位置のことです。通常、キャレットは、2つのグリフの間の点滅する縦線で表示されます。新しい文字は、このキャレットの場所に挿入され、表示されます。
キャレット位置の計算は、特に双方向テキストの場合には複雑になることがあります。双方向テキストでは、文字オフセットに対応する2つのグリフが互いに隣接して表示されるわけではないので、方向の境界上の挿入オフセットは、キャレット位置として2つの可能性を持ちます。これを示しているのが図4-6です。この図では、キャレットがどのグリフに対応しているかを示すために、キャレットが角カッコで表示されています。
文字オフセット8は、「_」の後、Aの前の場所に対応しています。ここでユーザーがアラビア語の文字を入力すると、入力した文字のグリフはAの右(前)に表示されます。英語の文字を入力すると、そのグリフは_の右(後)に表示されます。
このような状況に対処するために、一部のシステムでは、強い(プライマリ)キャレットと弱い(セカンダリ)キャレットのデュアル・キャレットを表示します。強いキャレットは、文字の方向がテキストの基準方向と同じ場合に、挿入された文字が表示される場所を示します。弱いキャレットは、文字の方向が基準方向と逆の場合に、挿入された文字が表示される場所を示します。TextLayout
はデュアル・キャレットを自動的にサポートしますが、JTextComponent
はデュアル・キャレットをサポートしていません。
双方向テキストを対象とする場合は、文字オフセットの前にグリフの幅を単純に加えるだけでは、キャレット位置を計算することはできません。このような方法を使用した場合、図4-7に示すように、キャレットが間違った場所に描画されてしまいます。
キャレットを適切に配置するには、オフセットの左側にあるすべてのグリフの幅を追加するとともに、現在のコンテキストを考慮する必要があります。コンテキストを考慮に入れないと、グリフのメトリックスが表示と一致しなくなる可能性があります。どのグリフが使われるかは、コンテキストによっても左右されるからです。
どのテキスト・エディタでも、ユーザーは矢印キーを使用してキャレットを移動できます。ユーザーは、自分が押した矢印キーの方向にキャレットが移動することを期待しています。左から右に記述するテキストでは、挿入オフセットの移動も単純です。右矢印キーが押されたら挿入オフセットを1つ増やし、左矢印キーが押されたら挿入オフセットを1つ減らします。双方向テキストや、合字が含まれたテキストでは、矢印キーを押すと、方向の境界でキャレットがいくつかのグリフを飛び越え、方向が逆になる部分では逆方向にキャレットが移動することになります。
双方向テキストでキャレットを円滑に移動するには、テキストの方向を考慮する必要があります。右矢印キーが押されたときに挿入オフセットを1つ増やし、左矢印キーが押されたときに挿入オフセット1つ減らすだけでは不十分です。現在の挿入オフセットが、右から左に記述する文字の中にある場合は、右矢印キーが押されたら挿入オフセットを減らし、左矢印キーが押されたら挿入オフセットを増やす必要があります。
方向の境界にまたがったキャレットの移動は、さらに複雑になります。図4-8は、ユーザーが矢印キーを使って移動中に、方向の境界を越えるとどのようなことが起こるかを示しています。表示されているテキスト内で右に3つ移動すると、それぞれオフセット7、19、および18の文字に移動することになります。
グリフによっては、その間にキャレットを置くことができないものがあります。この場合は、これらのグリフがあたかも1つの文字を表しているかのように、キャレットを移動する必要があります。たとえば、oとウムラウトが2つの独立した文字によって表されている場合、oとウムラウトの間にキャレットを置くことはできません。(詳細は、『The Unicode Standard, Version 2.0』の第5章を参照。)
TextLayout
は、双方向テキストでのキャレットの円滑な移動を簡単に実現するためのメソッド(getNextRightHit
とgetNextLeftHit
)を提供しています。
デバイス空間内での場所は、しばしばテキスト・オフセットに変換しなければなりません。たとえば、選択可能なテキスト上でユーザーがマウスをクリックした場合、マウスの場所はテキスト・オフセットに変換され、選択範囲の一方の端として使われます。これは、論理的にはキャレットの配置と逆の操作です。
双方向テキストを対象とする場合、ディスプレイ内の視覚的には単一の場所が、元のテキストでは2つの異なるオフセットに対応することがあります。このことを示しているのが、図4-9です。
視覚的には単一の場所が、2つの異なるオフセットに対応することがあるので、双方向テキストのヒット判定では、目的の場所のグリフが見つかるまでグリフの幅を計算し、該当するグリフが見つかったらその位置を文字オフセットに対応付けるという処理だけでは不十分です。2つの選択肢のうち適切なものを選ぶには、ヒットがあったのはどちら側かを検出する必要があります。
TextLayout.hitTestChar
を使うと、ヒット判定を行うことができます。ヒット情報はTextHitInfo
オブジェクトの中にカプセル化され、ヒットがあったのはどちら側かについての情報もその中に含まれています。
選択範囲の文字は、強調表示領域によってグラフィカルに表示されます。強調表示領域では、グリフは反転表示されるか、または異なる背景色の上に表示されます。
双方向テキストの場合は、キャレット同様、強調表示領域も単方向テキストの場合より複雑になります。双方向テキストでは、隣接する範囲の文字でも、表示されたときに強調表示領域が隣接しないことがあります。逆に、強調表示領域が、視覚的に隣接する範囲のグリフを示している場合でも、単一の隣接する範囲の文字に対応するとはかぎりません。
このため、双方向テキストで選択部分を強調表示する場合は、次の2つの方法が存在することになります。
論理的強調表示の方が実装は容易です。これは、選択された文字がテキスト内で常に隣接するためです。
使用するJava APIに応じて、テキスト・レイアウトの制御を必要なだけ使用できます。
JTextComponent
を使用できます。JTextComponent
は、ほとんどの国際アプリケーションのニーズに対処できるように設計されており、双方向テキストをサポートしています。JTextComponent
の詳細は、JavaチュートリアルのJFC/Swingパッケージの使用に関するトピックを参照してください。Graphics2D.drawString
を呼び出し、文字列のレイアウトをJava 2Dに行わせることができます。drawString
は、字体付き文字列や双方向テキストを含む文字列のレンダリングにも使用できます。Graphics2D
によるテキストのレンダリングの詳細は、「グラフィックス・プリミティブのレンダリング」を参照してください。TextLayout
を使うと、テキスト・レイアウト、強調表示、およびヒット検出を管理できます。TextLayout
が提供する機能を利用すれば、さまざまなフォント、言語、および双方向テキストが混在するテキスト文字列など、ほとんどの場合に対処できます。TextLayoutの使用法の詳細は、「テキスト・レイアウトの管理」を参照してください。Font
を使って独自のGlyphVectors
を構築し、これらをGraphics2D
を通じてレンダリングできます。独自のテキスト・レイアウト・メカニズムの実装方法の詳細は、「独自のテキスト・レイアウト・メカニズムの実装」を参照してください。一般に、テキスト・レイアウト操作をユーザーが行う必要はありません。ほとんどのアプリケーションでは、JTextComponent
が、静的で編集可能なテキストを表示するための最良の解決方法です。ただし、JTextComponent
は、双方向テキストでのデュアル・キャレットや、隣接してない選択部分の表示をサポートしていません。アプリケーションでこれらの機能が必要な場合、または独自のテキスト編集ルーチンを実装する場合は、Java 2Dテキスト・レイアウトAPIを使用できます。
TextLayout
クラスは、アラビア語やヘブライ語などのさまざまな書記法の複数の字体と文字を含むテキストをサポートしています。(アラビア語とヘブライ語の場合、許容できる程度の表示を行うにはテキストの形状決定と順序付けをやり直す必要があるので、表示は特に困難です。)
TextLayout
を使うと、英語だけのテキストを対象とする場合でも、テキストの表示と寸法決定のプロセスは単純化されます。TextLayout
を使えば、手間をかけることなく高品質のタイポグラフィを実現できます。
テキスト・レイアウトのパフォーマンス
|
---|
TextLayout は、単純な単方向テキストを表示する場合でも、パフォーマンスに大きな影響を与えることがないように設計されています。TextLayout を使ってアラビア語やヘブライ語を表示する場合は、処理のオーバーヘッドはやや増えます。ただし、1文字あたりマイクロ秒程度のオーバーヘッドであり、通常の描画コードの実行と比べても非常に小さい値にとどまっています。 |
TextLayout
クラスは、ユーザーに代わってグリフの配置と順序付けを管理します。TextLayout
を使うと、次のことができます。
場合によっては、テキスト・レイアウトを自分で計算し、使用するグリフとグリフを配置する場所を正確に制御したいことがあります。グリフのサイズ、カーニング表、および合字に関する情報を使えば、テキスト・レイアウトを計算するための独自のアルゴリズムを構築し、システムのレイアウト・メカニズムをバイパスできます。詳細は、「独自のテキスト・レイアウト・メカニズムの実装」を参照してください。
TextLayout
は、双方向(BIDI)テキストも含め、正しい形状と順序でテキストを自動的にレイアウトします。1行のテキストを表すグリフの形状決定と順序付けを適切に行うためには、TextLayout
はテキストの完全なコンテキストについて知る必要があります。
TextLayout
を構築できます。TextLayout
を構築することはできません。この場合は、十分なコンテキストを提供するためにLineBreakMeasurer
を使う必要があります。テキストの基準方向は、通常、テキストの属性(スタイル)によって設定されます。この属性がない場合、TextLayout
はUnicodeの双方向アルゴリズムに従って、段落内の最初のいくつかの文字から基準方向を導き出します。
TextLayout
は、キャレットのShape
、位置、および角度など、キャレットに関する情報を保持しています。この情報を使うと、単方向テキストと双方向テキストのどちらでも、キャレットを簡単に表示できます。双方向テキストでキャレットを描画する場合は、TextLayout
を使うと、キャレットが適切な位置に置かれることが保証されます。
TextLayout
は、デフォルトのキャレットShapes
を提供しており、デュアル・キャレットを自動的にサポートします。TextLayout
は、イタリック体と斜体のグリフに対しては、図4-12に示すような角度付きのキャレットを作成します。これらのキャレット位置は、強調表示とヒット判定ではグリフ間の境界としても使われ、ユーザーに違和感を与えないようになっています。
挿入オフセットが与えられると、getCaretShapes
メソッドは2つの要素からなるShape
の配列を返します。このうち要素0には強いキャレットが、要素1には弱いキャレット(存在する場合)が含まれています。デュアル・キャレットは、これらのShape
を両方ともレンダリングするだけで表示できます。キャレットは、自動的に適切な位置にレンダリングされます。
独自のキャレットShapes
を使う場合は、TextLayout
からキャレットの位置と角度を取り出して自分でキャレットを描画できます。
次の例は、デフォルトの強いキャレットと弱いキャレットのShapes
を、色を変えて描画します。これは、デュアル・キャレットを区別するための一般的な方法です。
Shape[] caretShapes = layout.getCaretShapes(hit); g2.setColor(PRIMARY_CARET_COLOR); g2.draw(caretShapes[0]); if (caretShapes[1] != null){ g2.setColor(SECONDARY_CARET_COLOR); g2.draw(caretShapes[1]); }
TextLayout
を使って、ユーザーが左矢印キーまたは右矢印キーを押したときに挿入オフセットを決定することもできます。現在の挿入オフセットを表すTextHitInfo
オブジェクトを指定すると、getNextRightHit
メソッドは、右矢印キーが押された場合の正しい挿入オフセットを表すTextHitInfo
オブジェクトを返します。getNextLeftHit
メソッドは、左矢印キーについて同じ情報を返します。
次の例では、右矢印キーに反応して現在の挿入オフセットが移動します。
TextHitInfo newInsertionOffset = layout.getNextRightHit(insertionOffset); if (newInsertionOffset != null) { Shape[] caretShapes = layout.getCaretShapes(newInsertionOffset); // draw carets ... insertionOffset = newInsertionOffset; }
TextLayout
は、テキストのヒット判定を行うための簡単なメカニズムを提供しています。hitTestChar
メソッドは、マウスからのx座標とy座標を引数に取り、TextHitInfo
オブジェクトを返します。TextHitInfo
には、指定された位置の挿入オフセットと、ヒットがあったのはどちら側かが含まれています。挿入オフセットは、ヒットにもっとも近いオフセットです。ヒットが行の終端からはみ出している場合は、行の終端のオフセットが返されます。
次の例は、TextLayout
に対してhitTestChar
を呼び出し、getInsertIndex
を使ってオフセットを取り出します。
強調表示領域を表すShape
はTextLayout
から取得できます。TextLayout
は、強調表示領域の大きさを計算するときに、コンテキストを自動的に考慮します。TextLayout
は、論理的強調表示と視覚的強調表示の両方をサポートしています。
次の例は、強調表示領域を強調表示色で塗りつぶし、塗りつぶした領域の上にTextLayout
を描画します。これは、強調表示テキストを表示するための1つの簡単な方法です。
Shape highlightRegion = layout.getLogicalHighlightShape(hit1, hit2); graphics.setColor(HIGHLIGHT_COLOR); graphics.fill(highlightRegion); graphics.drawString(layout, 0, 0);
TextLayout
では、このオブジェクトが表しているテキストのすべての範囲のグラフィカル・メトリックスにアクセスできます。TextLayout
から取得できるメトリックスには、アセント、ディセント、レディング、有効幅、可視有効幅、および境界の矩形領域があります。
1つのTextLayout
には、複数のFont
を関連付けることができ、異なるスタイル・ランには異なるフォントを使用できます。TextLayout
のアセントとディセントの値は、そのTextLayout
で使われているすべてのフォントを通じての最大値です。TextLayout
のレディングの計算はより複雑で、単にレディングの最大値ではありません。
TextLayout
の有効幅とは、長さのことです。つまり、いちばん左のグリフの左端からいちばん右のグリフの右端までの距離です。有効幅は、有効幅の合計と呼ばれることもあります。可視有効幅は、後続の空白を含まないTextLayout
の長さです。
TextLayout
のバウンディング・ボックスは、レイアウト内のすべてのテキストを囲みます。これには、すべての可視グリフおよびキャレットの境界が含まれます。(これらのうち一部のものは、起点または起点に有効幅を加えたものからはみ出ることがあります。)バウンディング・ボックスは、画面上の特定の位置に対するものではなく、TextLayout
の起点に対する相対的なものです。
次の例は、TextLayout
のテキストを、レイアウトのバウンディング・ボックスの中に描画します。
graphics.drawString(layout, 0, 0); Rectangle2D bounds = layout.getBounds(); graphics.drawRect(bounds.getX()-1, bounds.getY()-1, bounds.getWidth()+2, bounds.getHeight()+2);
TextLayout
を使って、複数の行にまたがるテキストを表示することもできます。たとえば、1つの段落を対象に、ある一定の幅で行を折り返して複数行のテキストとして表示できます。
この場合、テキストの各行を表すTextLayout
を直接作成することはしません。これらは、ユーザーに代わってLineBreakMeasurer
が生成します。双方向テキストでは、段落内のすべてのテキストを把握するまでは順序付けを適切に行うことができない場合があります。LineBreakMeasurer
は、コンテキストに関する十分な情報をカプセル化するので、正しいTextLayout
を生成できます。
複数の行にまたがってテキストを表示する場合、行の長さは一般に表示領域の幅によって決まります。行ブレーク(行の折り返し)は、行を収めなければならないグラフィカルな幅に基づいて、行の開始場所と終了場所を決定するプロセスです。
もっとも一般的な方法は、各行に収まる最大数の単語を配置する方法です。この方法は、LineBreakMeasurer
に実装されています。このほかに、より複雑な行ブレークの方法として、ハイフネーションを使う、段落内部での行の長さをできるだけそろえるなどの方法があります。Java 2D APIは、これらの方法の実装は提供していません。
テキストの段落を複数の行に分割するには、段落全体に対してLineBreakMeasurer
を構築し、次にnextLayout
を呼び出してテキストを順次処理し、行ごとにTextLayout
を生成します。
LineBreakMeasurer
は、この処理を行うために、テキスト内でのオフセットを保持しています。最初は、オフセットはテキストの先頭にあります。このオフセットは、nextLayout
を呼び出すたびに、生成されたTextLayout
の文字カウントの分だけ移動します。オフセットがテキストの末尾に到達すると、nextLayout
はnull
を返します。
LineBreakMeasurer
が生成する各TextLayout
の可視有効幅は、指定された行の幅を超えることはありません。nextLayout
を呼び出すときに、指定する幅を変えると、固定位置にイメージがあるHTMLページや、タブで区切られたフィールドなど、複雑な領域にテキストを分割して収めることができます。BreakIterator
を渡して、有効な分割点がどこかをLineBreakMeasurer
に指示することもできます。BreakIteratorを渡さなかった場合は、デフォルト・ロケールのBreakIterator
が使われます。
次の例では、2か国語のテキスト・セグメントが1行ずつ描画されます。各行は、基準方向が左から右か右から左かに応じて、左マージンまたは右マージンのどちらかにそろえられます。
Point2D pen = initialPosition; LineBreakMeasurer measurer = new LineBreakMeasurer(styledText, myBreakIterator); while (true) { TextLayout layout = measurer.nextLayout(wrappingWidth); if (layout == null) break; pen.y += layout.getAscent(); float dx = 0; if (layout.isLeftToRight()) dx = wrappingWidth - layout.getAdvance(); layout.draw(graphics, pen.x + dx, pen.y); pen.y += layout.getDescent() + layout.getLeading(); }
GlyphVector
クラスは、独自のレイアウト・メカニズムの処理結果を表示する手段を提供します。GlyphVector
オブジェクトは、文字列を受け取って、この文字列をどのように表示するかを正確に計算するアルゴリズムの出力とみなすことができます。システムには組込みのアルゴリズムが1つありますが、Java 2D APIでは独自にアルゴリズムを定義できます。
GlyphVector
オブジェクトは、基本的にはグリフとグリフの位置の配列です。文字の代わりにグリフを使うのは、カーニング、合字などのレイアウトの特性を完全に制御できるようにするためです。たとえば、文字列「final」を表示するときに、先頭のfiという部分文字列を合字のfiで置き換えたいことがあります。この場合、GlyphVector
オブジェクトは、元の文字列に含まれる文字の数より少ない数のグリフを持つことになります。
図4-13と図4-14は、レイアウト・メカニズムによってGlyphVector
オブジェクトがどのように使われるかを示しています。図4-13は、デフォルトのレイアウト・メカニズムを示しています。String
に対してdrawString
が呼び出されると、組込みレイアウト・アルゴリズムは次の処理を行います。
Graphics2D
コンテキスト内の現在のFont
を使って、使用するグリフを決定する。GlyphVector
に格納する。GlyphVector
を、実際のレンダリングを行うグリフ・レンダリング・ルーチンに渡す。図4-14は、独自のレイアウト・アルゴリズムを使う場合の処理を示しています。独自のレイアウト・アルゴリズムを使うには、テキストをレイアウトするのに必要なすべての情報を収集しなければなりません。基本的な処理は次のとおりです。
テキストをレンダリングするには、GlyphVector
をdrawString
に渡します。drawStringは、受け取ったGlyphVectorをグリフ・レンダラに渡します。図4-14では、独自のレイアウト・アルゴリズムがfiという部分文字列を合字のfiで置き換えています。
Font.deriveFont
メソッドを使うと、既存のFont
オブジェクトから、属性の異なる新しいFont
オブジェクトを生成できます。よく使われるのは、既存のFont
を変形して新しい派生Font
を作成する方法です。この手順は次のとおりです。
この方法によって、独自のサイズのFont
や既存Font
の歪んだ種類を簡単に作成できます。
次のコードでは、AffineTransform
を適用し、フォントHelveticaの歪んだ種類を作成します。次に、新しい派生フォントを使って文字列をレンダリングします。
// Create a transformation for the font. AffineTransform fontAT = new AffineTransform(); fontAT.setToShear(-1.2, 0.0); // Create a Font Object. Font theFont = new Font("Helvetica", Font.PLAIN, 1); // Derive a new font using the shear transform theDerivedFont = theFont.deriveFont(fontAT); // Add the derived font to the Graphics2D context g2.setFont(theDerivedFont); // Render a string using the derived font g2.drawString(“Java”, 0.0f, 0.0f);
目次|前|次 |