ข้ามไปยังเนื้อหา
getnextpdf.com

ทำให้คัดลอกและวางข้อความ CJK และอาหรับจาก PDF ได้ถูกต้อง

สร้างไฟล์ Portable Document Format (PDF) ที่มีข้อความภาษาจีน ญี่ปุ่น เกาหลี (CJK) และอาหรับซึ่งคัดลอกและวางออกมาเป็นอักขระเชิงตรรกะดั้งเดิมได้ เอนจินแมปกลิฟทุกตัวไปยัง Unicode ผ่าน /ToUnicode CMap ทำให้ผลลัพธ์อยู่ในรูปแบบบัญญัติด้วย Normalization Form Compatibility Composition (NFKC) และห่อรันที่ถูกจัดรูปหรือมีระยะห่างตัวอักษรไว้ใน /Span ที่มี /ActualText เมื่อลงทะเบียนฟอนต์แล้วเขียนเนื้อหา การแยกดึงจะยังคงถูกต้อง ตรวจสอบยืนยันการแยกดึงด้วย pdftotext/Poppler ไม่ใช่ PyMuPDF ส่วน veraPDF ใช้ตรวจสอบยืนยัน PDF/Universal Accessibility 2 (PDF/UA-2) ไม่ใช่ตรวจสอบการแยกดึง

Terminal window
composer require nextpdf/core

ลงทะเบียนฟอนต์ CJK เช่น Noto Sans CJK และฟอนต์ที่รองรับอาหรับซึ่งมีตารางอักขระครอบคลุมบล็อก Arabic Presentation Forms-B เช่น Noto Naskh Arabic ฝังเฉพาะฟอนต์ที่คุณมีสิทธิ์ฝังเท่านั้น

รหัสกลิฟในสตรีมเนื้อหาไม่ใช่ Unicode โดยตรง /ToUnicode CMap จะแมปแต่ละรหัสกลับเป็น Unicode เพื่อให้ตัวอ่านแยกดึงข้อความได้ (ISO 32000-2 §9.10) เอนจินทำให้ค่าเหล่านั้นอยู่ในรูปแบบบัญญัติด้วย Normalization Form Compatibility Composition (NFKC) ตามนิยามใน Unicode UAX #15 อักษรภาพเพื่อความเข้ากันได้ของ CJK และรูปแบบการแสดงผลของอาหรับจะแมปกลับไปยังอักขระฐานของตน ดังนั้นการค้นหาและการคัดลอกจึงคืนค่าข้อความบัญญัติแทน code point เพื่อความเข้ากันได้

มีสองกรณีที่ต้องใช้มากกว่า /ToUnicode ได้แก่ ภาษาละตินที่มีระยะห่างตัวอักษรและภาษาอาหรับแบบขวาไปซ้ายที่ถูกจัดรูป เอนจินวาดข้อความเหล่านี้เป็นกลิฟที่มีระยะห่างหรือถูกเรียงลำดับใหม่ ลำดับกลิฟเพียงอย่างเดียวจึงไม่สามารถกู้คืนสตริงเชิงตรรกะได้ เอนจินจึงห่อแต่ละรันไว้ในลำดับเนื้อหาที่มาร์กเป็น /Span และมี /ActualText ซึ่งเป็นการแทนที่เนื้อหาที่ห่อไว้อย่างตรงกันทุกประการ (ISO 32000-2 §14.9) เครื่องมือแยกดึงที่เคารพ /ActualText จะคืนค่าสตริงเชิงตรรกะ

สัญลักษณ์ตำแหน่งบทบาท
FontRegistry::register(string $fontFile, string $alias = ''): FontInfoNextPDF\Typography\FontRegistryลงทะเบียนฟอนต์ CJK และอาหรับ
DocumentFactory::create(): DocumentNextPDF\Core\DocumentFactoryสร้างเอกสารโดยใช้รีจิสทรีที่กำหนด
Document::writeHtml(string $html): staticNextPDF\Core\Concerns\HasTextOutputเรนเดอร์เนื้อหาหลายภาษา
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\DocumentFactory;
use NextPDF\Graphics\ImageRegistry;
use NextPDF\Typography\FontRegistry;
$fonts = new FontRegistry();
$fonts->register(__DIR__ . '/NotoSansCJK-Regular.ttf', alias: 'CJK');
$fonts->register(__DIR__ . '/NotoNaskhArabic-Regular.ttf', alias: 'Arabic');
$doc = (new DocumentFactory($fonts, new ImageRegistry(maxCacheBytes: 0)))->create();
$doc->addPage();
$doc->writeHtml(
'<p style="font-family: \'CJK\';">PDF 2.0 引擎 — 量子</p>'
. '<p style="direction: rtl; font-family: \'Arabic\';">فاتورة</p>'
);
$doc->save(__DIR__ . '/multilingual.pdf');
Terminal window
pdftotext multilingual.pdf - | head
# Extracts the logical text: "PDF 2.0 引擎 — 量子" and the logical Arabic "فاتورة",
# not compatibility code points or reversed presentation forms.

ตัวอย่างแบบครบในตัวนี้จะแท็กเอกสาร เพิ่มส่วนหัวที่มีระยะห่างตัวอักษร และเขียนไฟล์ไปยังพาธของฮาร์เนส

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\DocumentFactory;
use NextPDF\Graphics\ImageRegistry;
use NextPDF\Typography\FontRegistry;
$fonts = new FontRegistry();
$fonts->register(__DIR__ . '/NotoSansCJK-Regular.ttf', alias: 'CJK');
$fonts->register(__DIR__ . '/NotoNaskhArabic-Regular.ttf', alias: 'Arabic');
$doc = (new DocumentFactory($fonts, new ImageRegistry(maxCacheBytes: 0)))->create();
$doc->setTitle('Multilingual extraction');
$doc->enableTaggedPdf('en');
$doc->addPage();
$html = <<<'HTML'
<h1 style="font-family: 'CJK'; letter-spacing: 3px;">CODE WORD</h1>
<p style="font-family: 'CJK';">中文 · 日本語 · 한국어 · 量子 (compatibility ideograph)</p>
<p style="direction: rtl; font-family: 'Arabic';">المبلغ الإجمالي 380.00</p>
HTML;
$doc->writeHtml($html);
$out = getenv('NEXTPDF_OUT');
$doc->save($out !== false ? $out : __DIR__ . '/multilingual-copy-paste-extraction.pdf');
echo "Wrote the multilingual PDF\n";

เรียกใช้ pdftotext กับผลลัพธ์ ส่วนหัวที่มีระยะห่างตัวอักษรจะแยกดึงออกมาเป็น CODE WORD โดยไม่มีช่องว่างแทรก บรรทัด CJK จะแยกดึงออกมาเป็นอักขระฐานของตน และบรรทัดอาหรับจะแยกดึงออกมาเป็นสตริงเชิงตรรกะ

  • ตรวจสอบยืนยันการแยกดึงด้วย pdftotext ไม่ใช่ PyMuPDF โหมดข้อความดิบของ PyMuPDF จะเพิกเฉยต่อ /ActualText แบบ inline และคืนค่ากลิฟเชิงภาพ จึงอาจรายงานความถูกต้องต่ำกว่าความเป็นจริง Poppler (pdftotext) เคารพ /ActualText ส่วน veraPDF ใช้ตรวจสอบยืนยัน PDF/UA-2 ไม่ใช่ตรวจสอบการแยกดึง
  • การแยกดึงต้องอาศัย /ToUnicode ลงทะเบียนและฝังฟอนต์เพื่อให้ตัวเขียนสร้าง /ToUnicode CMap ฟอนต์ที่ไม่ได้ฝังและไม่เป็นมาตรฐานไม่สามารถรับประกันการแมป Unicode ได้
  • /ActualText ครอบคลุมรันที่ถูกจัดรูปและมีระยะห่างตัวอักษร สำหรับข้อความธรรมดาที่ไม่ถูกจัดรูปและไม่มีระยะห่าง /ToUnicode เพียงอย่างเดียวก็แยกดึงได้ถูกต้อง ส่วนการห่อด้วย /Span จะรักษารันที่มีระยะห่างหรือถูกเรียงลำดับใหม่ไว้
  • ตาราง HTML ที่แท็กไว้ผ่านการตรวจสอบ PDF/UA-2 การแยกดึงถูกต้อง และ <table> HTML ที่แท็กไว้ผ่าน veraPDF --flavour ua2 โดยมีความล้มเหลวเป็นศูนย์แล้วในขณะนี้ — ดู การเข้าถึง

การสร้าง /ToUnicode CMap และการห่อด้วย /Span ใช้ทรัพยากรเพิ่มขึ้นเชิงเส้นตามจำนวนกลิฟ สูตรนี้กำหนดงบประมาณไว้ที่ wall_ms: 1500, peak_mb: 96

ตรวจสอบความถูกต้องของความยาวสตริงหลายภาษาที่ผู้ใช้ป้อนเพื่อให้ขนาดผลลัพธ์อยู่ในขอบเขตที่จำกัด ตัวสร้าง /ToUnicode ปฏิเสธ surrogate ครึ่งเดียวและรหัสที่อยู่นอก codespace ดังนั้นการแมปที่มีรูปแบบผิดจึงไม่สามารถสร้างทรัพยากรการแยกดึงที่เสียหายได้ เอนจินไม่รันสคริปต์ใดและไม่ดึงทรัพยากรระยะไกลสำหรับฟอนต์ในเครื่อง

ข้อความระบุข้อกำหนดข้อ
/ToUnicode CMap แมปรหัสอักขระไปยัง Unicode สำหรับการแยกดึงISO 32000-2§9.10
/ActualText เป็นการแทนที่เนื้อหาที่ห่อไว้อย่างตรงกันทุกประการISO 32000-2§14.9
NFKC คือการแยกองค์ประกอบเพื่อความเข้ากันได้แล้วประกอบกลับแบบบัญญัติUnicode UAX #15§1.2

ไม่มี