其跨平台特性可以讓文件在任何系統中以相同的表現顯示
另外 PDF 是不能被修改的文件,方便確保文件的統一性
但 PDF 真的不能被修改嗎 ?
事實上 PDF 的結構非常複雜
不要說用純文字方式製作 PDF ,連純文字方式閱讀 PDF 都非常困難
以下是一個 PDF 的基本結構
%PDF-1.7 1 0 obj << /Type /Catalog /PageLayout /SinglePage /PageMode /UseThumbs /Outlines 8 0 R /OpenAction [ 3 0 R /XYZ 0 0 0 ] /Pages 2 0 R >> endobj 2 0 obj << /Type /Pages /Kids [ 3 0 R 4 0 R 5 0 R ] /Count 3 /Resources << /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /Font << /F6 6 0 R >> >> >> endobj 3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [ 0.00 0.00 100.00 100.00 ] /Contents [ 7 0 R ] >> endobj 4 0 obj << /Type /Page /Parent 2 0 R /MediaBox [ 0.00 0.00 100.00 100.00 ] /Contents [ 7 0 R ] >> endobj 5 0 obj << /Type /Page /Parent 2 0 R /MediaBox [ 0.00 0.00 100.00 100.00 ] /Contents [ 7 0 R ] >> endobj 6 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /UniCNS-UTF16-H >> endobj 7 0 obj << >> stream BT 0.00 0.00 0.00 rg 10.00 50.00 TD /F6 16.00 Tf (hello, world) Tj ET endstream endobj 8 0 obj << /Type /Outlines /First 9 0 R /Last 11 0 R /Count 3 >> endobj 9 0 obj << /Title (Page 1) /Parent 8 0 R /Next 10 0 R /Dest [ 3 0 R /XYZ 0 0 0 ] >> endobj 10 0 obj << /Title (Page 2) /Parent 8 0 R /Next 11 0 R /Dest [ 4 0 R /XYZ 0 0 0 ] >> endobj 11 0 obj << /Title (Page 3) /Parent 8 0 R /Dest [ 5 0 R /XYZ 0 0 0 ] >> endobj trailer << /Root 1 0 R /Info << /Title (Title) /Subject (Subject) /Author (Author) /Keywords (Keywords) /Creator (Creator) /Producer (Producer) /CreationDate (D:19700101000000+08'00') /ModDate (D:19700101000000+08'00') >> >> %%EOF
PDF 的標頭必定是以 %PDF-1.7 以 %%EOF 結束
由於現在最新的 PDF 版本是 1.7 所以以 %PDF-1.7 作標頭
一般由程式或軟件所創建的 PDF 的內容通常都被加密或壓縮,因此不能單純用純文字軟件閱讀
PDF 由一種類似物件導向的結構所組成
除了 trailer 每個物件都會以
n 0 obj << /Attribute value >> endobj的寫法所創建
n 是像 id 一樣的數值,由 1 開始的正整數,不應該重覆使用
Attribute 是物件的屬性資料
/Attribute 是左邊有一個 / 號,是使用由 PDF 定義資料的專有資料
value 是對應 Attribute 的參數
value 可以是專有、字串、16進制資料
字串是使用 () 所包著的文字,但字串只能顯示 ascii 字元,即由 0 至 255 的字元
超過 ascii 255 的字元必須配合字體及使用16進制或其他壓縮方法才能正常顯示文字
16進制是使用 <> 所包著的16進制資料,資料以每4個位為一個文字資料,即 0000~ffff 例如 <0041> 是「A」
在可編輯的概念下,這種使用16進制方式所使用的 PDF 是能夠只靠純文字製作
在例子中沒有提及的,「%」是 PDF 文件的註譯字元,「%」不一定放在最左,寫下「%」後右邊所以字元都是註譯
PDF 物件每個有特殊用途的集合都會在 /Type 中指定其用法
在例子中見到 /Catalog /Pages /Page /Font 當然這些都只是眾多 PDF 物件的部分類型但已經足夠構成一個 PDF 的元件
例子可以將 PDF 結構畫成以下樣式
trailer /Info /Title /Subject /Author /Keywords /Creator /Producer /CreationDate /ModDate /Root /Catalog /PageLayout /PageMode /Outlines /Outlines outline entries /OpenAction /Pages /Pages /Count /Kids /Page /Parent /MediaBox /Contents stream /Resources /Font /Subtype /BaseFont /Encodingtrailer 是一種特殊的 PDF 元件, trailer 沒有 id
trailer 是整合所有 PDF 元件的元件,它類似擔當於 XML 的 root element 角色
trailer 所指示的主要是 /Root ,例子中還有 /Info
先介紹 /Info
/Info 是 PDF 文件的類似 metadata 的資料,當中包括
/Author (作者), /Creator (創建者), /Keywords (關鍵字), /Producer (製作器), /Subject (標題), /Title (主旨),
/CreationDate (製作日期), /ModDate (修改日期)
/Author , /Creator , /Keywords , /Producer , /Subject , /Title
可以使用 () 直接編寫 ascii 資料,若果資料需要使用「(」、「)」便以「\」跳脫成一般字元,即「\(」、「\)」
亦可以使用 <> 編寫16進制資料,使用16進制資料必須以FEFF為前綴,以每4個字元一組,例如以16進制顯示「A」,即「<FEFF0041>」
(FEFF 就是 UTF-16 Big Endian 的 BOM)
而 /CreationDate , /ModDate 必須使用 (D:YYYYMMDDhhmmss+hh'mm')
D: 是定義日期時間格式 YYYYMMDDhhmmss 是對應 YYYY (年), MM (月), DD (日), hh (時), mm (分), ss (秒) ,不足位值需要補零
而 +hh'mm' 是 UTC 時間差,例如香港是 UTC+8 ,因此資料可以設定為 +08'00'
/Root 是指向 /Catalog 元件或直接編寫 /Catalog 元件於 /Root 之中
正式開始製作 PDF 元件
首先製作 /Catalog
1 0 obj << /Type /Catalog /PageLayout /OneColumn /PageMode /UseThumbs /OpenAction [ 3 0 R /XYZ 0 0 1 ] >> endobj所有指定功能的 PDF 元件都必須在 /Type 中指定,例如 /Type /Catalog
指定不同的 /Type 後,才能使用相對元件的屬性
例子中的 /Catalog 使用了 /PageLayout , /PageMode , /OpenAction
/PageLayout 是顯示文件的資料樣式,分別有:
/SinglePage (單頁,預設) | /TwoPageLeft (雙頁) | /TwoPageRight (雙頁,先顯示單頁) |
/OneColumn (連續單頁) | /TwoColumnLeft (連續雙頁) | /TwoColumnRight (連續雙頁,先顯示單頁) |
/PageMode 是顯示文件的頁面模式,分別有:
/UseNone 不使用(預設) | /UseOutlines 使用書籤,需要建立 Outlines | /UseThumbs 使用頁面 |
/FullScreen 全螢幕(第一次使用會詢問設定) | /UseOC 使用圖層(較少使用) | /UseAttachments 使用附件(較少使用) |
/OpenAction 是開啟文件時自動運行一次的操作,分別有:
[ n 0 R /XYZ 0 0 1 ] 跳至指定頁面,並以100%顯示 | [ n 0 R /Fit ] 跳至指定頁面,並符合頁面顯示 |
[ n 0 R /FitH 0 ] 跳至指定頁面,並符合闊度顯示 | [ n 0 R /FitV 0 ] 跳至指定頁面,並符合高度顯示 |
/Outlines 是書籤的設定
若果沒冇設定 /Outlines , /PageMode 設定為 /UseOutlines 則無效
/Pages 的 /Kids 指向各 /Page 的位置
[] 是 PDF 的 array 元件,所有 /Page 元件,而 /Count 則是載入 /Page 的數量
一般情況 /Kids 的 /Page 數量應與 /Count 的數值相同
但 PDF 載入 Page 是根據 /Count 所給予
即是假如 /Count 的數值大於 /Kids 的 /Page 數量,會因為不能載入有效的 /Page 而出錯
反之則不會出錯,但會不能載入所有 /Page
/Count 大於 /Kids 載入 /Page 數量的錯誤畫面
/Page 就是打開 PDF 文件時可見的畫面,在 PDF 元件中, /Page 類似畫板 (Canvas) 的一般的元件
/Page 必須設定 /Parent 來重新導自身的父項目,但若果 /Page 是直接嵌入至 /Pages 則不用重新導向
透過 /MediaBox 設定 /Page 的大小,格式為 [ x1 y1 x2 y2 ] ,一般會當成 [ 0 0 width height ] 方式設定
值得留意的是 PDF 是由左下角為 (0, 0) 座標,製作上必須由左下至右上設計,類似幾何學 CAST 的 A位置
而事實上由於使用座標值設定,是允許使用負數值
在下測試時,嘗試比較 [ 0 0 595 842 ] 及 [ 0 0 595 -842 ] 的設計
覺得 y2 使用負數值會比較形容掌握由上至下的排列
但之後要在對應的 /Page 上的資料,所有涉及 y 座標的 stream 都必須改成負數
/Contents 就是在 /Page 上的資料,即 stream 元件
由於 /Contents 是接受 [] ,所以可以載入大量 stream 元件
例子中
7 0 obj << >> stream BT 0.00 0.00 0.00 rg 10.00 50.00 TD /F6 16.00 Tf (hello, world) Tj ET endstream endobj其實是在下太過懶,沒有在 << >> 加入設定,依靠 PDF 本身的修正功能
BT 至 ET 之間是設定文字資料
0.00 0.00 0.00 rg 是設定非邊界顏色,3組數值分別是 R G B ,數值為 0~1 ,而不是 0~255
10.00 50.00 TD 是設定相對 /Page 的列印位置, 2組數值分別是 x y ,數值不限,座標以文字框左下角為 (0,0) 開始
/F6 16.00 Tf 是設定列印的文字使用,對應 /F6 的字型設定,然後是設定字型大小,大小以 px 為基礎而不是 pt
(hello, world) Tj 是列印的文字,與先前提及設定 /Author 相對,以 () 設定的文字為 ascii 字元
但列印16進制字元則不是單純以 <FEFF> 設定,列印非 ascii 字元往後一點才介紹
/Resources 是預先定義的資料,例如 /Font , /Image 之類
6 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /UniCNS-UTF16-H >> endobjPDF 中有 14種已定義的 /BaseFont 分別有:
/Courier , /Courier-Bold , /Courier-Oblique , /Courier-BoldOblique ,
/Helvetica , /Helvetica-Bold , /Helvetica-Oblique , /Helvetica-BoldOblique ,
/Times-Roman , /Times-Bold , /Times-Italic , /Times-BoldItalic ,
/Symbol , /ZapfDingbats
超過 16種已定義的 /Encoding 分別有:
/UniGB-UCS2-H , /UniGB-UCS2-V , /UniGB-UTF16-H , /UniGB-UTF16-V ,
/UniCNS-UCS2-H , /UniCNS-UCS2-V , /UniCNS-UTF16-H , /UniCNS-UTF16-V ,
/UniJIS-UCS2-H , /UniJIS-UCS2-V , /UniJIS-UTF16-H , /UniJIS-UTF16-V ,
/UniKS-UCS2-H , /UniKS-UCS2-V , /UniKS-UTF16-H , /UniKS-UTF16-V
在下只列具部分 Unicode 編碼
使用這些 Unicode 編碼使用可以在 stream 列印文字時使用 <> 16進制寫法 (不需要使用 FEFF 前綴)
但這14種預設的 /BaseFont 並不能編制非 Unicode 字元
若果需要編制非 Unicode 例如繁體中文字,需要自行編制
以下是在下根據 MSungStd Light Acro 所編制的自訂字型
n 0 obj << /Type /Font /Subtype /Type0 /BaseFont /MSungStd-Light-Acro /Encoding /UniCNS-UTF16-H /DescendantFonts [ << /Type /Font /Subtype /CIDFontType0 /BaseFont /MSungStd-Light-Acro /DW 1000.00 /W [ 1 [ 250.00 ] 2 [ 250.00 ] 3 [ 408.00 ] 4 [ 668.00 ] 5 [ 490.00 ] 6 [ 875.00 ] 7 [ 698.00 ] 8 [ 250.00 ] 9 [ 240.00 ] 10 [ 240.00 ] 11 [ 417.00 ] 12 [ 667.00 ] 13 [ 250.00 ] 14 [ 313.00 ] 15 [ 250.00 ] 16 [ 520.00 ] 17 [ 500.00 ] 18 [ 500.00 ] 19 [ 500.00 ] 20 [ 500.00 ] 21 [ 500.00 ] 22 [ 500.00 ] 23 [ 500.00 ] 24 [ 500.00 ] 25 [ 500.00 ] 26 [ 500.00 ] 27 [ 250.00 ] 28 [ 250.00 ] 29 [ 667.00 ] 30 [ 667.00 ] 31 [ 667.00 ] 32 [ 396.00 ] 33 [ 921.00 ] 34 [ 677.00 ] 35 [ 615.00 ] 36 [ 719.00 ] 37 [ 760.00 ] 38 [ 625.00 ] 39 [ 552.00 ] 40 [ 771.00 ] 41 [ 802.00 ] 42 [ 354.00 ] 43 [ 354.00 ] 44 [ 781.00 ] 45 [ 604.00 ] 46 [ 927.00 ] 47 [ 750.00 ] 48 [ 823.00 ] 49 [ 563.00 ] 50 [ 823.00 ] 51 [ 729.00 ] 52 [ 542.00 ] 53 [ 698.00 ] 54 [ 771.00 ] 55 [ 729.00 ] 56 [ 948.00 ] 57 [ 771.00 ] 58 [ 677.00 ] 59 [ 635.00 ] 60 [ 344.00 ] 61 [ 520.00 ] 62 [ 344.00 ] 63 [ 469.00 ] 64 [ 500.00 ] 65 [ 250.00 ] 66 [ 469.00 ] 67 [ 521.00 ] 68 [ 427.00 ] 69 [ 521.00 ] 70 [ 438.00 ] 71 [ 271.00 ] 72 [ 469.00 ] 73 [ 531.00 ] 74 [ 250.00 ] 75 [ 250.00 ] 76 [ 458.00 ] 77 [ 240.00 ] 78 [ 802.00 ] 79 [ 531.00 ] 80 [ 500.00 ] 81 [ 521.00 ] 82 [ 521.00 ] 83 [ 365.00 ] 84 [ 333.00 ] 85 [ 292.00 ] 86 [ 521.00 ] 87 [ 458.00 ] 88 [ 677.00 ] 89 [ 479.00 ] 90 [ 458.00 ] 91 [ 427.00 ] 92 [ 480.00 ] 93 [ 496.00 ] 94 [ 480.00 ] 95 [ 667.00 ] ] /CIDSystemInfo << /Registry (Adobe) /Ordering (CNS1) >> /FontDescriptor << /Type /FontDescriptor /FontName /MSungStd-Light-Acro /FontBBox [ -160.00 -249.00 1015.00 1071.00 ] /ItalicAngle 0.00 >> >> ] >> endobj其實當中的設定在下並不完全理解,大部分資料都是複製而來
能理解有
第一個 /Subtype 來自 PDF 定義的字型會使用 /Type1 ,而自訂的字型會使用 /Type0
/DW 為設定此字型的文字的預設闊度,一般 2-byte 字元的闊度為 1000
/W 為設定此字型的對應 Unicode 文字的闊度,以 1 開始,105鍵盤上可見字元分別為
|
|
|
|
|
|
|
|
| |
|
|
|
|
|
| ||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| ||||
|
|
|
|
|
| ||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| ||||
|
|
|
|
/W 是使用 [] 載入每個字元或群組的闊度,設定有兩種方法
1 [ 250.00 250.00 250.00 250.00 250.00 ]這種方法是指「由 1 開始的 5 個字元分別設定為 250.00, 250.00, 250.00, 250.00, 250.00 」
1 5 250.00另一種方法是指「第 1 至第 5 個字元都設定為 250.00 」
所有在 /W 中沒有設定的字元都以 /DW 的設定值定義闊度
(在下的寫法,每次只對應一個字元進行設定,是非常浪費時間)
嵌入的字型可以在「內容」>「字型」中找到
另外 MSungStd Light Acro 支援粗體及斜體不需要再定義
只需要複製原本的 MSungStd Light Acro 字型
並在 /BaseFont 及 /FontName 加上「,Bold」、「,BoldItalic」、「,Italic」,即:
... /MSungStd-Light-Acro,Bold ... /MSungStd-Light-Acro,BoldItalic ... /MSungStd-Light-Acro,Italic ...便可以設定 MSungStd Light Acro 的粗體、斜體及粗斜體字型
了解基本後,便是編製文件內文
PDF 文件中的內文,包括文字、圖形,都以 stream 保存
n 0 R << >> stream % stream data endstream endobjstream 至 endstream 之間的資料就是 PDF 文件的內文
例子的
BT 0.00 0.00 0.00 rg 10.00 50.00 TD /F6 16.00 Tf (hello, world) Tj ET是其中一種 stream 資料,以 BT (Begin Text) 至 ET (End Text) 定義為文字 stream
0.00 0.00 0.00 rgrg 為設定非邊線顏色,三組 0.00 分別為 RGB 數值,數值為 0~1 ,而非 0~255
10.00 50.00 TDTD (Text Destination) 為設定文字左下角方塊座目標, 10.00 為 x 座標, 50.00 為 y 座標
/F6 16.00 TfTf (Text font) 為設定文字的字型及大小, /F6 為字型於 PDF 文件內的匿稱, 16.00 為文字大小,以 px 計算
(hello, world) TjTj (Text justify) 為將會列印於 PDF 上的文字資料
除了文字還有圖形
線
q 0.50 w 2 J 0 j 0.00 0.00 0.00 RG 10.00 20.00 m 30.00 40.00 l S Qq 至 Q 定義為圖形 stream
0.50 ww 為設定線闊度,以 px 計算
2 JJ 為設定線末端樣式,樣式有 3 種:
0 方末端 | 1 圓末端 | 2 方末端補充邊沿 |
0 jj 為設定線轉角樣式,樣式有 3 種:
0 自動調節角度 | 1 圓轉角 | 2 將轉角切去 |
0.00 0.00 0.00 RGRG 為設定邊線顏色,三組 0.00 分別為 RGB 數值,數值為 0~1 ,而非 0~255
10.00 20.00 m 30.00 40.00 l Sm 為設定起始座標, 10.00 為 x 座標, 20.00 為 y 座標
l 為設定往後座標, 30.00 為 x 座標, 40.00 為 y 座標
(可以繼續添加 l 為設定)
S 為提交剛才所設定的資料,以線方式列印在文件上
既然繼續添加 l 即是可以列印多邊形,要自動連接起點與終點便需加上 h
10.00 20.00 m 30.00 40.00 l 50.00 60.00 l h S另外 s 等價於 h S ,即可以使用
10.00 20.00 m 30.00 40.00 l 50.00 60.00 l s來達到相同效果
S | h S 或 s | f |
矩形
q 0.50 w 2 J 0 j 0.00 0.00 0.00 RG 20.00 10.00 40.00 30.00 re f Q
20.00 10.00 40.00 30.00 rere 為設定矩形座標及闊高,以左下角為中心 20.00 為 x 座標, 10.00 為 y 座標, 40.00 為闊度, 30.00 為高度
雖然稱為「闊高」但實際的定義是與 x 及 y 座標的偏移位置
圖像
q 32.00 0.00 0.00 32.00 48.00 48.00 cm BI /W 128.00 /H 128.00 /CS /RGB /BPC 8 /F [ /AHx ] ID ffffff ffffff ffffff ffffff ...... . . . > EI Qcm 為設定圖像的縮放大小及座標
32.00 0.00 0.00 32.00 為設定圖像在 PDF 文件中的縮放大小,一般會以 width 0 0 height 的寫法
48.00 48.00 為設定圖像在 PDF 文件中的位置
先前提若將 PDF 文件的的 y 座標設定為以負值設定
例子中則需要設定為 32.00 0.00 0.00 32.00 48.00 -48.00 cm
BI 即 Being Image 的縮寫,/W 為圖像的原闊度,/H 為圖像的原高度
/CS 為 Color Space ,/RGB 為 RGB三原色,即指定圖像的 Color Space 使用三原色繪製
/BPC 為 Bits Per Component ,可以使用 1, 2, 4, 8, 16 一般會使用 8
/F 為 Filter ,array 中載入的是以 array 的方式解析 ID (即 Image Data) 的資料
/AHx 為以 RRGGBB 的 16進制方式解析 ID 但不包含 alpha channel
除了 /AHx (ASCIIHexDecode) 還有
/A85 (ASCII85Decode), /LZW (LZWDecode), /Fl (FlateDecode), /RL (RunLengthDecode), /CCF (CCITTFaxDecode), /DCT (DCTDecode)
在下的例子中由於強調使用純文字製作,因為只有 /AHx 及 /A85 適合
其中 /A85 使用 Ascii Base-85 方式壓縮,仍能使用純文字建立但需要特定功能輔助
而 /AHx 則為沒有壓縮的 RGB 結構,只需要以點陣圖形式拆解即可,但相對體積卻是最大
普遍由軟件插圖像至 PDF 中都使用 /Fl 以 gzip 方式壓縮
而
ffffff ffffff ffffff ffffff ......
.
.
.
>
就是以 RGB 方式展示的 Image Data
事實上只要指定 /AHx 方式為 Filter ,Image Data 中的 [空格] 、[換行]、[製表符] 都不會理會
在下在每組 RGB 中插入空格只是方便觀看
當 Image Data 製作完成後,在最後加上「>」來終結 Image Data
再以 EI 即 End Image 終結整個圖像處理
實際上 cm 的首 4 個參數是能夠控制圖像的轉旋
width 0 0 height (0 度,原圖角度) | 0 -height width 0 (90 度) |
-width 0 0 -height (180 度) | 0 height -width 0 (270 度) |
第1, 2個 (x1,y1) 參數 為 圖像 右下角 為 定位點 偏移值
第3, 4個 (x2,y2) 參數 為 圖像 左上角 為 定位點 偏移值
透過三角幾何便可以計算不同角度的偏移值,例如 45度
degrees = 45 x1 = width * cos(degrees) y1 = -height * sin(degrees) x2 = width * sin(degrees) y2 = height * cos(degrees)
45 度 | 135 度 |
225 度 | 315 度 |
更多詳細資料請等待在下能將 1300多頁的 PDF 結構文件讀熟再向閣下分享
沒有留言 :
張貼留言