บล็อก

PDF metadata fields อธิบาย: title, language, author, subject, creator, producer

Walkthrough standard PDF metadata fields 6 ตัวที่ gPdf expose — title, language, author, subject, creator, producer แต่ละตัวเพื่ออะไร ใครอ่าน common mistakes และวิธี verify

คู่หูของ PDF properties ควรแสดง brand ของคุณ ไม่ใช่ tool ของคนอื่น — post นั้นโต้แย้งว่าทำไมต้องสนใจ PDF metadata อันนี้คือคู่มือการใช้งาน: แต่ละ field คืออะไรใน PDF specification ใครอ่าน common mistakes และวิธี verify ว่า output ของคุณ ship ตรงตามที่คุณตั้งใจไว้จริง

gPdf expose standard fields 6 ตัวที่ PDF spec define สำหรับ document-level metadata พวกมันอยู่ใต้ settings.metadata ใน DocumentRequest JSON ทุก field เป็น optional — ถ้าคุณไม่ set อันใด gPdf จะ fall back ไปที่ default_metadata ของ token (Enterprise policy feature) หรือ system default

{
  "settings": {
    "metadata": {
      "title":    "...",
      "language": "...",
      "author":   "...",
      "subject":  "...",
      "creator":  "...",
      "producer": "..."
    }
  }
}

ส่วนที่เหลือของ post คือหนึ่ง section ต่อ field แต่ละ section ตามรูปแบบเดียวกัน: field คืออะไร, ที่ไหนปรากฏ, common mistakes, rule of thumb ลำดับคือสิ่งที่ควรกรอกก่อน → ที่สอง → … → ท้ายสุด

title — เอกสารคืออะไร

PDF spec อธิบายเป็น “document title”

ที่ไหนปรากฏ:

  • title bar ใน PDF viewers (Adobe Reader, Preview, Foxit, Chromium PDF viewer แสดงทั้งหมด)
  • browser tab เมื่อ PDF เปิด inline (Content-Disposition: inline)
  • Search indexes — Spotlight, Outlook, SharePoint, full-text indexer ของ Google Drive อ่าน title ทั้งหมดและให้น้ำหนักหนัก

Common mistakes:

  • Set title เป็น filename invoice-20260318.pdf คือ filename title ควรเป็นสิ่งที่มนุษย์อ่าน เช่น Invoice INV-2026-3401 Filename และ title คือ concerns ที่แตกต่างกัน; filename สำหรับ filesystems, title สำหรับ viewers และ search
  • ทิ้ง title ว่าง Viewers fall back ไปที่ filename ผลลัพธ์อ่านได้ว่าเป็น auto-generated และ machine-emitted
  • เพิ่ม brand ใน title Acme Logistics — Invoice INV-2026-3401 clutter title bar brand อยู่ใน author ไม่ใช่ title

Rule of thumb: title ควรตรงกับ H1 ของหน้าที่ render หาก top line ของ template invoice คือ “Invoice INV-2026-3401” นั่นคือ title

language — สำหรับ accessibility, search และ compliance

language คือ BCP-47 language tag: en, de, zh-Hans, pt-BR, ar-SA Set สำหรับทุกเอกสาร จาก fields 6 ตัวมี downstream consequences ที่ชัดเจนที่สุดและ implementation cost น้อยที่สุด — ซึ่งเป็นเหตุผลที่อยู่ตำแหน่งที่ 2 แทนที่จะถูกฝังไว้ด้านล่าง

ที่ไหนปรากฏ:

  • Screen readers — JAWS, NVDA, VoiceOver ใช้เพื่อเลือก phoneme set ที่ถูกต้อง English screen reader อ่าน language: "de" PDF จะออกเสียงคำเยอรมันถูกต้อง; ไม่มี tag จะได้ prosody ผิด
  • Search engines และ indexers — กระทบว่า stemming และ stopword list ของ locale ไหนใช้ Invoice language: "zh-Hans" ถูก index ด้วย Chinese segmentation; tag ที่หายไปมัก default เป็น English และ index ใช้ไม่ได้
  • PDF/A compliance — PDF/A-2a และ PDF/A-3a (accessibility profiles) ต้องการ language tag ไม่มี veraPDF validation fail

Common mistakes:

  • ปล่อยไม่ set Default เป็น “locale ของผู้รับ” ไม่ใช่ “default ของ platform” leaky stack ส่วนใหญ่ไม่เขียน field; ผลลัพธ์คือ screen readers ที่ออกเสียงผิดและ search indexes ที่ stem ผิด
  • ใช้ non-BCP-47 string เช่น "english" หรือ "EN-US" PDF spec คาดหวัง RFC 5646 tags: en, en-US, de, pt-BR
  • Hard-code default ของ platform (เช่น "en" เสมอ) โดยไม่คำนึงถึงภาษา content จริงของเอกสาร Portuguese invoice ที่ tag เป็น "en" แย่กว่าเอกสารที่ไม่ tag — มันทำให้ indexer เข้าใจผิดอย่างจริงจัง

Rule of thumb: tag ควรตรงกับภาษา content จริง สำหรับลูกค้าใน Brazil ที่รับ invoice เป็นภาษา Portuguese, set "language": "pt-BR" ไม่ใช่ "en" สำหรับเอกสารหลายภาษา เลือกภาษาหลักและใช้ Lang attribute บน content elements แต่ละตัวสำหรับส่วนที่เหลือ — นั่นคือ tagged-PDF accessibility feature นอกเหนือจาก document-level language field

author — เอกสารเป็นของใคร

ใน PDF spec author คือ “the name of the person or organisation that created the document” สำหรับ business PDFs ที่ ship ไปยังผู้รับ คำตอบเกือบจะเป็น organisation เสมอ — แต่รูปร่างที่ถูกต้องแตกต่างกันจริงๆ ตาม context

ที่ไหนปรากฏ:

  • Properties dialog ใน PDF viewer ทุกตัว labeled “Author” อย่างเด่นชัด
  • DMS / archive indexers มักใช้เป็น filter
  • PDF/A XMP metadata stream ที่มันถูก carry เข้าสู่ long-term archives

Common mistakes:

  • "author": "[email protected]" — leak email ของ operator เข้าทุก PDF โดยอุบัติเหตุ จบลงใน search index ทุกตัว กลายเป็น long-term PII issue
  • "author": "PDF Generator Service" — ชื่อ internal tool; ไม่มีความหมายต่อผู้รับ
  • ❌ ว่าง — Preview และ viewer ส่วนใหญ่แสดง literally “(no author)” ใน properties dialog ซึ่งอ่านว่า “ไม่มีใครเป็นเจ้าของนี้”

Shapes ที่ใช้ได้:

  • "author": "Acme Logistics, Inc." — organisation ตรงๆ
  • "author": "Acme Logistics — Billing" — organisation + department สำหรับเอกสารที่ route ไปยัง desk เฉพาะ
  • "author": "Bridge Capital Partners — Fund III" — มีประโยชน์ใน finance/legal ที่ attribution เป็นไปยัง entity เฉพาะ
  • "author": "Maria López, RICS Surveyor" — สำหรับ single-author publishing (reports, valuations, legal opinions) ที่ individual คือ editorial attribution

Rule of thumb: author คือ entity ที่ผู้รับควร associate เอกสารด้วย ใน multi-tenant SaaS ที่ platform สร้าง PDF ในนามของ ลูกค้า author ควรเป็นชื่อ organisation ของลูกค้า ไม่ใช่ชื่อของ platform (ชื่อของ platform อยู่ใน creator — ดูด้านล่าง) สำหรับ context consultancy / publishing / legal ที่ individuals คือ brand individuals ก็ใช้ได้

subject — นี่คือเอกสารประเภทไหน

subject คือ short-description-of-the-document Viewers ไม่แสดงโดดเด่น — users ส่วนใหญ่จะไม่เห็นเว้นแต่จะเปิด Properties dialog แต่ document management systems, archive systems และ rules-based email/file routing ใช้

ที่ไหนปรากฏ:

  • Properties dialog ตำแหน่งรอง
  • DMS routing rules, archive bucketing logic
  • XMP metadata stream (PDF/A)

Common mistakes:

  • "subject": "Invoice for Acme on 2026-03-18 for $4,532.10" — นั่นคือ document-instance description ไม่ใช่ type อยู่ใน title
  • ❌ ว่าง — เสียค่า routing hook ฟรีสำหรับ downstream systems
  • ❌ ผสม classes อย่างไม่สม่ำเสมอ ("Invoice" vs "Invoice/2026-03" vs "Monthly invoice") — DMS filters ไม่สามารถ bucket บน moving target

Shapes ที่ใช้ได้:

  • "subject": "Invoice"
  • "subject": "Monthly account statement"
  • "subject": "Shipping label — 4×6 thermal"
  • "subject": "Q3 2026 board pack"

Rule of thumb: granularity ที่ถูกต้องคือ document class ไม่ใช่ document instance DMS ที่มี PDF เข้าหลายพันสามารถ route บน subject ถ้าคุณให้ vocabulary ที่สอดคล้องกัน เลือก finite set ของ classes สำหรับ platform ของคุณและไม่เคย deviate — invoice ทุกตัวที่ platform ของคุณสร้างต้องมี exactly "subject": "Invoice"

creator vs producer — คู่ที่สับสนมากที่สุด

นี่คือจุดที่ team ส่วนใหญ่หยุดอ่าน PDF spec และเดา Spec แม่นยำ; field สองตัวหมายถึงสิ่งที่ต่างกัน

  • creator — application ที่สร้าง source content (ระบบ upstream ที่ตัดสินใจว่าเอกสารควรพูดอะไร)
  • producer — application ที่สร้าง PDF bytes (rendering engine ที่เปลี่ยน content นั้นเป็น PDF file)

สำหรับ SaaS billing platform ที่สร้าง invoices ผ่าน JSON-to-PDF API เช่น gPdf:

  • creator = SaaS billing platform พร้อม version นั่นคือ application ที่ตัดสินใจว่าควรเป็น invoice สำหรับ Acme จำนวน $4,532.10
  • producer = renderer By default คือ “gPdf” แต่เนื่องจาก rendering layer เป็น infrastructure ที่ SaaS เลือก SaaS สามารถ set producer เป็นชื่อ platform ของตัวเองได้อย่างถูกต้อง — platform ของมัน ในความหมายจริง สร้าง PDF bytes โดย delegate ไปยัง gPdf เป็น infrastructure
{
  "creator":  "Acme Billing Platform v7.2",
  "producer": "Acme Billing Platform"
}

ที่ไหนปรากฏ:

  • Properties dialog ทั้งคู่ labeled
  • pdfinfo output, side by side
  • PDF/A XMP stream (ทั้งสอง fields ต้องไม่ว่างใน PDF/A)

Common mistakes:

  • creator set เป็น user-agent string ของ Chromium / Mozilla เกิดขึ้นเมื่อ headless-browser PDF stack pass User-Agent ไปยัง creator โดยอัตโนมัติ มันคือ version ของ browser ไม่ใช่ system source-of-truth Override
  • producer ทิ้งไว้เป็นชื่อ renderer default team ส่วนใหญ่ไม่เคย override ดังนั้น PDF ทุกตัวพูดว่า “Skia/PDF m120” หรือ “wkhtmltopdf” — ดู white-label post สำหรับเหตุผลที่นี่สำคัญสำหรับ B2B
  • ใส่ค่าเดียวกันในทั้งคู่ ยอมรับได้แต่สิ้นเปลือง — field สองอันมีอยู่อย่างแน่นอนเพื่อให้ viewer สามารถบอก “source app” จาก “render engine” ได้ ใช้พวกมัน

Rule of thumb: creator คือชื่อ application ของคุณ with version (เช่น "Acme Billing Platform v7.2"); producer คือ brand หรือชื่อ platform ของ application ของคุณ without version (เช่น "Acme Billing Platform") ทั้งคู่ควรเป็นค่าที่ผู้รับจะ recognise

Empty fields, per-token defaults, downstream surprises

รายละเอียดการ implement สามอย่างที่ควรรู้ก่อน ship:

  1. Empty หรือ whitespace-only strings ถูก treat เป็น not provided ส่ง "title": "" เหมือนกับ omit title — มันไม่เขียน empty string ลงใน PDF มัน walk fallback chain (token default → system default) เป็นสาเหตุของ bug report ที่ common ที่สุด “ฉัน set มัน มันไม่ติด”
  2. Token policies สามารถ strip หรือ default metadata fields multi-tenant SaaS ที่ใช้ gPdf สามารถ set default_metadata บน API token แต่ละตัวเพื่อให้ทุก PDF ที่ token นั้นสร้าง carry author และ producer ของลูกค้าโดยไม่ต้องเชื่อทุก developer ในการ set บน request แต่ละครั้ง Token-level default คือ enforcement layer ที่ถูกต้องสำหรับ “ทุก Acme PDF ต้องพูดว่า Acme”
  3. Downstream pipelines อาจ rewrite metadata ของคุณ Tools ที่ post-process PDFs หลังจาก gPdf return — Ghostscript โดยไม่มี explicit metadata-preservation flags, enterprise DRM tools บางตัว, “PDF optimisers” บางตัว — สามารถ overwrite Producer ด้วยชื่อของตัวเองและ undo branding ที่คุณเพิ่ง set Verify กับ production pipeline จริงของคุณ ไม่ใช่แค่ raw gPdf response

Verify metadata ของคุณ

หลังจาก implement การเปลี่ยนแปลงด้านบน สามวิธีอย่างรวดเร็วในการ check ว่า PDF ship ตามที่คุณตั้งใจไว้จริง:

Command line (macOS / Linux, ต้องการ poppler-utils):

$ pdfinfo your-output.pdf | head -10
Title:           Invoice INV-2026-3401
Subject:         Monthly invoice 2026-03
Author:          Acme Logistics, Inc.
Creator:         Acme Billing Platform v7.2
Producer:        Acme Billing Platform
Language:        en

Acrobat / Adobe Reader: File → Properties → Description tab fields ทั้ง 6 ปรากฏ พร้อม Title แสดงใน title bar ของ viewer ด้านบน

macOS Preview: ⌘+I (Get Info) “PDF” inspector pane แสดง fields เดียวกัน

ถ้า field ใดปรากฏว่าง, blank, หรือมีชื่อ tool ที่คุณไม่ได้ set walk กลับผ่าน request body — สาเหตุที่ common ที่สุดคือการส่ง "" (empty string) ซึ่ง API treat เป็น “not provided” และ walk fallback chain ไปยังค่า default สาเหตุที่ common เป็นอันดับสองคือ downstream pipeline (Ghostscript, DRM, optimiser) overwrite field หลังจาก gPdf return; test กับ production ไม่ใช่แค่ raw render response

Metadata ใน PDF/A archival

ถ้าคุณ render สำหรับ long-term archival ด้วย settings.profile: "pdfa-2b" (หรือ -2a, -3a, -3b) metadata หยุดเป็น optional และกลายเป็น load-bearing:

  • field producer ไม่สามารถว่างใน PDF/A-conformant file — อย่างน้อย system default จะ ship
  • language จำเป็นสำหรับ accessibility profiles (PDF/A-2a, PDF/A-3a) ไม่มี veraPDF validation fail outright
  • XMP metadata stream ที่ PDF/A ต้องการถูก generate อัตโนมัติจาก 6 fields ด้านบน; คุณไม่ต้อง construct เอง
  • title, author, subject, creator, producer และ language ทั้งหมด ride เข้า XMP stream ดังนั้น metadata indexer ของ downstream archive (Preservica, Archivematica) สามารถ build catalog จากพวกมันโดยไม่ต้อง re-parse document body

สำหรับเอกสาร archival branded metadata ไม่ใช่แค่ brand polish — เป็นส่วนหนึ่งของ durability ของ artefact สำนักงานศุลกากร German, หน่วยงานภาษี Brazilian หรือ long-term archive ใดที่เปิด PDF ของคุณในสิบปีจะเห็นสิ่งที่อยู่ใน fields เหล่านั้นในวันที่คุณ render การ set พวกมันโดยตั้งใจที่ render time เป็นโอกาสเดียวที่คุณได้

สิ่งที่ gPdf ยังไม่ expose

เพื่อให้ซื่อสัตย์เกี่ยวกับ surface วันนี้: PDF spec ยัง define Keywords (free-form search terms) และ XMP metadata stream ที่ support arbitrary custom key-value pairs gPdf ไม่ expose อันใดในปัจจุบัน API

ถ้าคุณต้องเก็บ arbitrary business data ภายใน PDF (order UUID, warehouse code, template version) workarounds วันนี้:

  • Set subject เป็น structured short string ที่ downstream systems parse
  • เก็บ business data ใน database ของคุณเอง keyed ด้วย filename หรือ content hash
  • Wait — XMP custom fields อยู่ใน roadmap และเมื่อ ship จะเป็นคำตอบที่ถูกต้องสำหรับ hidden machine-readable workflow context

การ conflate “branded metadata” (6 standard fields, available ตอนนี้) กับ “custom business metadata” (XMP custom fields, future) เป็นวิธีที่ง่ายที่สุดที่จะ over-promise สิ่งที่เป็นไปได้วันนี้ ควรเก็บแยกในการวางแผนของคุณเอง

ตัวอย่างที่สมบูรณ์

SaaS billing platform (Acme Billing Platform) สร้าง invoice สำหรับลูกค้า German (Müller Versand GmbH) พร้อม archive เป็น PDF/A:

{
  "settings": {
    "profile": "pdfa-3b",
    "metadata": {
      "title":    "Rechnung RE-2026-0412",
      "language": "de",
      "author":   "Müller Versand GmbH",
      "subject":  "Monatsrechnung — März 2026",
      "creator":  "Acme Billing Platform v7.2",
      "producer": "Acme Billing Platform"
    }
  }
}

pdfinfo กับ PDF ที่ได้:

$ pdfinfo invoice-2026-0412.pdf | head -10
Title:           Rechnung RE-2026-0412
Subject:         Monatsrechnung — März 2026
Author:          Müller Versand GmbH
Creator:         Acme Billing Platform v7.2
Producer:        Acme Billing Platform
Language:        de

Title ใน German, author เป็น Müller Versand (entity GmbH ของลูกค้า ผู้รับเอกสาร), creator เป็น Acme Billing Platform (ระบบ editorial ที่ตัดสินใจว่าจะใส่อะไรในหน้า), producer เป็น brand ของ Acme Billing Platform, language ถูก tag อย่างถูกต้องสำหรับ German screen reader และสำหรับ German full-text indexer ที่จะ pick up ใน DMS ของ Müller ต่อไป profile PDF/A-3b หมายความว่า metadata set นี้ยัง serialise เข้า XMP stream สำหรับ long-term archival

ไม่มีอะไรใน file properties ระบุชื่อ gPdf, Chromium หรือ tool ใดที่ลูกค้าไม่ได้เลือก ซึ่งคือ point พอดี

upgrade ที่เล็กที่สุดเท่าที่เป็นไปได้

ถ้าคุณ POST ไปที่ /api/v1/pdf/render อยู่แล้ว และ call ปัจจุบันของคุณไม่มี settings.metadata การปรับปรุงที่เล็กที่สุดคือ 3 บรรทัดที่เพิ่มเข้าไปใน JSON ที่คุณส่งอยู่แล้ว:

 {
   "pages": [...],
   "settings": {
+    "metadata": {
+      "author":   "Your customer's organisation",
+      "producer": "Your platform"
+    }
   }
 }

2 fields, 1 new key. Verify ได้ด้วย pdfinfo ในไม่กี่วินาที เมื่อสิ่งเหล่านี้ลงตัวแล้ว กรอก title, language, subject และ creator เมื่อคุณมีเวลา

สิ่งนี้อยู่ที่ไหน