مقاله فنی

تبدیل (Flattening) فرم‌های XFA به AcroForm در دلفی با استفاده از HotPDF

دو فرم می‌توانند فیلدهای یکسانی داشته باشند و اصلاً شبیه به هم رفتار نکنند. یک AcroForm فیلدهای خود را به عنوان اشیاء معمولی PDF در بالای محتوای واقعی صفحه نگه می‌دارد، بنابراین هر خواننده استانداردی آن را ترسیم می‌کند. یک فرم پویای XFA تقریباً هیچ‌چیز را به عنوان PDF نگه نمی‌دارد: فیلدها، طرح‌بندی و حتی هندسه صفحه در یک بسته XML قرار دارند، و صفحات قابل مشاهده در زمان باز شدن توسط یک موتور طرح‌بندی تولید می‌شوند که فقط Adobe تاکنون آن را به طور گسترده عرضه کرده است. اگر آن فایل را به یک نمایشگر وب، یک رندرکننده آرشیو یا یک استخراج‌کننده متن بدهید، فرم را دریافت نمی‌کنید. شما یک صفحه خاکستری رنگ دریافت می‌کنید که روی آن نوشته شده "Please wait... If this message is not eventually replaced by the proper contents of the document, your PDF viewer may not be able to display this type of document." (لطفاً صبر کنید... اگر این پیام در نهایت با محتویات مناسب سند جایگزین نشد، ممکن است نمایشگر PDF شما نتواند این نوع سند را نمایش دهد). هر کسی که اسناد دولتی یا بیمه‌ای را پردازش کرده باشد، با دیدن این صفحه آن را می‌شناسد

این صفحه نگهدارنده نشانه خرابی نیست. این دقیقاً همان چیزی است که فرمت مشخص می‌کند در زمان نبود پردازنده XFA باید اتفاق بیفتد، و از سال 2026 این موضوع تقریباً در مورد هر نمایشگری خارج از نسخه دسکتاپ Acrobat صادق است. بنابراین اقدام عملی این است که فرم پویا را قبل از رسیدن به هر ابزار پایین‌دستی به یک AcroForm ساده تبدیل کنید. ابزار HotPDF، کتابخانه PDF شرکت losLab برای دلفی و C++Builder، این تبدیل را در کد انجام می‌دهد و فرم XML را به عنوان فیلدهای بومی در صفحات بومی بازسازی می‌کند

چرا این دو مدل نمی‌توانند با هم همزیستی داشته باشند

فرمت AcroForm در استاندارد ISO 32000-1 بخش 12.7 تعریف شده است. هر فیلد یک شیء PDF است که دارای حاشیه‌نویسی ویجت و یک استریم ظاهری (appearance stream) است، صفحه، محتوای واقعی PDF است و داده‌ها روی آن قرار می‌گیرند. مدل XFA این را وارونه می‌کند: فرم، یک سند XML است (یک بسته XDP که در ورودی /XFA از دیکشنری AcroForm ذخیره شده است) و صفحات PDFِ یک فرم پویا، فقط نگهدارنده پیام "Please wait" را در خود دارند و هیچ چیز دیگری ندارند، زیرا محتوای واقعی هرگز به عنوان PDF سریال‌سازی نشده است. یک خواننده، فایل را یا به عنوان یک مدل و یا مدل دیگر پردازش می‌کند. ورودی /XFA را نادیده بگیرید تا پوسته خالی را ببینید؛ آن را بدون موتور XFA رعایت کنید تا اخطار را ببینید. استاندارد ISO 32000-2 با حذف کامل XFA از PDF 2.0 به این بحث پایان داد، که این دلیل اصلی تبدیل "تبدیل کردن تا زمانی که هنوز می‌توانیم" از یک حالت استثنایی به یک سیاست ورودی معمول است

قبل از تبدیل هر چیزی، آن را طبقه‌بندی کنید، زیرا همه فایل‌های XFA صفحه نگهدارنده را نشان نمی‌دهند. فرم‌های XFA ایستا (Static) صفحات از پیش رندر شده PDF را در کنار XML ارسال می‌کنند، بنابراین در همه‌جا نمایش داده می‌شوند و تنها در زمان پر شدن بدرفتاری می‌کنند. فرم‌های پویا (Dynamic) فقط صفحه نگهدارنده را ارسال می‌کنند و تا زمانی که تبدیل نشوند غیرقابل استفاده هستند. چیزی که باید به آن اعتماد کرد خود سند است، نه پسوند یا فرستنده. فایلی که محتوای واقعی را در یک نمایشگر غیر از Adobe رندر می‌کند اما همچنان دارای یک ورودی /XFA است، ایستا یا ترکیبی (hybrid) است؛ فایلی که صفحه اخطار را نشان می‌دهد، پویا است. ثبت کنید که هر فایل ورودی در کدام دسته قرار گرفته است. این دو نوع بعداً به روش‌های مختلفی دچار مشکل می‌شوند، و یک تیکت در مورد یک فرم بایگانی شدهِ خالی در عرض چند ثانیه بسته می‌شود، زمانی که لاگ ورودی از قبل نوشته است: "dynamic XFA, converted, 47 fields mapped, 2 warnings"

تبدیل یک سند بارگذاری شده XFA به فیلدهای بومی

این تبدیل بر روی سندی که قبلاً در حافظه بارگذاری شده است اجرا می‌شود. متد FlattenLoadedXFA قالب XFA و بسته‌های داده آن را تجزیه می‌کند، فرم را طرح‌بندی می‌کند و آن را به عنوان فیلدهای AcroForm در صفحات واقعی PDF بازسازی می‌کند:

var
  Pdf: THotPDF;
  MappedCount, I: Integer;
  Warnings: TStrings;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.LoadFromFile('dynamic_xfa.pdf');
    MappedCount := Pdf.FlattenLoadedXFA(True);   // True = fields stay editable
    Warnings := Pdf.XFAFlattenWarnings;
    for I := 0 to Warnings.Count - 1 do
      Log('XFA flatten warning: ' + Warnings[I]); // unmapped elements
    Pdf.SaveLoadedDocument('native_acroform.pdf');
    Log(Format('Mapped %d fields', [MappedCount]));
  finally
    Pdf.Free;
  end;
end;

مقدار بازگشتی و لیست هشدارها جزو خروجی‌ها هستند، نه نویز اشکال‌زدایی (debug)، بنابراین هر دو را نگه دارید. این تبدیل به دلیل ماهیت خود، اطلاعاتی را از دست می‌دهد: اسکریپت‌نویسی در XFA، فیلدهای محاسبه شده، و رفتار پویای زیرفرم‌ها (subform) هیچ معادلی در AcroForm ندارند، و ویژگی XFAFlattenWarnings نام هر عنصر قالبی را که مپ (map) نشده است ذکر می‌کند. فایل تبدیل شده را بدون لیست هشدارهای آن آرشیو کنید و روزی به یک کادر مجموع‌های خالی در یک کپی آرشیو شده بدون ثبت دلیل آن خیره خواهید شد. پرچم Editable کنترل می‌کند که آیا فیلدهای جدید قابل پر کردن باقی بمانند یا خیر. اگر افراد به کار با فرم بعد از آن ادامه می‌دهند، مقدار True را پاس دهید، و زمانی که هدف یک رکورد قفل شده است، مقادیر را قفل کنید

بررسی یک تبدیل تا حدی بصری و تا حدی ساختاری است، و شما به هر دو نیمه نیاز دارید. نیمه ساختاری آسان است: تأیید کنید که تعداد فیلدها با مقدار MappedCount مطابقت داشته باشد. نیمه بصری همان بخشی است که آسیب واقعی را کشف می‌کند. فرم منبع را در نسخه دسکتاپ Acrobat باز کنید (که هنوز هم تنها نمایشگری است که موتور XFA را اجرا می‌کند)، در کنار آن فایل تبدیل شده را در یک خواننده معمولی باز کنید و مقادیر و طرح‌بندی را روی حداقل یک نمونه پر شده برای هر الگو (template) مقایسه کنید. تاریخی که موتور XFA آن را به صورت 2026-06-11 نمایش داده است، ممکن است در نسخه کپی AcroForm به عنوان یک مقدار خام و فرمت‌بندی نشده قرار بگیرد و این چیزی است که فقط چشمان شما می‌توانند متوجه آن شوند

زمانی که ورودی یک بسته XDP است

همیشه کار با یک PDF پر شده شروع نمی‌شود. گاهی اوقات بسته XDP را به تنهایی دریافت می‌کنید، که از یک ابزار طراحی فرم اکسپورت شده است یا توسط یک سیستم شریک تحویل داده شده است. متد ApplyXFAAsAcroForm مرحله بارگذاری (load) را کنار می‌گذارد و بسته را مستقیماً روی سند فعلی اعمال می‌کند:

XDPBytes := TFile.ReadAllBytes('benefit-claim.xdp');
MappedCount := Pdf.ApplyXFAAsAcroForm(XDPBytes, True);

همین گروه از فراخوانی‌ها در جهت دیگر نیز اجرا می‌شوند، برای موارد نادرتری که مجبور هستید به جای استفاده از XFA آن را تولید کنید. متد AddXFAPacket بسته‌های نام‌گذاری شده جداگانه‌ای مانند 'xdp' یا 'config' را ضمیمه می‌کند. متد SetXFADocument یک بارِ داده کامل تک-جریانی (single-stream payload) را در یک فراخوانی نصب می‌کند. متد ClearXFAPackets عملیات ثبت را پاک می‌کند تا بتوانید دوباره شروع کنید، و متد AddXFASignaturePacket داده‌های XAdES را برای جریان‌های کاری که داده‌های فرم XML را مستقیماً امضا می‌کنند تعبیه می‌کند. تولید XFA در سال 2026 یک نیاز حاشیه‌ای (niche) است که تقریباً همیشه توسط یک مصرف‌کننده قدیمی که هر چیز دیگری را رد می‌کند تحمیل می‌شود، اما زمانی که یک قرارداد از آن نام می‌برد، این فراخوانی‌ها آن را به جای یک ابزار جداگانه، در حد یک انتخاب پیکربندی نگه می‌دارند

معنای دیگر "flatten"

کلمه "flatten" باعث سوءتفاهم در بسیاری از مکالمات می‌شود، زیرا این کلمه به طور کامل به یک عملیات دوم نیز اشاره می‌کند: رایت کردنِ (burning) ظاهر فیلد AcroForm در استریم محتوای صفحه تا زمانی که هیچ شیء تعاملی باقی نماند. ابزار HotPDF امروز هیچ APIای برای آن ندارد، و شما می‌خواهید این را اکنون بدانید تا در میانه یک پروژه متوجه آن نشوید. چیزی که این کتابخانه به جای آن به شما می‌دهد، قفل کردن در سطح فیلد هنگام ایجاد فیلد است که توسط مجوزهای سند پشتیبانی می‌شود:

// Lock the value at field creation: read-only text field
Pdf.CurrentPage.AddTextField('CaseNumber', 'BC-2026-0117',
  Rect(50, 700, 220, 720), 0, [ffReadOnly]);

// Belt and suspenders: restrict form filling document-wide
Pdf.ActivateProtection := True;
Pdf.CryptKeyLength := aes256;
Pdf.OwnerPassword := 'records-owner';
Pdf.ProtectOptions := [prPrint, prInformationCopy, prExtractContent];
// fill permission withheld: prFillAnnotations is absent from the set

در مورد اینکه این ویژگی چه چیزی برای شما به ارمغان می‌آورد و چه چیزی را خیر، شفاف باشید. یک فیلد فقط‌خواندنی هنوز هم یک شیء فرم است. در پنل فیلد نمایشگر نشان داده می‌شود، مقدار آن از طریق فرم API قابل خواندن است و ابزاری که فایل را بازنویسی می‌کند می‌تواند پرچم فقط‌خواندنی را دوباره پاک کند. پرچم‌های مجوز مانع را سخت‌تر می‌کنند اما به این بستگی دارند که نمایشگر تصمیم بگیرد به آنها احترام بگذارد، محدودیتی که ISO 32000-1 به وضوح آن را بیان می‌کند. زمانی که یک قانون‌گذار اصرار دارد که یک رکورد آرشیو شده به هیچ‌وجه نباید حاوی اشیاء فرم باشد، پاسخ صادقانه در HotPDF امروز بازسازی سند است: مقادیر را بخوانید، سپس آنها را به عنوان محتوای معمولی TextOut روی یک صفحه تازه ترسیم کنید، به جای اینکه پرچم‌های فقط‌خواندنی را به عنوان flatten جا بزنید. یک نکته که باید در مسیر مجوزها به خاطر بسپارید این است که ویژگی CryptKeyLength باید قبل از BeginDoc تنظیم شود؛ مابقی موارد در مقاله رمزنگاری و مجوزهای AES-256 ما آمده است

XFA به چه معناست برای انطباق با آرشیو

استانداردهای PDF/A و PDF/X هر دو، XFA را به طور کامل رد می‌کنند. بنابراین یک پایپ‌لاین که یک آرشیو ISO 19005 را تغذیه می‌کند باید در ابتدا تبدیل را انجام دهد، و ترتیب آن قابل مذاکره نیست: بارگذاری (load)، متد FlattenLoadedXFA، ذخیره‌سازی، سپس اجرای تولیدِ بایگانی یا اعتبارسنجی روی نتیجه AcroForm. تبدیل را به عنوان دلیلی بر انطباق در نظر نگیرید. این کار مدل فرم را اصلاح می‌کند و فونت‌ها، رنگ و متادیتا را دقیقاً به همان صورت که بوده‌اند باقی می‌گذارد، بنابراین قبل از اعتماد به خروجی، آن را با ابزار veraPDF اعتبارسنجی کنید. هنگامی که فرم روی AcroForm قرار گرفت، رفتار آن مجموعه کنترل‌های خاص خود را دریافت می‌کند. تریگرهای جاوااسکریپت، اکشن‌های submit و اسکریپت‌های اعتبارسنجی در مقاله فیلدها و عملکردهای AcroForm در HotPDF پوشش داده شده‌اند

رابط‌های کاربری برنامه‌نویسی (API) برای ثبت نام XFA، تبدیل، و فرم که در اینجا نشان داده شده است به همراه کامپوننت HotPDF برای دلفی و C++Builder عرضه می‌شود که مستندات آن، مجموعه ویژگی‌های XFA را به موازات رشد آنها در نسخه‌های اخیر ردیابی می‌کند