PPDF123
Formatsอ่านประมาณ 3 นาที

การแก้ไขข้อความใน PDF ทำงานอย่างไร: content stream, การเข้ารหัสฟอนต์ และเหตุผลที่แก้ได้ทีละบรรทัด

หน้า PDF ไม่มีย่อหน้าหรือกล่องข้อความ มีเพียงคำสั่งให้วาด glyph ด้วยฟอนต์หนึ่ง ณ ตำแหน่งหนึ่ง การแก้ไขหนึ่งบรรทัดคือการลบคำสั่งเดิมแล้วเขียนคำสั่งใหม่ที่ตำแหน่งเดิม และสิ่งนี้เองกำหนดว่าอะไรแก้ได้และอะไรแก้ไม่ได้

PDF123 · Updated 2026-10-14
การแก้ไขข้อความใน PDF ทำงานอย่างไร: content stream, การเข้ารหัสฟอนต์ และเหตุผลที่แก้ได้ทีละบรรทัด

หน้า PDF ไม่มีย่อหน้าและไม่มีกล่องข้อความ มีเพียงรายการคำสั่ง คือ ณ ตำแหน่งนี้ ด้วยฟอนต์และขนาดนี้ ให้วาด glyph เหล่านี้ การเปลี่ยนข้อความคือการลบคำสั่งที่วาดบรรทัดนั้น แล้วเขียนคำสั่งใหม่ที่ตำแหน่งเดิม ด้วยฟอนต์ ขนาด และสีเดิม นี่คือเหตุผลที่ แก้ไข PDF แก้ได้ทีละบรรทัดและขึ้นบรรทัดใหม่ให้อัตโนมัติไม่ได้ ซึ่งเป็นผลโดยตรงจากวิธีที่ข้อความถูกเก็บ

ข้อความหนึ่งบรรทัดในไฟล์หน้าตาเป็นอย่างไร

เนื้อหาของหน้าอยู่ใน content stream ซึ่งเป็นโปรแกรมเล็ก ๆ ที่ประกอบด้วยตัวดำเนินการ สำหรับข้อความ ตัวดำเนินการสี่ตัวทำงานหลัก คือ BT และ ET ครอบบล็อกข้อความ Tf เลือกฟอนต์และขนาด Td ย้ายไปตำแหน่งถัดไป และ Tj วาดสตริง กรณีที่ง่ายที่สุดกับฟอนต์มาตรฐานมีหน้าตาแบบนี้

BT /F1 18 Tf 20 150 Td (Hello World) Tj ET

หมายความว่า เลือกฟอนต์ F1 ขนาด 18 พอยต์ ย้ายไปที่ x 20, y 150 แล้ววาด Hello World PDF ที่พิมพ์จาก Chrome มักไม่เหมือนนี้ วงเล็บไม่ได้ใส่ตัวอักษรอีกต่อไป แต่ใส่หมายเลข glyph ของฟอนต์ และ Tm กำหนดตำแหน่งครั้งเดียว ด้านล่างคือบรรทัด Hello World from Georgia จากหน้าที่ Chrome ส่งออก โดยเลขฐานสิบหกทุกสี่หลักคือหมายเลข glyph หนึ่งตัว

BT /F4 24 Tf 1 0 0 -1 30 52 Tm <002b0048004f004f00520003003a00520055004f0047000300490055005200500003002a004800520055004a004c0044> Tj ET

002b คือ H, 0048 คือ e, 004f คือ l, 0003 คือช่องว่าง, 003a คือ W และต่อไปเรื่อย ๆ หมายเลขเหล่านี้มีความหมายเฉพาะในฟอนต์ตัวนี้ตัวเดียว ไม่ใช่ Unicode เมื่อคุณคัดลอกข้อความ โปรแกรมดูจะแปลงหมายเลขกลับเป็นตัวอักษรด้วยตารางค้นหาที่เก็บไว้ที่อื่นในไฟล์

จากนี้มีสองข้อ ข้อแรก ไม่มีตรงไหนใน content stream ที่บอกว่า "นี่คือย่อหน้า" การขึ้นบรรทัดใหม่เป็นเพียง Td หรือ Tm อีกตัวที่ย้ายตำแหน่งไปบรรทัดถัดไป และย่อหน้าหนึ่งมีกี่บรรทัดต้องอนุมานจากตำแหน่ง ข้อที่สอง การจะเขียนตัวอักษรใหม่ลงในบรรทัด ต้องรู้หมายเลขของตัวอักษรนั้นในฟอนต์นั้น แต่ฟอนต์มักมีเฉพาะ glyph ที่ถูกใช้ตอนเขียนไฟล์ นั่นคือผลของการทำซับเซตฟอนต์

คุณขยายสตรีมได้ด้วย qpdf

qpdf --qdf --object-streams=disable in.pdf out.pdf

เปิด out.pdf ในตัวแก้ข้อความแล้วค้นหา Tj เพื่อดูทุกสตริงที่ถูกวาดบนหน้า ส่วนสิ่งอื่นที่ PDF บรรจุอยู่ ดูข้างใน PDF

ตัวแก้ไขสร้าง "บรรทัด" ขึ้นใหม่จาก glyph อย่างไร

ตัวแก้ไขต้องรวม glyph ที่กระจัดกระจายให้เป็นบรรทัดที่ผู้ใช้คลิกได้ก่อน glyph ที่ฟอนต์ ขนาด และสีเดียวกัน และห่างจาก glyph ก่อนหน้าไม่เกินหนึ่งเท่าของขนาดฟอนต์ จัดอยู่ในช่วงข้อความเดียวกัน การขึ้นบรรทัดใหม่ ฟอนต์ใหม่ หรือสีใหม่จะปิดช่วงข้อความนั้น ข้อความที่เขียนจากขวาไปซ้าย หรือเขียนแนวตั้ง จะไม่ถูกจัดกลุ่มและเปิดให้แก้ไขไม่ได้

มีผลสองอย่างที่สังเกตได้โดยตรง บรรทัดที่ผสมสองฟอนต์ เช่น บรรทัดภาษาจีนที่มีคำภาษาอังกฤษแทรก จะถูกแยกเป็นสองช่วงข้อความที่อยู่ติดกัน ซึ่งคลิกแยกกัน หลังการแก้ไขก็เป็นแบบเดียวกัน ถ้าข้อความใหม่ใช้ฟอนต์อื่น บรรทัดนั้นจะกลายเป็นสองช่วงข้อความและการแก้อันหนึ่งไม่ทำให้อีกอันเปลี่ยน

การแก้ไขหนึ่งบรรทัด: ลบข้อความเดิม แล้วเขียนข้อความใหม่

การลบใช้การทาดำแบบเฉพาะข้อความ glyph ในพื้นที่ของบรรทัดจะถูกเอาออกจาก content stream ภาพและเส้นไม่ถูกแตะ และไม่มีการวาดกล่องสีทับข้อความเดิม ดังนั้นการคัดลอกและการค้นหาจะไม่พบมันอีก วิธีนี้เปลี่ยนเฉพาะเนื้อหาของหน้า ถ้าข้อความเดียวกันปรากฏในคำอธิบายประกอบหรือในคุณสมบัติเอกสารด้วย ส่วนนั้นจะไม่ได้รับผลกระทบ

การเขียนจะต่อคำสั่งข้อความใหม่ที่ท้าย content stream ของหน้า โดยเริ่มจากตำแหน่งเส้นฐานของบรรทัดเดิมและใช้ขนาดกับสีเดิม หลังเปลี่ยนบรรทัดข้างบนเป็น Hello World 2 สิ่งที่ถูกต่อไว้ท้ายหน้าคือ

q 1 0 0 1 22.5 148.92 cm 0 0 0 rg
/Span <</ActualText <feff00480065006c006c006f00200057006f0072006c006400200032>>> BDC
BT /F4 18 Tf 0 0 Td <002b0048004f004f00520003003a00520055004f00470003> Tj ET
BT /F9 18 Tf 102.18 0 Td (2) Tj ET
EMC
Q

BT อันแรกใช้ฟอนต์เดิม F4 และหมายเลข glyph เดิม ดังนั้น Hello World จึงถูกนำกลับมาใช้เหมือนเดิมทุกประการ ตัว "2" ไม่อยู่ในซับเซตของฟอนต์เดิม BT อันที่สองจึงเริ่มฟอนต์ Helvetica มาตรฐานตัวใหม่ (F9) เพื่อวาดมัน q และ Q จำกัดการแปลงพิกัดและสีของบล็อกนี้ไว้ภายในตัวเอง ตัวแก้ไขยังครอบเนื้อหาเดิมด้วยคู่ q/Q เพื่อให้สถานะที่ยังไม่ถูกปิดคู่ตอนท้ายหน้าเดิมไม่กระทบข้อความใหม่ นี่คือหน้าตาของช่วงข้อความหนึ่ง ตัวเลขจะต่างกันไปตามไฟล์

การต่อท้ายมีสามผลตามมา

  • ข้อความใหม่ถูกวาดทีหลังสุด ทับอยู่เหนือทุกอย่างบนหน้า ข้อความที่เคยอยู่ใต้ภาพหรือบล็อกสีจะปรากฏอยู่เหนือมันหลังแก้ไข
  • เครื่องมือที่ดึงข้อความตามลำดับใน content stream (เช่น pdftotext -raw) จะอ่านบรรทัดที่แก้เป็นบรรทัดสุดท้ายของหน้านั้น ส่วนการดึงตามตำแหน่ง (pdftotext -layout) ไม่เห็นการเปลี่ยนแปลง หน้าดูเหมือนเดิม แต่ลำดับการอ่านเปลี่ยนไปแล้ว
  • ตัวแก้ไขแนบ ActualText ให้ข้อความใหม่ ซึ่งบอกโปรแกรมดูว่า glyph เหล่านั้นแทนตัวอักษรจริงตัวใด glyph หนึ่งตัวอาจตรงกับหลายตัวอักษร และการค้นกลับจากหมายเลข glyph อย่างเดียวบางครั้งได้ตัวอักษรเพื่อความเข้ากันได้ (เช่น อ่าน 文 เป็นรากอักษรคังซี ⽂) ActualText ทำให้สิ่งที่คัดลอกออกมาตรงกับที่คุณพิมพ์

ฟอนต์ใดวาดข้อความใหม่

การใช้ฟอนต์เดิมซ้ำปลอดภัยที่สุด เพราะรูปทรง glyph น้ำหนัก และความกว้างตรงกับข้อความที่อยู่ข้างกัน จะใช้ซ้ำได้หรือไม่ขึ้นกับว่าตัวแก้ไขบอกได้หรือไม่ว่าตัวอักษรมีรหัสอะไรในฟอนต์นั้น

  • ฟอนต์แบบประกอบอย่างข้างบน (Type0, หมายเลข glyph สองไบต์, การเข้ารหัส Identity-H): ค้นหมายเลข glyph ของตัวอักษรในฟอนต์ ถ้าเจอก็ใช้ ถ้าไม่เจอ แสดงว่าตัวอักษรนั้นไม่อยู่ในซับเซต
  • ฟอนต์แบบง่ายหนึ่งไบต์ (TrueType, Type1) ที่ใช้การเข้ารหัส WinAnsi หรือ MacRoman: การจับคู่จากตัวอักษรเป็นไบต์ตายตัว และความกว้างมาจากตารางความกว้างในไฟล์
  • ฟอนต์ที่กำหนดการเข้ารหัสเอง (Differences) หรือพึ่งการเข้ารหัสที่ติดมากับฟอนต์: ไม่มีการจับคู่ที่เชื่อถือได้ ตัวแก้ไขจึงไม่เดา แต่ไปใช้ฟอนต์สำรอง ไม่เช่นนั้นข้อความจะเพี้ยนเป็นอักขระผิด

เมื่อตัวอักษรไม่อยู่ในฟอนต์เดิม จะใช้ฟอนต์สำรอง ตัวอักษรละตินล้วนใช้ตัวที่ใกล้ที่สุดจาก Helvetica, Times และ Courier ซึ่งเป็นฟอนต์มาตรฐานที่โปรแกรมดูทุกตัวมี จึงไม่ต้องฝังอะไร ส่วนอื่นทั้งหมด เช่น อักษรจีน คานะ ฮันกึล ไทย ละตินที่มีเครื่องหมายกำกับ ซิริลลิก และกรีก ใช้ฟอนต์ Noto แบบโอเพนซอร์ส เซิร์ฟเวอร์รับตัวอักษรในคำขอ แล้วตัดเฉพาะ glyph เหล่านั้นออกจากฟอนต์เต็มด้วยไลบรารีทำซับเซต HarfBuzz แบบโอเพนซอร์ส ซึ่งมักมีขนาดไม่กี่ KB จากนั้นเบราว์เซอร์ฝังมันลงในไฟล์ของคุณ รูปแบบตามภูมิภาคของอักษรจีนเป็นไปตามภาษาของอินเทอร์เฟซ คือจีนตัวย่อ จีนตัวเต็ม ญี่ปุ่น หรือเกาหลี

เหตุที่ฝัง glyph แทนการเขียนเพียงชื่อฟอนต์ คือฟอนต์ที่อ้างด้วยชื่อเพียงอย่างเดียวจะถูกแทนด้วยฟอนต์สำรองที่โปรแกรมดูแต่ละตัวเลือกเอง และน้ำหนักอาจต่างกันระหว่างโปรแกรมดู เมื่อฝังแล้ว ทุกโปรแกรมดูจะแสดงรูปทรงเดียวกัน

ทำไมจึงแก้ได้ทีละบรรทัด

ตัวแก้ไขไม่มีเอนจินจัดวาง ตำแหน่งของทุก glyph ถูกกำหนดตายตัวในไฟล์ ข้อความใหม่ถูกจัดวางไปทางขวาจากจุดเริ่มด้วยความกว้างของฟอนต์เท่านั้น ไม่มีตรรกะ "ตัดบรรทัดที่ขอบ" และไม่มีสิ่งใดที่อยู่ถัดไปถูกเลื่อน ถ้าทำให้บรรทัดยาวขึ้น ตัวอักษรที่เกินจะต่อไปทางขวา และส่วนที่พ้นขอบหน้าจะมองไม่เห็น การเขียนย่อหน้าใหม่หรือเพิ่มแถวในตารางต้องจัดวางใหม่ ซึ่งหมายถึงต้องกลับไปที่ไฟล์ต้นฉบับที่ใช้สร้าง PDF

ความกว้างก็เป็นเพียงระยะเลื่อนที่ฟอนต์กำหนดให้แต่ละ glyph (ระยะที่ตำแหน่งวาดขยับไปยังตัวถัดไป) ไม่มีการปรับระยะระหว่างตัวอักษร (kerning) และไม่มีการแทน glyph ข้อความที่ไม่ต้องใช้การจัดรูปข้อความ (text shaping คือการเลือก glyph ตามบริบทและกำหนดตำแหน่ง) (ละติน ซิริลลิก กรีก และอักษรจีน) จัดวางได้ถูกต้อง วรรณยุกต์ไทยต้องมีการจัดตำแหน่ง glyph จึงจะอยู่ถูกที่ ซึ่งตัวแก้ไขไม่ทำ จึงอาจเพี้ยนตำแหน่ง ภาษาอาหรับต้องใช้รูป glyph ที่เปลี่ยนตามบริบทและการจัดวางจากขวาไปซ้าย ภาษาฮีบรูต้องใช้การจัดวางจากขวาไปซ้าย และอักษรอินดิกต้องรวมพยัญชนะกับเครื่องหมายสระเป็น glyph ทั้งหมดนี้ต้องใช้การจัดรูปข้อความเต็มรูปแบบ ซึ่งตัวแก้ไขยังไม่ทำ จึงปฏิเสธการพิมพ์ แทนที่จะสร้างตัวอักษรผิด

ยังมีข้อจำกัดอีกสองข้อที่มาจากตัวไฟล์ เนื้อหาของหน้าสแกนเป็นภาพเดียวที่ไม่มีตัวดำเนินการข้อความ จึงไม่มีอะไรให้ลบหรือเปลี่ยน หลังแก้ไขไฟล์ที่มีลายเซ็นดิจิทัล เนื้อหาเดิมที่ลายเซ็นคุ้มครองเปลี่ยนไปแล้ว และลายเซ็นจึงใช้ไม่ได้อีก

ตรวจสอบเอง

คุณเห็นการเปลี่ยนแปลงที่อธิบายข้างต้นในไฟล์ที่ดาวน์โหลดได้ด้วยสามคำสั่ง

pdftotext -raw edited.pdf -      # ดึงตามลำดับ content stream: ข้อความเดิมหายไป บรรทัดที่แก้อยู่ท้ายสุด
pdffonts edited.pdf              # รายการฟอนต์: ฟอนต์เดิมที่ถูกใช้ซ้ำ และซับเซตฟอนต์ที่ฝังใหม่
qpdf --qdf --object-streams=disable edited.pdf edited.qdf   # ขยาย content stream: บล็อก BT ... ET ที่ต่อท้ายอยู่ท้ายสุด

อ่านต่อ

Open tool
Process in the browser — no watermark, files removed after the job.
Open tool
Read next
ให้ผู้ช่วย AI ใช้เครื่องมือ PDF: เริ่มต้นกับ @pdf123/mcp และแบบในเครื่องเทียบกับแบบโฮสต์
เชื่อม @pdf123/mcp กับ Claude Code, Claude Desktop หรือ Cursor ภายในสองนาที เพื่อให้ผู้ช่วย AI รวม บีบอัด และแปลง PDF บนเครื่องของคุณผ่านพาธไฟล์ เซิร์ฟเวอร์ในเครื่องส่งต่อเฉพาะพาธ ส่วนเอนด์พอยต์ /mcp แบบโฮสต์ต้องใส่ไฟล์เป็น base64 ในอาร์กิวเมนต์ของเครื่องมือ และ PDFX_MCP_ROOT จำกัดไดเรกทอรีที่เซิร์ฟเวอร์ในเครื่องอ่านและเขียนได้
จาก Markdown เป็น PDF และกลับ: การแปลงรูปแบบเก็บอะไรไว้
Markdown↔PDF เก็บหัวเรื่อง รายการ และตารางง่ายได้ดีกว่าพิกเซล ฟอนต์เป๊ะ การแบ่งหน้า และเลย์เอาต์ไม่รอดเป็น round trip ที่ไม่สูญเสีย
ข้างใน PDF: อ็อบเจกต์ สตรีม และตาราง cross-reference
PDF คือกราฟอ็อบเจกต์มีหมายเลข สตรีมบีบอัด และแผนที่ xref ของออฟเซ็ตไบต์ Get Info ตรวจไฟล์; Repair สร้างโครงสร้างใหม่เมื่อแผนที่นั้นพัง