💡 ตัวอย่างการทำงานเต็มที่พร้อมใช้งานบน GitHub:
python-linux-container-pdf-signing
บทนำ
สคริปต์ทำงานได้ในเครื่องของคุณ คุณทำคอนเทนเนอร์ด้วย python:3.11-slim แล้วมันล้มเหลวที่ import groupdocs.signature คุณแก้ไขส่วนนี้แล้วก็ล้มเหลวอีกครั้งที่ลายเซ็นแรก ทั้งสองข้อผิดพลาดไม่ได้บอกว่าอะไรหายไปจริง ๆ
การเซ็นคอนเทนเนอร์ด้วย Python เป็นเวิร์กโฟลว์ของ GroupDocs.Signature ที่ต้องการสองชั้นการจัดหาแทนหนึ่งชั้น: ไลบรารีรันไทม์ .NET ที่ไบน์ดิงสร้างขึ้น, และฟอนต์ที่ลายเซ็นข้อความทุกอันต้องใช้ในการเรนเดอร์ บทเรียนนี้จะสร้างทั้งสองชั้น, แล้วสคริปต์ที่ค้นหาครอบครัวฟอนต์ในเวลารันแทนการกำหนดค่าแบบคงที่, เพื่อให้โค้ดเดียวทำงานได้ทั้งในคอนเทนเนอร์และบนเครื่องที่คุณเขียนมัน
ทำไมทั้งสองชั้นจึงสำคัญ
GroupDocs.Signature สำหรับ Python เป็นไบน์ดิงของ .NET, ดังนั้น libicu และไลบรารีที่เข้ากันได้กับ OpenSSL 1.1 ต้องมีอยู่ก่อนที่การ import ใด ๆ จะสำเร็จ นั่นคือชั้นที่หนึ่งและได้รับการอธิบายไว้อย่างละเอียดใน Running in Docker
เหตุผลที่สองชั้นถูกผสานเข้าด้วยกันคือทั้งสองล้มเหลวในช่วงที่เกี่ยวข้องกับการ import และไม่มีข้อผิดพลาดใดบอกสาเหตุ libssl1.1 ที่หายไปจะให้ข้อผิดพลาดโหลดเกี่ยวกับ shared object; ฟอนต์ที่หายไปจะให้ข้อผิดพลาดการเซ็นที่ห่อหุ้มด้วย proxy exception ทั้งสองไม่ได้บอกว่า “ภาพฐานของคุณเล็กเกินไป”, ซึ่งเป็นสิ่งที่จริง ๆ แล้วหมายถึง
ชั้นที่สองคือฟอนต์, และเป็นชั้นที่ทำให้คนหลายคนประหลาดใจ python:3.11-slim ไม่มีไฟล์ฟอนต์เลย GroupDocs.Signature ไม่ได้ทำการแทนที่ครอบครัวที่หายไป – การระบุครอบครัวที่ไม่ได้ติดตั้งจะทำให้เกิดข้อผิดพลาดและไม่มีอะไรถูกเขียน – และการล้างฟอนต์ก็ไม่ใช่วิธีแก้ไขเช่นกัน, เพราะไลบรารีจะขอใช้ค่าเริ่มต้นของตัวเองและล้มเหลวเช่นเดียวกัน บนภาพที่ไม่มีฟอนต์, ลายเซ็นข้อความเป็นไปไม่ได้โดยสิ้นเชิง
ข้อกำหนดเบื้องต้น
Python 3.11 (wheel รองรับได้ถึง CPython 3.14) และ groupdocs-signature-net==26.1 Docker หากคุณต้องการเห็นทั้งสองข้อผิดพลาดโดยเจตนา ซึ่งใช้เวลาประมาณสิบนาที
การติดตั้ง
pip install groupdocs-signature-net==26.1
ขั้นตอนที่ 1 - สร้างชั้น .NET
libssl1.1 ไม่มีใน bookworm, ดังนั้นจึงต้องดึงจาก snapshot ของ Debian ที่กำหนดไว้:
ENV SNAPSHOT_DATE=20220328T000000Z
RUN echo "deb [trusted=yes] http://snapshot.debian.org/archive/debian/${SNAPSHOT_DATE} bullseye main" \
> /etc/apt/sources.list.d/debian-archive.list \
&& apt-get -o Acquire::Check-Valid-Until=false update \
&& apt-get install -y --no-install-recommends \
libicu67 \
libssl1.1 \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
ประเด็นสำคัญ:
- ชั้นนี้ทำให้การ import ทำงานได้เท่านั้น; ไม่ได้กล่าวถึงฟอนต์เลย
- การกำหนด snapshot date ทำให้การสร้างสามารถทำซ้ำได้เมื่อ archive มีการเปลี่ยนแปลง
ขั้นตอนที่ 2 - สร้างชั้นฟอนต์
สี่แพ็กเกจ, แยกเป็นชั้นของตนเองเพื่อให้สามารถคอมเมนต์ออกเพื่อทำให้เกิดความล้มเหลวได้:
RUN apt-get update && apt-get install -y --no-install-recommends \
fontconfig \
fonts-dejavu-core \
fonts-liberation \
fonts-noto-cjk \
&& fc-cache -f \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
fontconfig เป็นตัวแก้ไขและให้คุณใช้ fc-list fonts-dejavu-core มีฟอนต์พื้นฐานสำหรับ Latin, Greek และ Cyrillic fonts-liberation รองรับเอกสารที่อ้างอิง Arial หรือ Times New Roman ตามชื่อ fonts-noto-cjk ครอบคลุม Chinese, Japanese และ Korean
ขั้นตอนที่ 3 - ถามไลบรารีว่าครอบครัวฟอนต์ใดที่สามารถใช้ได้
การสแกน /usr/share/fonts เพื่อหาไฟล์ชื่อดูเหมือนจะเทียบเท่าแต่จริง ๆ แล้วไม่ใช่: fonts-noto-cjk ติดตั้ง NotoSansCJK-Regular.ttc ซึ่งชื่อครอบครัวคือ Noto Sans CJK JP วิธีที่พกพาได้คือการทำ probe – ลายเซ็นจริงลงไฟล์ชั่วคราว – แล้วแปลงความล้มเหลวเป็นค่า:
with signature.Signature(source_path) as sign:
options = TextSignOptions()
options.text = "probe"
options.left = 10
options.top = 10
options.width = 60
options.height = 20
font = SignatureFont()
font.family_name = family_name
font.size = 10.0
options.font = font
sign.sign(scratch, [options])
return None
ดูที่ font.size = 10.0 อย่างใกล้ชิด ไบน์ดิงแปลงขนาดเป็น .NET float และปฏิเสธ int ด้วยข้อความ numeric argument expected, got 'int' เนื่องจากเกิดขึ้นภายใน probe, ทุกครอบครัวที่เป็นไปได้ล้วนล้มเหลวและผลลัพธ์ดูเหมือนภาพที่ไม่มีฟอนต์เลย ฉันได้เพิ่มสามแพ็กเกจฟอนต์ลงในภาพที่มีทั้งหมดแล้วก่อนจะสังเกตว่ามีการระบุค่าแบบตัวอักษร
การแก้ไขจึงเป็นลูป:
for candidate in candidates:
if try_family(source_path, candidate) is None:
return candidate
return None
ขั้นตอนที่ 4 - เซ็นสิ่งที่ได้แก้ไขแล้ว, ตรวจสอบสิ่งที่คุณเซ็น
ครอบครัว Latin จำเป็น, ครอบครัว CJK เป็นตัวเลือก:
with signature.Signature(source_path) as sign:
options = [build_text_options(LATIN_TEXT, latin_family, 50)]
if cjk_family:
options.append(build_text_options(CJK_TEXT, cjk_family, 120))
result = sign.sign(output_path, options)
return len(result.succeeded)
จากนั้นตรวจสอบ, เพราะ CJK ที่เรนเดอร์เป็นกล่องว่างจะไม่ทำให้เกิดข้อผิดพลาด:
options = TextVerifyOptions()
options.text = expected_text
options.match_type = gsd.TextMatchType.CONTAINS
options.all_pages = True
result = sign.verify(options)
CONTAINS ถูกเลือกโดยเจตนา: ในโหมดประเมินไลบรารีจะเพิ่มข้อความทดลองลงในหน้า, และการจับคู่ที่ตรงเป๊ะจะทำให้เอกสารที่สมบูรณ์ดีถูกรายงานว่าไม่ผ่าน
แล้วเอกสารที่บอกว่า Python มีการสนับสนุน Linux จำกัดล่ะ?
หน้า Running in Docker ระบุแพ็กเกจ Python ที่พร้อมใช้งานบน Linux แต่ไม่ได้รวม Signature ไว้ ใน groupdocs-signature-net==26.1 ตัวอย่างนี้เซ็นและตรวจสอบได้ภายใน python:3.11-slim, รวม CJK ด้วย, เมื่อทั้งสองชั้นถูกติดตั้ง ถือว่ารายการนั้นล้าสมัยแล้วและควรตรวจสอบด้วยเวอร์ชันของคุณเองก่อนทำการปรับใช้
การประยุกต์ใช้ในโลกจริง
บริการออกใบแจ้งหนี้ที่ต้องประทับบรรทัดการอนุมัติลงบน PDF ที่สร้างขึ้นต้องการสิ่งนี้อย่างแม่นยำ: ชั้น .NET, ฟอนต์ Latin หนึ่งตัว, และการตรวจสอบการแก้ไขขณะเริ่มต้น การตรวจสอบนี้คือสิ่งที่ทำให้การปรับใช้ที่ไม่ดีกลายเป็นคอนเทนเนอร์ที่ไม่สามารถเริ่มทำงานได้, แทนที่จะเป็นคิวใบแจ้งหนี้ที่ล้มเหลวแบบเงียบ ๆ พอร์ทัลเอกสารที่รับชื่อลูกค้าในสคริปต์ใด ๆ ก็ต้องการแพ็กเกจ CJK ด้วย, พร้อมขั้นตอนการตรวจสอบ, เพราะนี่คือสิ่งเดียวที่แยกความแตกต่างระหว่างกล่องว่างที่เรนเดอร์และชื่อที่เซ็นแล้ว
ที่ที่การตรวจสอบการแก้ไขควรอยู่
ใส่ไว้ในส่วนที่ทำงานหนึ่งครั้งต่อกระบวนการ: การเรียกระดับโมดูล, ตัวจัดการ FastAPI lifespan, AppConfig.ready ของ Django, หรือบรรทัดแรกของ worker หลัก จะได้ค่าที่ออกมาสองค่า, ครอบครัว Latin และครอบครัว CJK, ทั้งสองควรบันทึกในล็อกเริ่มต้นข้างนับจำนวนฟอนต์
การวางตำแหน่งนี้ทำมากกว่าการประหยัดเวลา probe; มันย้ายความล้มเหลวจากการจัดการคำขอ (ซึ่งเป็นปัญหาของลูกค้าคนเดียวและสแตกเทรซที่ไม่มีใครอ่าน) ไปยังการเริ่มต้น, ซึ่งเป็นการปรับใช้ที่ไม่ขึ้นและมีคนกำลังเฝ้าดูอยู่ คอนเทนเนอร์ที่ออกด้วยข้อความ “no usable font family, install fonts-dejavu-core” ไม่ต้องการการดีบักใด ๆ เลย
การแก้ไขปัญหาที่พบบ่อย
import groupdocs.signature fails
ชั้น .NET หายไปหรือ repository ของ snapshot ไม่สามารถเข้าถึงได้ระหว่างการสร้าง นี่คือชั้นที่หนึ่งและไม่มีส่วนเกี่ยวข้องกับฟอนต์ ตรวจสอบบันทึกการสร้างสำหรับขั้นตอน apt ก่อนจะไปแก้ไขโค้ดการเซ็น, เพราะการดึง snapshot ที่ล้มเหลวไม่ได้หยุดการสร้างอิมเมจ
Every candidate font fails, but fc-list shows fonts
ตรวจสอบว่า font.size ถูกตั้งเป็น int ก่อนเพิ่มแพ็กเกจเพิ่มเติม
The signature is there but the CJK text is boxes
fonts-noto-cjk หายไป ลายเซ็นถูกเขียนด้วยครอบครัวที่ไม่มี glyph สำหรับโค้ดพอยท์เหล่านั้น, ซึ่งเป็นเหตุผลที่ขั้นตอนการตรวจสอบมีอยู่: มันจะล้มเหลวในกรณีนี้โดยเฉพาะ, แม้ว่าการเซ็นจะรายงานว่าประสบความสำเร็จ
สิ่งที่สองภาพจริง ๆ พิมพ์ออกมา
รันทั้งสองและอ่านสี่บรรทัดแรก ภาพที่ไม่มีฟอนต์จะแสดง font files on disk: 0, ทั้งสองบรรทัดการแก้ไขเป็น (none), ข้อผิดพลาดฟอนต์หายโดยเจตนา, แล้วออกด้วยรหัส 3 พร้อมพิมพ์วิธีแก้ไขขั้นต่ำ ภาพที่จัดเตรียมไว้จะแสดงจำนวนฟอนต์ที่ไม่เป็นศูนย์, DejaVu Sans สำหรับ Latin และ Noto Sans CJK JP สำหรับ CJK, มีลายเซ็นสองอันและข้อความทั้งสองได้รับการตรวจสอบ
ผลลัพธ์สองชุดนี้เป็นศิลปวัตถุที่ควรเก็บไว้ คัดลอกลงในบันทึกการปรับใช้ของคุณและคนต่อไปที่เปลี่ยนภาพฐานจะมีอ้างอิงว่าคอนเทนเนอร์ที่สุขภาพดีควรเป็นอย่างไร, โดยไม่ต้องเข้าใจ fontconfig เลย
สรุป
สองชั้นและหนึ่ง probe. ติดตั้ง dependencies ของ .NET, ติดตั้งอย่างน้อย fontconfig และ DejaVu, แก้ไขครอบครัวโดยการถามแทนการสันนิษฐาน, และตรวจสอบผลลัพธ์ก่อนสรุปงาน ไม่มีโค้ดมาก, ทั้งหมดเป็นสิ่งที่ดูชัดเจนเมื่อมองย้อนกลับและมองไม่เห็นในสแตกเทรซ Repository ตัวอย่างส่งมอบ Dockerfile ทั้งสอง, ดังนั้นความแตกต่างระหว่างอิมเมจที่ทำงานและที่เสียหายคือการสร้างหนึ่งขั้นตอน