坐标系统
在 PDF 文档中以及使用 Aspose.PDF 库时,坐标系统从页面的左下角开始。
点 (0,0) 对应于左下角。这意味着在放置图像、文本或其他对象时,重要的是要记住 Y 轴向上增加,这与某些其他坐标系统不同。
每个页面都有自己的坐标系统。坐标以平台无关的单位指定。PDF 规范指出,PDF 使用多个坐标空间。
设备空间
每个输出设备——无论是显示器、打印机等——都使用自己的坐标系统来渲染图像。
这个系统称为设备空间。原点(点 0,0)可能位于不同的位置,具体取决于设备。
此外,坐标轴的方向可能会有所不同:垂直的 Y 轴可以从下到上增加,也可以从上到下增加。
设备的分辨率也各不相同。显示器的分辨率通常为每英寸 72 或 96 像素,而打印机的分辨率可能为 300、600 或更高的每英寸点数。
某些设备在水平和垂直轴上的分辨率也可能不同。
如果图形元素直接以设备空间坐标指定,则结果将依赖于特定设备的特性。
这可能导致渲染失真:同一对象在屏幕上和打印时可能看起来不同。
例如,在分辨率为 72 ppi 的显示坐标中指定的 8 英寸长的线,在 600 dpi 打印机上打印时将占用不到一英寸。
用户空间
为了避免此类问题,PDF 使用设备无关的坐标系统(用户空间),确保无论输出设备如何,图形渲染都保持一致。
在页面渲染过程中,内容从用户空间转换到设备空间,考虑到输出设备的特定特性。
文档中的每个页面都有自己的用户空间坐标系统。两个轴上一个单位的长度等于 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 单位的宽度和高度,而不管其实际分辨率如何。
为了正确显示图像,通过修改变换矩阵(缩放、旋转)来设置其比例和位置。
表单空间
表单(Form 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" );
}