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

Pro รุ่น

Writer — เอกสารอ้างอิงเชิงลึก

โมดูล Writer เขียน revision แบบ incremental-update ของ PDF และแพ็กออบเจกต์ขนาดเล็กลงใน Object Stream ตัวเขียน incremental บังคับกฎ append-only แบบ fail-closed คือทุกไบต์ที่บัฟเฟอร์ถือไว้ก่อน revision ต้องคงเดิมไม่เปลี่ยนหลังจากนั้น ตัวสร้าง Object Stream รวมออบเจกต์ที่มีสิทธิ์เข้าเป็นออบเจกต์ /Type /ObjStm เดียวที่บีบอัดด้วย FlateDecode ภายใต้ขนาดที่จำกัด

ความสามารถนี้มาพร้อมกับ NextPDF Pro ( nextpdf/pro ) และเปิดใช้งานด้วย envelope ใบอนุญาตระดับ Pro การติดตั้งที่ไม่มีสิทธิ์ดังกล่าวจะไม่โหลดคลาสของความสามารถนี้ เปรียบเทียบรุ่นและรับใบอนุญาต ไม่มีแฟล็กใบอนุญาตต่อฟีเจอร์ โค้ดมาพร้อมกับรุ่น Pro

โมดูลอยู่ภายใต้ namespace NextPDF\Pro\Writer สัญลักษณ์สาธารณะทั้งหมดแสดงไว้ด้านล่าง value object เป็นคลาส final readonly แบบไม่เปลี่ยนแปลง

SymbolParametersDefault behaviorReturnsThrows or fails withNotes
IncrementalUpdateWriter::writeRevisionBinaryBuffer $buffer, ObjectRegistry $registry, int $prevXrefOffset, int $catalogObject, array $catalogEntries, array $catalogUpdates, array $newObjectNumbers, string $fileIdStatic เขียน catalog ใหม่ด้วยรายการที่ผสานแล้ว ต่อท้าย cross-reference table แบบดั้งเดิมสำหรับออบเจกต์ใหม่และที่แก้ไข และเขียน trailer ที่มี /Size, /Root, /Prev และ /ID ตรวจสอบว่า prefix ก่อน revision เท่ากันแบบไบต์ต่อไบต์หลังจากนั้นint — byte offset ของ cross-reference table ใหม่\NextPDF\Exception\WriterException เมื่อการตรวจ append-only prefix ล้มเหลว; getWriterState() คืนค่า dss-append-only-invariantจุดเข้าแบบ static ไม่มีผลลัพธ์ที่ใช้งานได้เมื่อละเมิด
ObjectStreamWriter::addObjectint $objectNumber, string $contentต่อท้ายออบเจกต์หนึ่งรายการเข้ากับ stream ที่ค้างอยู่หลังตรวจขนาดvoidOverflowException เมื่อ index รวมกับ body จะเกิน 65,536 ไบต์$content ไม่รวม wrapper N 0 obj / endobj
ObjectStreamWriter::canAcceptstring $contentประมาณ overhead ของ index และทดสอบยอดรวมสะสมกับค่าสูงสุดboolไม่ยกข้อยกเว้นpredicate ล้วน ไม่เปลี่ยนสถานะ
ObjectStreamWriter::buildnoneสร้าง index ต่อ body เข้าด้วยกัน บีบอัดด้วย FlateDecode และห่อพจนานุกรม /Type /ObjStmstring — เนื้อหา Object Stream ดิบObjectStreamWriteException เมื่อไม่มีออบเจกต์ถูกเพิ่ม หรือเมื่อการบีบอัด zlib ล้มเหลวผู้เรียกใช้กำหนดหมายเลขออบเจกต์และห่อ marker
ObjectStreamWriter::getEntriesnoneคำนวณ offset สัมพัทธ์กับ body ใหม่สำหรับออบเจกต์ที่สะสมไว้list<ObjectStreamEntry>ไม่ยกข้อยกเว้นoffset สัมพัทธ์กับส่วน body
ObjectStreamWriter::countnoneรายงานจำนวนออบเจกต์ที่สะสมไว้intไม่ยกข้อยกเว้น
ObjStmCompressor::__constructint $maxStreamSize = 65536, int $maxObjectsPerStream = 200เก็บขีดจำกัดขนาดและจำนวนออบเจกต์ที่ใช้ในการจัดกลุ่มไม่ยกข้อยกเว้นค่าเริ่มต้นตรงกับการปรับแต่ง Object Stream ของโมดูล
ObjStmCompressor::groupObjectslist<array{number: int, generation?: int, content: string}> $objectsกรองออบเจกต์ที่ไม่มีสิทธิ์ออก แล้วแพ็กที่เหลือลงใน writer ภายในขีดจำกัดขนาดและจำนวนlist<ObjectStreamWriter>ไม่ยกข้อยกเว้น; ออบเจกต์ที่ไม่มีสิทธิ์ถูกข้ามออบเจกต์ที่ generation ไม่ใช่ศูนย์จะตกไปยัง serialization ปกติ
ObjStmCompressor::isEligiblestring $content, int $generation = 0ปฏิเสธ stream object, /Encrypt, /XRef, /Catalog และ generation ที่ไม่ใช่ศูนย์ใด ๆboolไม่ยกข้อยกเว้นการจับคู่ /Type ทนต่อ whitespace และ escape #xx
ObjStmCompressor::writeToBufferlist<ObjectStreamWriter> $streams, BinaryBuffer $buffer, ObjectRegistry $registryจัดสรร carrier object ต่อ stream ลงทะเบียน type-2 compressed entry และเขียนบล็อก ObjStm แต่ละบล็อกlist<int> — หมายเลข carrier objectส่งต่อ ObjectStreamWriteException จาก build() เมื่อการบีบอัดล้มเหลวในกรณีที่พบยากรันหลังจากเขียนออบเจกต์ที่ไม่มีสิทธิ์แล้ว และก่อนที่ cross-reference จะถูก emit
ObjStmCompressor::estimateSavingslist<ObjectStreamWriter> $streams, int $originalSizeสร้างแต่ละ stream เพื่อวัดขนาดที่บีบอัดเทียบกับต้นฉบับObjStmCompressionResultส่งต่อ ObjectStreamWriteException จาก build() เมื่อการบีบอัดล้มเหลวในกรณีที่พบยากตัวช่วยวัดค่าแบบอ่านอย่างเดียว
ObjectStreamEntry::__constructint $objectNumber, string $content, int $offsetเรกคอร์ดแบบไม่เปลี่ยนแปลงของออบเจกต์ที่แพ็กหนึ่งรายการและ body offset ของมันไม่ยกข้อยกเว้นfinal readonly; property สาธารณะ
ObjStmCompressionResult::__constructint $originalObjectCount, int $streamCount, int $estimatedOriginalSize, int $estimatedCompressedSizeตัวเก็บ metric แบบไม่เปลี่ยนแปลงไม่ยกข้อยกเว้นfinal readonly; property สาธารณะ
ObjStmCompressionResult::savedBytesnoneคืนค่าขนาดต้นฉบับลบขนาดที่บีบอัดintไม่ยกข้อยกเว้นอาจเป็นค่าลบเมื่อการแพ็กทำให้ข้อมูลขยายขึ้น
ObjStmCompressionResult::savedPercentnoneคืนค่าเปอร์เซ็นต์การลดขนาดfloatไม่ยกข้อยกเว้นคืนค่า 0.0 เมื่อขนาดต้นฉบับเป็นศูนย์
ObjStmCompressionResult::compressionRationoneคืนค่าขนาดที่บีบอัดหารด้วยต้นฉบับfloatไม่ยกข้อยกเว้นคืนค่า 1.0 เมื่อขนาดต้นฉบับเป็นศูนย์
ObjectStreamWriteExceptionส่งสัญญาณความล้มเหลวในการสร้าง Object Streamสืบทอดจาก RuntimeExceptionยกโดย build(); ดักจับได้ผ่าน RuntimeException เพื่อความเข้ากันได้ย้อนหลัง
final class IncrementalUpdateWriter
{
public static function writeRevision(
BinaryBuffer $buffer,
ObjectRegistry $registry,
int $prevXrefOffset,
int $catalogObject,
array $catalogEntries,
array $catalogUpdates,
array $newObjectNumbers,
string $fileId,
): int;
}
final class ObjectStreamWriter
{
public function addObject(int $objectNumber, string $content): void;
public function canAccept(string $content): bool;
public function build(): string;
/** @return list<ObjectStreamEntry> */
public function getEntries(): array;
public function count(): int;
}
final class ObjStmCompressor
{
public function __construct(
int $maxStreamSize = 65536,
int $maxObjectsPerStream = 200,
);
/**
* @param list<array{number: int, generation?: int, content: string}> $objects
* @return list<ObjectStreamWriter>
*/
public function groupObjects(array $objects): array;
public function isEligible(string $content, int $generation = 0): bool;
/**
* @param list<ObjectStreamWriter> $streams
* @return list<int>
*/
public function writeToBuffer(array $streams, BinaryBuffer $buffer, ObjectRegistry $registry): array;
/** @param list<ObjectStreamWriter> $streams */
public function estimateSavings(array $streams, int $originalSize): ObjStmCompressionResult;
}

writeRevision เขียน revision แบบ incremental-update หนึ่งรายการ มันทำ snapshot ของ prefix ของบัฟเฟอร์ที่มีอยู่ก่อนเขียน มันเขียน catalog ใหม่ด้วยรายการที่ผสานแล้ว ลงทะเบียน offset ของออบเจกต์ใหม่ เขียน cross-reference table แบบดั้งเดิมที่จัดกลุ่มเป็น subsection ที่ต่อเนื่องกัน และเขียน trailer ที่มี /Size, /Root, /Prev และ /ID หลังจากเขียนแล้ว มันเปรียบเทียบ prefix อีกครั้ง หากไบต์ก่อนหน้าใดเปลี่ยนไป มันจะยก WriterException ที่พกสถานะ append-only-violation และไม่คืนผลลัพธ์ที่ใช้งานได้ เมื่อสำเร็จ มันจะคืนค่า byte offset ของ cross-reference table ใหม่เพื่อร้อยต่อ revision เพิ่มเติม การผสม cross-reference table และ stream ข้าม revision ต่าง ๆ เป็นที่อนุญาต

ObjectStreamWriter สะสมออบเจกต์ addObject ยกข้อผิดพลาด overflow เมื่อ index และ body รวมกันจะเกินค่าสูงสุด 65,536 ไบต์แบบไม่บีบอัด build ยกข้อผิดพลาดสำหรับ stream ที่ว่าง มิฉะนั้นมันจะบีบอัด index บวก body และคืนเนื้อหา Object Stream พร้อมรายการ /Type /ObjStm, /N, /First, /Length และ /Filter /FlateDecode ผู้เรียกใช้เป็นผู้กำหนดหมายเลขออบเจกต์และห่อ marker N 0 obj / endobj

ObjStmCompressor ตัดสินว่าออบเจกต์ใดจะถูกแพ็ก มันยกเว้น stream object, พจนานุกรมการเข้ารหัส, cross-reference stream, document catalog และออบเจกต์ใดที่มีหมายเลข generation ไม่ใช่ศูนย์ writeToBuffer จัดสรร carrier object ต่อ stream ลงทะเบียนออบเจกต์ที่แพ็กแต่ละตัวเป็น type-2 compressed cross-reference entry และเขียนบล็อก ObjStm ที่ offset ปัจจุบันของบัฟเฟอร์ estimateSavings สร้างแต่ละ stream เพื่อคำนวณ metric ขนาดโดยไม่เปลี่ยนแปลงบัฟเฟอร์

  • การตรวจ append-only ทำสำเนา prefix ที่มีอยู่ ต้นทุนของมันเพิ่มตามขนาดของเอกสารที่เขียนไปแล้ว ต้นทุนนี้เป็นความตั้งใจและปกป้องไบต์ที่ลงนามแล้ว
  • ขีดจำกัดของ Object Stream ใช้กับ index บวก body แบบไม่บีบอัด ให้วางพจนานุกรมการเข้ารหัสและชนิดออบเจกต์อื่นที่ถูกยกเว้นเป็น direct indirect object
  • การยกเว้น /Type ทนต่อ whitespace ระหว่าง token ตามอำเภอใจและ hex escape #xx รูปแบบเช่น /Type /Encrypt, /Type\n/Encrypt และ /Type /#45ncrypt ทั้งหมดถูกปฏิเสธ ไม่ใช่เพียงการสะกดตัวอักษรตามรูปแบบมาตรฐานเท่านั้น
  • ออบเจกต์ใดที่พกหมายเลข generation ไม่ใช่ศูนย์จะถูกถือว่าไม่มีสิทธิ์ และตกไปยัง serialization N G obj … endobj ปกติ เพราะ generation ของออบเจกต์ที่บีบอัดเป็นศูนย์โดยปริยาย
  • writeToBuffer ต้องรันหลังจากเขียนออบเจกต์ที่ไม่มีสิทธิ์ทั้งหมดแล้ว และก่อนที่ cross-reference จะถูก emit ออบเจกต์ที่แพ็กแล้วต้องไม่ถูก serialize แยกอีกด้วย

โมดูล Writer ไม่ทำการดำเนินการเชิงการเข้ารหัสลับใด ๆ มันปกป้องไบต์ที่ลงนามแล้วโดยปฏิเสธที่จะ emit เมื่อไบต์ก่อนหน้าจะเปลี่ยนไป ซึ่งเป็นการทดสอบความเท่ากันของไบต์มากกว่าการทดสอบเชิงการเข้ารหัสลับ การเลือกอัลกอริทึม FIPS สำหรับการลงนามและการแฮชถูกกำกับโดยโมดูลการลงนาม ไม่ใช่โดย writer นี้ การเปิดหรือปิดโหมด FIPS ไม่เปลี่ยนพฤติกรรมของเมธอด Writer ใด ๆ

NextPDF ทำการ implement โมดูลนี้ตาม ISO 32000-2:2020 ตัวเขียน incremental ทำตามไวยากรณ์ incremental-update ของ §7.5.6 คือแต่ละ revision ต่อท้าย cross-reference section ที่ครอบคลุมเฉพาะออบเจกต์ที่ใหม่ เปลี่ยนแปลง หรือถูกลบ และ trailer ที่ /Prev ให้ offset ของ cross-reference ก่อนหน้า ตัวสร้าง Object Stream ทำตามแบบจำลอง object-stream ของ §7.5.7 คือ index ของคู่หมายเลขออบเจกต์และ offset ที่ offset วัดจากรายการ /First โดยเรียงเพิ่มขึ้น นำหน้า body ของออบเจกต์ที่แพ็กไว้ การอ้างอิงข้อกำหนดทั้งสองถูกตรวจสอบเทียบกับคอร์ปัส ISO 32000-2:2020 การร้อยต่อ revision สำหรับ workflow PAdES B-LT และ B-LTA ทำตาม ETSI EN 319 142-1 §5.4 ตามที่ระบุไว้ในซอร์ส การรองรับข้อกำหนดหนึ่งเป็นคำแถลงความสามารถเชิงวิศวกรรม ไม่ใช่การรับรอง NextPDF ไม่ถือใบรับรองความสอดคล้องอย่างเป็นทางการใด ๆ

  • ติดตั้งแพ็กเกจด้วย composer require nextpdf/pro:^3 คลาสจะ resolve ภายใต้ NextPDF\Pro\Writer
  • IncrementalUpdateWriter::writeRevision เป็นจุดเข้าแบบ static มันไม่ถือสถานะ instance ระหว่าง revision
  • ObjectStreamEntry, ObjStmCompressionResult, IncrementalUpdateWriter และ compressor รวมกันเป็นพื้นผิวสาธารณะของโมดูล; repository ไม่มีตัวอย่างที่รันได้สำหรับมัน
  • WriterException จาก writeRevision บ่งชี้การละเมิด append-only ให้ถือเป็นความล้มเหลวขั้นรุนแรงและทิ้งบัฟเฟอร์
  • Object Stream carrier เป็น indirect object; ผู้เรียกใช้กำหนดหมายเลขออบเจกต์ผ่าน registry

หน้านี้บันทึกเฉพาะพฤติกรรมที่สังเกตได้จากภายนอกและพื้นผิว public API ที่รองรับเท่านั้น เส้นทาง namespace ภายใน คลาสตัวช่วย ตารางกลไก ชื่อไฟล์ runbook และ prefix ของ ticket อยู่นอกขอบเขต