座標系
PDF文書およびAspose.PDFライブラリを使用する際、座標系はページの左下隅から始まります。
点(0,0)は左下隅に対応します。これは、画像、テキスト、または他のオブジェクトを配置する際に、Y軸が上方向に増加することを覚えておくことが重要であることを意味します。他のいくつかの座標系とは異なります。
各ページには独自の座標系があります。座標はプラットフォームに依存しない単位で指定されます。PDF仕様では、PDFは複数の座標空間で動作することが記載されています。
デバイス空間
各出力デバイス(ディスプレイ、プリンターなど)は、画像をレンダリングするために独自の座標系を使用します。
このシステムはデバイス空間と呼ばれます。原点(点0,0)はデバイスによって異なる位置にある場合があります。
さらに、座標軸の向きも異なる場合があります:垂直Y軸は、下から上に増加する場合もあれば、上から下に増加する場合もあります。
デバイスの解像度も異なります。ディスプレイは通常、72または96ピクセル/インチの解像度を持ち、プリンターは300、600、またはそれ以上のドット/インチを持つ場合があります。
一部のデバイスは、水平および垂直軸に沿って異なる解像度を持つ場合もあります。
グラフィック要素がデバイス空間座標で直接指定されると、結果は特定のデバイスの特性に依存します。
これにより、歪んだレンダリングが発生する可能性があります:同じオブジェクトが画面上と印刷物で異なって表示されることがあります。
たとえば、72 ppiの解像度でディスプレイ座標で指定された8インチの長さの線は、600 dpiのプリンターで印刷されると1インチ未満の幅を占めます。
ユーザー空間
このような問題を避けるために、PDFはデバイスに依存しない座標系(ユーザー空間)を使用しており、出力デバイスに関係なく一貫したグラフィックスレンダリングを保証します。
ページのレンダリング中、コンテンツはユーザー空間からデバイス空間に変換され、出力デバイスの特定の機能を考慮します。
文書内の各ページには独自のユーザー空間座標系があります。両軸に沿った1単位の長さは1/72インチに等しいです。
UserUnitを設定することで測定単位を変更できます。デフォルトでは1に等しいです。この値はページオブジェクトのUserUnitプロパティを使用して指定できます。
UserUnitは1/72インチの乗数として機能します。
ユーザー空間からデバイス空間への変換は、現在の変換行列(CTM)によって定義されます。
画像1. 座標空間の行列変換。ISO 32000-2:2020仕様から取得
PDF演算子を使用してページコンテンツを作成する場合、Aspose.Pdf.ConcatenateMatrix
演算子を使用してCTM(現在の変換行列)を変更できます。これにより、現在の行列と提供した行列を連結します。
これにより、レンダリングされたコンテンツの回転、平行移動、スケーリングを行うことができます。
画像2. デバイス空間への変換。ISO 32000-2:2020仕様から取得
ユーザー空間とデバイス空間に加えて、PDFは特定の目的に応じた他のいくつかの座標系を使用しています:
テキスト空間
テキストは独自の座標系—テキスト空間に配置されます。テキスト空間からユーザー空間への変換は、専用のテキスト行列とさまざまなテキストレンダリング設定を使用して行われます。
グリフ空間
フォント文字(グリフ)はグリフ空間で定義されます。この空間はフォント行列を介してテキスト空間に変換されます。
ほとんどのフォントでは、1000グリフ空間単位 = 1テキスト空間単位のスケールが使用されます。
Type 3フォントなどの一部のフォントでは、この行列が明示的に定義されています。
画像空間
ラスター画像は独自の画像空間で定義されます。この空間は常に自動的にユーザー空間に変換されます:
画像は、実際の解像度に関係なく、幅と高さが1単位であると見なされます。
画像を正しく表示するには、スケールと位置を変換行列(スケーリング、回転)を変更することで設定します。
フォーム空間
フォーム(フォームXObjects)は、グラフィック要素として埋め込むことができる独立したコンテンツフラグメントです。
各フォームは独自のフォーム空間で定義され、フォーム行列を使用してユーザー空間に変換されます。
画像3. 座標空間間の関係。ISO 32000-2:2020仕様から取得
変換行列
この行列は、ページ上のオブジェクトのスケーリング、回転、平行移動、せん断、反射などの操作を可能にします。
これは3×3の二次元行列ですが、PDFでは6つの数値パラメータのみが使用されます:[a b c d e f]。
この行列は、変換された空間の各点(x, y)に次の式を使用して適用されます:
x’ = a * x + c * y + e
y’ = b * x + d * y + f
画像4. 座標変換方程式。ISO 32000-2:2020仕様から取得
座標変換の公式 — ISO 32000-2:2020仕様から取得。
行列要素の値に応じて、さまざまなタイプの変換を定義できます:
平行移動: [1 0 0 1 e f] — オブジェクトをX軸に沿ってe単位、Y軸に沿ってf単位移動します。
スケーリング: [a 0 0 d 0 0] — オブジェクトをX方向にa倍、Y方向にd倍スケーリングします。
回転: [cos(θ) sin(θ) -sin(θ) cos(θ) 0 0] — オブジェクトを角度θ(ラジアン)だけ回転させます。
せん断: X方向 [1 0 c 1 0 0]、Y方向 [1 b 0 1 0 0]。
反射: X軸に対して [1 0 0 -1 0 0]、Y軸に対して [-1 0 0 1 0 0]
変換行列を設定する例:
// For complete examples and data files, visit https://github.com/aspose-pdf/Aspose.PDF-for-.NET
var matrix = new Aspose . Pdf . Matrix ( new double []{ a , b , c , d , e , f });
page . Contents . Add ( new Aspose . Pdf . Operators . ConcatenateMatrix ( matrix ));
例1では、ページのコーナー座標でテキストをレンダリングしようとすると、原点にあるテキストのみが表示されることがわかります。
残りのテキストは表示領域の外にあり、クリップされます。
.NET Core 3.1
// For complete examples and data files, visit https://github.com/aspose-pdf/Aspose.PDF-for-.NET
private static string CoordinatesExample1 ()
{
// The path to the documents directory
var dataDir = RunExamples . GetDataDir_AsposePdf_Text ();
string outputFilePath = dataDir + "coordinates1.pdf" ;
// Create new document
using ( var document = new Aspose . Pdf . Document ())
{
// Get particular page
var page = document . Pages . Add ();
// Create TextBuilder object
var textBuilder = new Aspose . Pdf . Text . TextBuilder ( page );
DrawText ( "Low left" , 0 , 0 , textBuilder );
DrawText ( "Low right" , ( int ) page . CropBox . URX , 0 , textBuilder );
DrawText ( "Upper left" , 0 , ( int ) page . CropBox . URY , textBuilder );
DrawText ( "Upper right" , ( int ) page . CropBox . URX , ( int ) page . CropBox . URY , textBuilder );
// Save PDF document
document . Save ( outputFilePath );
}
return outputFilePath ;
}
private static void DrawText ( string text , int x , int y , TextBuilder textBuilder )
{
// Create text fragment
var textFragment = new Aspose . Pdf . Text . TextFragment ( text );
textFragment . Position = new Aspose . Pdf . Text . Position ( x , y );
// Set text properties
textFragment . TextState . FontSize = 12 ;
textFragment . TextState . Font = Aspose . Pdf . Text . FontRepository . FindFont ( "TimesNewRoman" );
// Append the text fragment to the PDF page
textBuilder . AppendText ( textFragment );
}
.NET 8
// For complete examples and data files, visit https://github.com/aspose-pdf/Aspose.PDF-for-.NET
private static string CoordinatesExample1 ()
{
// The path to the documents directory
var dataDir = RunExamples . GetDataDir_AsposePdf_Text ();
string outputFilePath = dataDir + "coordinates1.pdf" ;
// Create new document
using var document = new Aspose . Pdf . Document ();
// Get particular page
var page = document . Pages . Add ();
// Create TextBuilder object
var textBuilder = new Aspose . Pdf . Text . TextBuilder ( page );
DrawText ( "Low left" , 0 , 0 , textBuilder );
DrawText ( "Low right" , ( int ) page . CropBox . URX , 0 , textBuilder );
DrawText ( "Upper left" , 0 , ( int ) page . CropBox . URY , textBuilder );
DrawText ( "Upper right" , ( int ) page . CropBox . URX , ( int ) page . CropBox . URY , textBuilder );
// Save PDF document
document . Save ( outputFilePath );
return outputFilePath ;
}
private static void DrawText ( string text , int x , int y , TextBuilder textBuilder )
{
// Create text fragment
var textFragment = new Aspose . Pdf . Text . TextFragment ( text );
textFragment . Position = new Aspose . Pdf . Text . Position ( x , y );
// Set text properties
textFragment . TextState . FontSize = 12 ;
textFragment . TextState . Font = Aspose . Pdf . Text . FontRepository . FindFont ( "TimesNewRoman" );
// Append the text fragment to the PDF page
textBuilder . AppendText ( textFragment );
}
画像5. 例1の実行結果。
例2では、座標系をスケーリングし、ページの中央に移動します。
.NET Core 3.1
// For complete examples and data files, visit https://github.com/aspose-pdf/Aspose.PDF-for-.NET
private static void Example2 ()
{
// The path to the documents directory
var dataDir = RunExamples . GetDataDir_AsposePdf_Text ();
string sourcePdf = Example1 ();
// Open document
using ( var document = new Aspose . Pdf . Document ( sourcePdf ))
{
Page page = document . Pages [ 1 ];
// Get the page's dimensions
double pageWidth = page . CropBox . Width ;
double pageHeight = page . CropBox . Height ;
// Set the scale factor
double scale = 0.5 ;
// Calculate the offset
double offsetX = ( pageWidth - pageWidth * scale ) / 2 ;
double offsetY = ( pageHeight - pageHeight * scale ) / 2 ;
// Get the page's contents
var contents = page . Contents ;
// Add the current graphical state to the content's beginning
contents . Insert ( 1 , new Aspose . Pdf . Operators . GSave ());
// Scale and move the coordinate system to fit the content in the center of the page
contents . Insert ( 2 ,
new Aspose . Pdf . Operators . ConcatenateMatrix ( scale , 0 , 0 , scale , offsetX , offsetY ));
contents . Add ( new Aspose . Pdf . Operators . GRestore ());
// Save PDF document
document . Save ( dataDir + "coordinates2.pdf" );
}
}
.NET 8
// For complete examples and data files, visit https://github.com/aspose-pdf/Aspose.PDF-for-.NET
private static void Example2 ()
{
// The path to the documents directory
var dataDir = RunExamples . GetDataDir_AsposePdf_Text ();
string sourcePdf = Example1 ();
// Open document
using var document = new Aspose . Pdf . Document ( sourcePdf );
Page page = document . Pages [ 1 ];
// Get the page's dimensions
double pageWidth = page . CropBox . Width ;
double pageHeight = page . CropBox . Height ;
// Set the scale factor
double scale = 0.5 ;
// Calculate the offset
double offsetX = ( pageWidth - pageWidth * scale ) / 2 ;
double offsetY = ( pageHeight - pageHeight * scale ) / 2 ;
// Get the page's contents
var contents = page . Contents ;
// Add the current graphical state to the content's beginning
contents . Insert ( 1 , new Aspose . Pdf . Operators . GSave ());
// Scale and move the coordinate system to fit the content in the center of the page
contents . Insert ( 2 ,
new Aspose . Pdf . Operators . ConcatenateMatrix ( scale , 0 , 0 , scale , offsetX , offsetY ));
contents . Add ( new Aspose . Pdf . Operators . GRestore ());
// Save PDF document
document . Save ( dataDir + "coordinates2.pdf" );
}
画像6. 例2の実行結果。
Aspose.PDFにおける座標系と配置の理解
Aspose.PDFを使用する際には、配置に使用されるオブジェクトとそれぞれの座標系の違いを理解することが重要です。以下に、主要なオブジェクト(TextFragment、TextStamp、FloatingBox、Rectangle)とその使用例、利点と欠点について詳しく説明します。
TextFragment
座標系: 絶対位置決め(左下原点)。
説明: TextFragmentクラスを使用すると、絶対座標を使用してページの特定の位置にテキストを追加できます。
利点:
正確な位置にテキストを追加するためのシンプルで効率的な方法。
TextStampに比べて軽量な操作。
欠点:
回転やスケーリングなどの高度な変換をサポートしていません。
使用例: 追加の複雑さなしにページの特定の位置にテキストを追加する必要がある場合に使用します。
TextStamp
座標系: 絶対位置決め(左下原点)。
説明: TextStampクラスは、ページにスタンプとしてテキストを追加します。内部的には、ページのリソースにフォームを作成するため、TextFragmentよりも複雑な操作になります。
利点:
回転、スケーリング、透明度などの高度な変換をサポート。
ウォーターマークや繰り返しテキストを追加するのに最適。
欠点:
TextFragmentに比べてリソースを多く消費します。
設定がやや複雑です。
使用例: 変換や繰り返し使用が必要なウォーターマーク、ヘッダー、フッターを追加するために使用します。
FloatingBox
座標系: 相対位置決め(ページの余白に基づく)。
説明: FloatingBoxクラスは、テキストや他の要素を保持できるコンテナです。その位置はページの余白に対して定義されます。
利点:
ページレイアウトに対して要素を簡単に配置できます。
コンテンツの動的なサイズ変更とラッピングをサポート。
欠点:
正確でピクセル単位の配置には適していません。
複雑なレイアウトには追加の調整が必要です。
使用例: ヘッダー、フッター、サイドバーなど、コンテンツが動的に流れるレイアウトを作成するために使用します。
Rectangle
座標系: 絶対位置決め(左下原点)。
説明: Rectangleクラスは、ページ上に領域や形状を描画するために使用されます。また、特定の長方形領域内のテキストを検索するためにTextFragmentAbsorberとともに使用することもできます。
利点:
フレームやハイライトなどのグラフィック要素を描画するのに便利です。
テキストや画像などの他の要素と組み合わせることができます。
欠点:
長方形の形状に制限されています。
他のコンテンツと統合するために追加の手順が必要です。
使用例:
要素の周りにフレームを描画したり、ページ上の領域をハイライトするために使用します。
特定の領域内のテキストを検索するためにTextFragmentAbsorberとともに使用します。
.NET Core 3.1
// For complete examples and data files, visit https://github.com/aspose-pdf/Aspose.PDF-for-.NET
private static void DrawRectangle ()
{
// The path to the documents directory
var dataDir = RunExamples . GetDataDir_AsposePdf_Text ();
// Create PDF document
using ( var document = new Aspose . Pdf . Document ())
{
//Add the page
var page = document . Pages . Add ();
//Save graphicState
page . Contents . Add ( new Aspose . Pdf . Operators . GSave ());
//Colorize rectangle
var colorStroke = new Aspose . Pdf . Operators . SetRGBColorStroke ( 0.7 , 0 , 0 );
page . Contents . Add ( colorStroke );
//Create and add Rectangle on page
var rectangle = new Aspose . Pdf . Rectangle ( 100 , 100 , 200 , 150 );
page . Contents . Add (
new Aspose . Pdf . Operators . Re ( rectangle . LLX ,
rectangle . LLY ,
rectangle . Width ,
rectangle . Height ));
//Close path
page . Contents . Add ( new Aspose . Pdf . Operators . ClosePathStroke ());
//Restore graphic state
page . Contents . Add ( new Aspose . Pdf . Operators . GRestore ());
// Save PDF document
document . Save ( dataDir + "DrawRectangle.pdf" );
}
}
.NET 8
// For complete examples and data files, visit https://github.com/aspose-pdf/Aspose.PDF-for-.NET
private static void DrawRectangle ()
{
// The path to the documents directory
var dataDir = RunExamples . GetDataDir_AsposePdf_Text ();
// Create PDF document
using var document = new Aspose . Pdf . Document ();
//Add the page
var page = document . Pages . Add ();
//Save graphicState
page . Contents . Add ( new Aspose . Pdf . Operators . GSave ());
//Colorize rectangle
var colorStroke = new Aspose . Pdf . Operators . SetRGBColorStroke ( 0.7 , 0 , 0 );
page . Contents . Add ( colorStroke );
//Create and add Rectangle on page
var rectangle = new Aspose . Pdf . Rectangle ( 100 , 100 , 200 , 150 );
page . Contents . Add (
new Aspose . Pdf . Operators . Re ( rectangle . LLX ,
rectangle . LLY ,
rectangle . Width ,
rectangle . Height ));
//Close path
page . Contents . Add ( new Aspose . Pdf . Operators . ClosePathStroke ());
//Restore graphic state
page . Contents . Add ( new Aspose . Pdf . Operators . GRestore ());
// Save PDF document
document . Save ( dataDir + "DrawRectangle" );
}