PDF标准详解(三)—— PDF坐标系统和坐标变换

之前我们了解了PDF文档的基本结构,并且展示了一个简单的hello world。这个hello world 虽然只在页面中显示一个hello world 文字,但是包含的内容却是不少。这次我们仍然以它为切入点,来了解PDF的坐标系统以及坐标变换的相关知识

图形学中二维图形变换

中学我们学习了平面直角坐标系,x轴沿着水平方向从左往右递增,Y轴沿着竖直方向,从下往上坐标递增。而PDF的坐标系与数学中的坐标系相同。但是PDF的坐标是有单位的,PDF的坐标单位为磅,一般来说他们与英寸等的转化关系为
1 磅 = 1/72 英寸
因为PDF需要做到设备无关,也是就是在不同的显示像素和打印机上,显示的长度都一致,所以这里不能采用像素做单位。但是我们可以通过相关的接口来将这个单位转化为像素。例如在Windows平台可以通过下列的代码来获取一英寸有多少像素

HDC hdc = GetDC(NULL);
short cxInch = GetDeviceCaps(hdc, LOGPIXELSX);
short cyInch = GetDeviceCaps(hdc, LOGPIXELSY);
ReleaseDC(NULL, hdc);

对于我的显示器来说,水平和竖直方向都是 1英寸=96像素

有了这些概念之后,我们来看一个例子,下面是在页面的(200, 200) 位置画一个 长宽都为100的正方形

3 0 obj % 页面内容流
<< >>
stream % 流的开始
200 200 100 100 re S
endstream % 流结束
endobj

之前说过,页面显示内容在页面流中,因此这里我们将内容放置到页面流对象中。前面的200 200 是矩形的起始位置。后面的100 100 分别是长和宽。re 代表我们要构建一个矩形,最后的S表示要显示这个图形。严格意义上来说,re 和S都是路径构造所使用的操作符。这里的矩形也不单单是一个图形,它是一个路径。关于他们的概念将在后面继续介绍。下面我们来介绍基本的2D图形变换

平移

平移

假设一个点原始坐标是(x1, x2),那么沿着x轴平移a,y轴平移b,那么平移之后点的坐标为 (x1 + a, x2 + b) ,转换成矩阵就是
[ x y 1 ] [ 1 0 0 0 1 0 a b 1 ] = [ x + a y + b 1 ] \begin{bmatrix} x & y & 1\end{bmatrix} \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ a & b & 1 \end{bmatrix} = \begin{bmatrix}x + a & y + b & 1\end{bmatrix} [xy1] 10a01b001 =[x+ay+b1]

旋转

旋转
利用中学的知识可以知道
x 1 = r ∗ c o s ( θ + ψ ) = r ∗ ( c o s θ ∗ c o s ψ − s i n θ ∗ s i n ψ ) = x ∗ c o s θ − y s i n θ x_1 = r * cos(\theta+\psi) \ = r*(cos\theta*cos\psi-sin\theta*sin\psi) \ = x*cos\theta-ysin\theta x1=rcos(θ+ψ) =r(cosθcosψsinθsinψ) =xcosθysinθ

同理,我们可以得到
y 1 = r ∗ s i n ( θ + ψ ) = r ∗ ( s i n θ ∗ c o s ψ + c o s θ ∗ s i n ψ ) = x ∗ s i n θ + y ∗ c o s θ y_1 = r * sin(\theta+\psi) \ = r * (sin\theta*cos\psi+cos\theta*sin\psi)\ = x*sin\theta+y*cos\theta y1=rsin(θ+ψ) =r(sinθcosψ+cosθsinψ) =xsinθ+ycosθ

转换成矩阵就是
[ x y 1 ] [ c o s θ s i n θ 0 − s i n θ c o s θ 0 0 0 1 ] = [ x ∗ c o s θ − y ∗ s i n θ x ∗ s i n θ + y ∗ c o s θ 1 ] \begin{bmatrix} x & y & 1\end{bmatrix} \begin{bmatrix} cos\theta & sin\theta & 0 \\ -sin\theta & cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix}x*cos\theta - y*sin\theta & x*sin\theta+y*cos\theta & 1\end{bmatrix} [xy1] cosθsinθ0sinθcosθ0001 =[xcosθysinθxsinθ+ycosθ1]

缩放

缩放就是将坐标扩大或者缩小为原来的多少倍,我们可以很清楚的知道
x 1 = x ∗ a y 1 = y ∗ b x_1=x*a y_1=y*b x1=xay1=yb

这里的a和b都是缩放的系数
利用矩阵表示就是
[ x y 1 ] [ a 0 0 0 b 0 0 0 1 ] \begin{bmatrix} x & y & 1\end{bmatrix} \begin{bmatrix} a & 0 & 0 \\ 0 & b & 0 \\ 0 & 0 & 1\end{bmatrix} [xy1] a000b0001

pdf 矩阵变换

还有另外几种变换,这里就不一一列举了。现在我们知道二维图形的变换使用一个矩阵就能进行描述。所以PDF在变换图形的时候直接使用的是变换的矩阵。另外我们观察到对于二维变换来说,最后一列一直都是 0 0 1这三个数字。所以pdf中设置变换矩阵时忽略最后一列,仅仅保留前两列,采用6个数字
[ a b 0 c d 0 e f 1 ] \begin{bmatrix}a & b & 0 \\ c & d & 0 \\ e & f & 1\end{bmatrix} acebdf001

这个矩阵在PDF中表现为 a b c d e f。

回到我们之前hello的例子中,我们在 hello world 字符流开始的时候,给定了几个数字
1. 0. 0. 1. 50. 700. cm
各个数字之间采用空格隔开,这里数字后面跟的点表示它是一个浮点数。我们可以将这一列数字写成如下的矩阵
[ 1.0 0.0 0 0.0 1.0 0 50.0 700.0 1 ] \begin{bmatrix}1.0 & 0.0 & 0 \\ 0.0 & 1.0 & 0 \\ 50.0 & 700.0 & 1 \end{bmatrix} 1.00.050.00.01.0700.0001

这个矩阵我们叫做当前变换矩阵 (Current Transformation Matrix CTM),最后的cm表示使用该矩阵进行图形变换。它是current matrix 的缩写

所以上述这一串数值的意思就是将 hello world 这个字符串平移到页面坐标 (50, 700) 的位置

PDF 中控制图形变换的操作符

现在我们利用这个上述知识来做一个小练习。我们将一个长宽都为100 的矩形在 (200, 200) 位置逆时针旋转45°
绕任意点旋转,可以先将该点移动到坐标原点,然后按照坐标原点的进行旋转的公式进行计算,最后再将坐标点平移回原来的位置。这个过程产生3个变换矩阵

平移矩阵
[ 1 0 0 0 1 0 − C x − C y 1 ] \begin{bmatrix}1 & 0 & 0 \\ 0 & 1 & 0 \\ -C_x & -C_y & 1\end{bmatrix} 10Cx01Cy001

旋转矩阵
[ c o s θ s i n θ 0 − s i n θ c o s θ 0 0 0 1 ] \begin{bmatrix}cos\theta & sin\theta & 0 \\ -sin\theta & cos\theta & 0 \\ 0 & 0 & 1\end{bmatrix} cosθsinθ0sinθcosθ0001

平移矩阵
[ 1 0 0 0 1 0 C x C y 1 ] \begin{bmatrix}1 & 0 & 0 \\ 0 & 1 & 0 \\ C_x & C_y & 1\end{bmatrix} 10Cx01Cy001

我们将这三个矩阵相乘
[ 1 0 0 0 1 0 − C x − C y 1 ] ∗ [ c o s θ s i n θ 0 − s i n θ c o s θ 0 0 0 1 ] ∗ [ 1 0 0 0 1 0 C x C y 1 ] \begin{bmatrix}1 & 0 & 0 \\ 0 & 1 & 0 \\ -C_x & -C_y & 1\end{bmatrix} * \begin{bmatrix}cos\theta & sin\theta & 0 \\ -sin\theta & cos\theta & 0 \\ 0 & 0 & 1\end{bmatrix} * \begin{bmatrix}1 & 0 & 0 \\ 0 & 1 & 0 \\ C_x & C_y & 1\end{bmatrix} 10Cx01Cy001 cosθsinθ0sinθcosθ0001 10Cx01Cy001

最终得到这样一个矩阵
[ c o s θ s i n θ 0 − s i n θ c o s θ 0 C x − C x c o s θ + C y s i n θ C y − C x s i n θ − C y c o s θ 1 ] \begin{bmatrix}cos\theta & sin\theta & 0 \\ -sin\theta & cos\theta & 0 \\ C_x - C_xcos\theta+C_ysin\theta & C_y-C_xsin\theta-C_ycos\theta & 1\end{bmatrix} cosθsinθCxCxcosθ+CysinθsinθcosθCyCxsinθCycosθ001
因此这里可以这样写

3 0 obj     % 页面内容流
<< >>
stream      % 流的开始
200 200 100 100 re S %原始矩形
0.7 0.7 -0.7 0.7 200 -80 cm%进行坐标变换
200 200 100 100 re S %变换后的矩形
endstream   % 流结束
endobj

这样我们可以得到如下所示的图形
在这里插入图片描述
这个时候我们会发现,同样是(200, 200) 的位置,在变换前和变换后,得到不一样的图形,这就说明我们的坐标系统被改变了。不再是水平和竖直方向的x y轴了。如果我们想要它变回原来的位置该怎么办?

在GDI或者其他框架的图形编程中,在改变画笔、画刷等图形状态的时候,会首先保存原来的,然后更新,最后再还原。同样在PDF中,也存在有这样的保存和还原的操作符。我们使用q/Q这么一对操作符来完成保存和还原的操作。

我在原来的基础上,再加一个矩形,在(400, 400) 位置画一个长宽都是100的矩形

3 0 obj     % 页面内容流
<< >>
stream      % 流的开始
200 200 100 100 re S %原始矩形
0.7 0.7 -0.7 0.7 200 -80 cm%进行坐标变换
200 200 100 100 re S %变换后的矩形
400 400 100 100 re S % 这个矩形是相对于 (200, 200) 这个点旋转了45°的矩形
endstream   % 流结束
endobj

我们再采用q/Q这一对操作符来保存和还原图形状态

3 0 obj     % 页面内容流
<< >>
stream      % 流的开始
200 200 100 100 re S %原始矩形
q
0.7 0.7 -0.7 0.7 200 -80 cm%进行坐标变换
200 200 100 100 re S %变换后的矩形
Q
400 400 100 100 re S 
endstream   % 流结束
endobj

这个时候我们发现它已经在(400, 400) 这个位置画了一个矩形。没有任何的图形变换

PDF中将图形状态保存成一个栈结构,每次执行q就是将当前图形状态进行入栈,使用Q将之前保存在栈顶的图形状态进行出栈,并还原成当前图形状态。一般来说q/Q必须成对出现。
好了,本节到这里就结束了。本节主要介绍了图形变换矩阵以及PDF中变换矩阵的操作符cm以及q/Q 这一对保存和还原图形状态的操作符

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1452134.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

如果给电商系统颜值搞排名,我觉得淘宝千牛系统是天花板了。

淘宝的商家操作界面-千牛系统经过多年的迭代&#xff0c;无论从颜值上、功能上还是用户体验上都是行业天花板的存在&#xff0c;我截图软件上的一些图给大家分享下。

C++ //CCF-CSP计算机软件能力认证 202406-1 矩阵重塑(其一)

CCF-CSP计算机软件能力认证 202406-1 矩阵重塑&#xff08;其一&#xff09; 题目背景 矩阵&#xff08;二维&#xff09;的重塑&#xff08;reshape&#xff09;操作是指改变矩阵的行数和列数&#xff0c;同时保持矩阵中元素的总数不变。 题目描述 矩阵的重塑操作可以具体…

秋招突击——第三弹——Java的SSN框架快速入门——SpringMVC

文章目录 说明SpringMVC简介入门案例入门案例的工作流程配置环境的问题步骤总结注释学习开发模式Severlet容器类配置入门案例工作流程分析Bean加载控制 Postman工具请求 请求和响应请求映射路径请求参数普通参数设置过滤器&#xff0c;对输入进行处理 Json数据传递参数注释学习…

eclipse中报出Invaild project path

一、问题&#xff1a; 二、提示分析&#xff1a; 资源路径位置类型无效的项目路径&#xff1a;找到重复的路径条目 项目路径中有一个名为"/eclipse_rtt_demo"的条目被多次定义&#xff0c;这在Eclipse的构建路径设置中是不允许的。这个重复的条目既被标记为系统包含…

利用免费的可视化组件,零代码制作一个电商销量大屏居然这么简单!

每到一年一度的618和双十一时&#xff0c;由于各种平台的优惠力度&#xff0c;使人们纷纷清空购物车下单&#xff0c;而在这庞大的销售数据下&#xff0c;各大商家却能够在第一时间发布整体销售业绩和数额&#xff0c;在这高效且巨大的数据背后&#xff0c;你是否有了解过展示数…

大模型系列:Prompt提示工程常用技巧和实践

前言 Prompt提示语是使用大模型解决实际问题的最直接的方式&#xff0c;本篇介绍Prompt提示工程常用的技巧&#xff0c;包括Zero-Shot、Few-Shot、CoT思维链、Least-to-Most任务分解。 内容摘要 Prompt提示工程简述Prompt的一般结构介绍零样本提示Zero-Shot少样本提示Few-Sho…

Java——面向对象进阶(三)

前言&#xff1a; 抽象类&#xff0c;接口&#xff0c;内部类 文章目录 一、抽象类1.1 抽象方法1.2 抽象类1.3 抽象类的使用 二、 接口2.1 接口的定义和实现2.2 default 关键字2.3 实现接口时遇到的问题 三、内部类3.1 成员内部类3.2 静态内部类3.3 成员内部类3.4 匿名内部类&a…

Docker 镜像库国内加速的几种方法

在国内&#xff0c;拉取 Docker 镜像速度慢 / 时不时断线 / 无账号导致限流等&#xff0c;比较痛苦。 这里提供几个当前可用的镜像仓库&#xff0c;更新到/etc/docker/daemon.json即可。 更新完记得运行&#xff1a; sudo systemctl daemon-reload sudo systemctl restart …

【AI绘画】教你一个完美的文生图方法,简单易学好上手,新手也能轻松掌握Stable Diffusion使用!

当我们还在思索&#xff0c; AI&#xff08;人工智能&#xff09;是否能替代人类的地位&#xff0c; 它已悄然无声&#xff0c; 融入我们生活的点滴细节。 在艺术创作领域&#xff0c; AI技术作为核心力量&#xff0c;展现其无尽的魅力。 AI换脸、AI影像&#xff0c;AI角色、A…

自学C语言-9

** 第9章 函数 ** 大型程序一般会被分为若干个程序模块&#xff0c;每个模块实现一个特定功能 。C语言中&#xff0c;由函数实现子程序&#xff0c;由子程序实现模块功能。本章致力于使读者了解函数的概念&#xff0c;掌握函数的定义及调用方式&#xff1b;了解内部函数和外部…

基于51单片机太阳能热水器设计

基于51单片机太阳能热水器 &#xff08;仿真&#xff0b;程序&#xff09; 功能介绍 具体功能&#xff1a; 1.LCD1602显示屏第一行显示温度&#xff0c;第二行显示温度下限&#xff1b; 2.按键可以设置温度的下限&#xff0c;控制出水&#xff1b; 3.当温度低于设置下限值…

使用Redis将单机登录改为分布式登录

使用Redis将单机登录改为分布式登录 1. 背景 ​ 现在大多数的应用程序登录的方式都是必须满足分布式登录的效果&#xff0c;比如我们在一个客户端登录之后可以在另一个客户端上面共享当前用户的信息&#xff0c;这样在另一个客户端登录的时候就不用用户再次输入自己的账号密码…

昂科烧录器支持Prolific旺玖科技的电力监控芯片PL7413C1FIG

芯片烧录行业领导者-昂科技术近日发布最新的烧录软件更新及新增支持的芯片型号列表&#xff0c;其中Prolific旺玖科技的高度集成的电力监控芯片PL7413C1FIG已经被昂科的通用烧录平台AP8000所支持。 PL7413C1FIG是一款高度集成的电力监控芯片&#xff0c;用于测量电力使用情况的…

【C语言】13.数组指针与函数指针及其应用

一、数组指针 顾名思义&#xff0c;数组指针就是指向数组的指针。形如&#xff1a;int (*p)[10]; 注意&#xff1a;[]的优先级要高于*号的&#xff0c;所以必须加上&#xff08;&#xff09;来保证p先和*结合。 数组指针的使用 int arr[10] {0}; int (*parr)[10] &arr;…

【因果推断python】34_合成控制4

目录 进行推理 关键思想 进行推理 由于我们的样本量非常小&#xff08;39&#xff09;&#xff0c;因此在确定我们的结果是否具有统计学意义时&#xff0c;我们必须更加聪明&#xff0c;而不仅仅是由于随机运气。在这里&#xff0c;我们将使用 Fisher 精确检验的思想。它的直…

【机器学习】LightGBM: 优化机器学习的高效梯度提升决策树

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 LightGBM: 优化机器学习的高效梯度提升决策树引言一、LightGBM概览二、核心技术…

腾讯大模型最新路线图,主打一个“实用”

让大模型更接地气&#xff0c;腾讯交出一份阶段性答卷。 大模型狂奔一年多后&#xff0c;正迈向产业落地的深水区。如何用好这一革命性的新技术&#xff0c;已成为整个社会的一道必答题。 去年&#xff0c;国内大模型百花齐放&#xff0c;外界关注的焦点也集中在模型能力上。…

深入理解ReentrantLock

深入理解ReentrantLock 在Java并发编程中&#xff0c;锁&#xff08;Lock&#xff09;是控制多个线程对共享资源访问的重要工具。虽然Synchronized关键字是实现锁的常用方式&#xff0c;但它在功能上比较有限。ReentrantLock是java.util.concurrent.locks包中提供的一个更加灵…

FreeRTOS移植:STM32L476 nucleo-L476RG 开发板《02》

系列文章 FreeRTOS移植&#xff1a;STM32L476 nucleo-L476RG 开发板《01》 说明 上一篇 FreeRTOS移植&#xff1a;STM32L476 nucleo-L476RG 开发板《01》 主要讲了一下如何快速搭建一个 STM32 裸机工程&#xff0c;其实 STM32CubeMX 可以生成 FreeRTOS 的工程&#xff0c;这就…

C语言小例程20/100

题目&#xff1a;一个数如果恰好等于它的因子之和&#xff0c;这个数就称为"完数"。例如61&#xff0b;2&#xff0b;3.编程找出1000以内的所有完数。 #include<stdio.h> #define N 1000 int main() {int i,j,k,n,sum;int a[256];for(i2;i<N;i){suma[0]1;k…