مقاله فنی

فرم‌های PDF با محاسبه خودکار در دلفی با استفاده از HotPDF

یک فاکتور یا PDF هزینه که در آن اقلام به طور خودکار با هم جمع می‌شوند و تاریخ خودش را فرمت‌بندی می‌کند، به سه چیز متصل به AcroForm نیاز دارد، و HotPDF هر سه مورد را به یک فایل بارگذاری‌شده در دلفی اضافه می‌کند: ApplyLoadedFieldCalculation برای مجموع‌های AFSimple_Calculate، متد ApplyLoadedFieldValidation برای فرمت AF آکروبات و ابزارهای کمکی اعتبارسنجی، و EnsureLoadedFieldAppearanceStream برای ثبت مقداری که نمایشگر واقعاً رسم می‌کند

ابزار HotPDF یک کامپوننت بومی VCL PDF برای دلفی و سی‌پلاس‌پلاس‌بیلدر است، و این سه متد روی سندی عمل می‌کنند که شما با LoadFromFile باز کرده‌اید، نه سندی که از ابتدا در حال ساخت آن هستید. هر کدام یک ایندکس فیلد مبتنی بر ۰ را در آرایه /Fields در AcroForm دریافت می‌کنند — همان ترتیبی که GetLoadedFormFieldNames برمی‌گرداند — و مستقیماً در دیکشنری فیلد می‌نویسند، بنابراین کل کار عبارت است از بارگذاری، ایندکس‌گذاری، متصل کردن، و SaveLoadedDocument. ساخت خود مجموعه فیلد کار مجزایی است که در بخش افزودن فیلدهای AcroForm به یک PDF بارگذاری‌شده در دلفی پوشش داده شده است، در حالی که این صفحه درباره ارائه محاسبات ریاضی و فرمت‌بندی به یک مجموعه فیلد موجود است

چرا یک فرم PDF برای جمع زدن فیلدهای خود به اسکریپت نیاز دارد؟

زیرا PDF فاقد لایه صفحه‌گسترده (spreadsheet) است. یک فیلد متنی یک رشته را نگه می‌دارد، نه یک فرمول، و هیچ چیز در فایل در هنگام تغییر یک قلم کالا، مجموع را دوباره محاسبه نمی‌کند مگر اینکه اسکریپت برای انجام این کار متصل شده باشد. مکانیسم مربوطه، دیکشنری اکشن‌های اضافی فیلد یعنی /AA است (استاندارد ISO 32000-1 §12.7.5)، که رویدادهای محرک را به اکشن‌ها نگاشت می‌کند: یک محرک فشردن کلید (K) با ورود کاراکترها، یک محرک فرمت برای نمایش، یک محرک اعتبارسنجی (V) در زمان ثبت نهایی، و یک محرک محاسبه (C) که هر زمان هر فیلدی روی فرم تغییر کند اجرا می‌شود. هر اکشن یک اکشن جاوا اسکریپت /S /JavaScript است (استاندارد ISO 32000-1 §12.6.4.14)، و اسکریپت که اجرا می‌کند همان چیزی است که واقعاً جمع یا تغییر فرمت را انجام می‌دهد. آکروبات کتابخانه‌ای از ابزارهای کمکی آماده مانند AFSimple_Calculate، AFNumber_Format، AFDate_FormatEx و بقیه را ارائه می‌دهد، بنابراین اسکریپت متصل‌شده معمولاً یک فراخوانی تک‌خطی به آن کتابخانه است تا یک کد سفارشی

یک مورد جزئی تعیین می‌کند که آیا یک فرم چندمرحله‌ای پاسخ درستی می‌دهد یا خیر: ترتیب محاسبه. آکروبات هر فیلدی را که دارای یک اکشن محاسبه است اجرا می‌کند و آنها را به ترتیبی که در آرایه /CO در AcroForm لیست شده‌اند اجرا می‌کند، نه به ترتیب صفحات. متد ApplyLoadedFieldCalculation اکشن محاسبه را به فیلدی که نام می‌برید متصل می‌کند؛ وقتی یک فیلد محاسباتی فیلد دیگری را تغذیه می‌کند — مثلاً یک جمع جزء که یک سطر مالیاتی آن را می‌خواند، و سپس یک جمع کل که هر دو را می‌خواند — باید تأیید کنید که ترتیب /CO هر ورودی را قبل از فیلدی که آن را مصرف می‌کند لیست کرده باشد، در غیر این صورت جمع کل یک جمع جزء قدیمی را می‌خواند و یک مرحله ویرایش عقب می‌ماند

چگونه کاری کنیم که یک فرم PDF مجموع مبالغ را به طور خودکار محاسبه کند؟

متد ApplyLoadedFieldCalculation ایندکس فیلد مقصد، یک عملیات، و نام فیلدهای مبدا را که باید تجمیع کند، دریافت می‌کند. ابزار HotPDF دستور AFSimple_Calculate("SUM", ["lineTotal.1", "lineTotal.2", ...]) را در اکشن محاسبه فیلد مقصد می‌نویسد، و شمارشگر THPDFFieldCalcOp تابع را انتخاب می‌کند: fccSum، fccAverage، fccProduct، fccMinimum و fccMaximum به توابع SUM، AVG، PRD، MIN و MAX آکروبات نگاشت می‌شوند. نام‌های مبدا همان نام‌های فیلدهای AcroForm هستند، یعنی همان رشته‌هایی که GetLoadedFormFieldNames پس می‌دهد، بنابراین محاسبه مجموع روی سه قلم کالا و یک هزینه ارسال در قالب یک فراخوانی واحد انجام می‌شود

فراخوانی EnsureLoadedFieldAppearanceStream روی فیلد مجموع جنبه ظاهری ندارد. محاسبه در داخل نمایشگر اجرا می‌شود، بنابراین تا زمانی که یک خواننده با قابلیت اجرای اسکریپت فایل را باز نکند، فیلد مجموع محتوای رندرشده‌ای از خود ندارد. ثبت کردن یک جریان ظاهری (appearance stream) به آن رقمی می‌دهد تا در این فاصله نشان دهد، که این همان چیزی است که یک کار چاپ یا یک تولیدکننده تصویر بندانگشتی در زمانی که هرگز اسکریپت را اجرا نمی‌کند، دریافت خواهد کرد

فرمت‌بندی و اعتبارسنجی فیلدها با مجموعه کمکی AF

متد ApplyLoadedFieldValidation اسکریپت‌های فرمت، فشردن کلید و اعتبارسنجی را در یک فراخوانی متصل می‌کند، و نوع THPDFFieldValidationKind ارسالی تعیین می‌کند که کدام ابزار کمکی آکروبات متصل شود. با ارسال fvkCurrency، ابزار HotPDF دستور AFNumber_Format(2,0,0,0,"$ ",true) را به عنوان اسکریپت فرمت به همراه AFNumber_Keystroke مربوطه می‌نویسد؛ fvkNumber از "." برای آرگومان جداکننده استفاده می‌کند، fvkPercentage دستور AFPercent_Format را صادر می‌کند، و fvkDate دستور AFDate_FormatEx("dd/mm/yyyy") را صادر می‌کند مگر اینکه ماسک خود را در آرگومان DateFormat ارسال کنید. فرمت‌های خاص — fvkZipCode، fvkSSN و fvkPhone — به AFSpecial_Format با کدی که آکروبات برای هر کدام رزرو کرده است نگاشت می‌شوند، و fvkArbitraryMask یک رشته ماسک را از طریق آرگومان Mask برای هر چیزی که آن پیش‌فرض‌ها پوشش نمی‌دهند دریافت می‌کند. برای انواع عددی، HotPDF همچنین یک اسکریپت AFRange_Validate متصل می‌کند تا ورودی خارج از محدوده در زمان ثبت نهایی رد شود

// Format the invoice date as ISO, the amount as currency, and mask a tax id
Pdf.ApplyLoadedFieldValidation(DateIdx,   fvkDate,          'yyyy-mm-dd', '');
Pdf.ApplyLoadedFieldValidation(AmountIdx, fvkCurrency,      '', '');
Pdf.ApplyLoadedFieldValidation(TaxIdIdx,  fvkArbitraryMask, '', '999-99-9999');
Pdf.EnsureLoadedFieldAppearanceStream(AmountIdx);

چرا باید جریان ظاهری را دوباره تولید کنید؟

زیرا رشته مقدار و پیکسل‌هایی که یک نمایشگر ترسیم می‌کند دو چیز متفاوت در PDF هستند، و تغییر یکی دیگری را تغییر نمی‌دهد. یک فیلد مقدار خود را در /V ذخیره می‌کند، اما آنچه یک خواننده رندر می‌کند از جریان ظاهری فیلد یعنی /AP /N می‌آید. اگر /V را از طریق گراف شیء ویرایش کنید، یک نمایشگر سخت‌گیر همچنان ظاهر قدیمی را نشان می‌دهد تا زمانی که چیزی آن را دوباره تولید کند. دیکشنری فرم حاوی پرچم /NeedAppearances است که از نمایشگر می‌خواهد در زمان باز شدن، هر ظاهر را بازسازی کند و آکروبات آن را رعایت می‌کند — اما رندرکننده‌های تعبیه‌شده در مرورگر، خواننده‌های موبایل، سرورهای چاپ و تولیدکننده‌های تصاویر بندانگشتی اغلب آن را نادیده می‌گیرند و هر جریانی را که از قبل وجود دارد رسم می‌کنند، یا اصلاً چیزی رسم نمی‌کنند. متد EnsureLoadedFieldAppearanceStream این شکاف را با ساخت خود Form XObject در مسیر /AP /N، بر اساس فیلدهای /DA، /V و /Rect پر می‌کند، به طوری که مقدار رندرشده در داخل فایل منتقل می‌شود به جای اینکه به نمایشگر برای بازطراحی آن وابسته باشد

جایی که اسکریپت‌های AF دیگر کار نمی‌کنند

هر اسکریپت *AF به این بستگی دارد که نمایشگر دارای موتور جاوا اسکریپت باشد، و بیشتر نمایشگرها فاقد آن هستند. آکروبات و چند محصول دسکتاپ این ابزارهای کمکی را اجرا می‌کنند؛ نمایشگرهای PDF تعبیه‌شده در Chrome، Edge، Firefox و پلتفرم‌های اصلی موبایل، به همراه شطرنجی‌سازهای سمت سرور و خطوط لوله چاپ، اصلاً هیچ جاوا اسکریپت فرمی را اجرا نمی‌کنند. در آن خواننده‌ها، محاسبه هرگز اجرا نمی‌شود و فرمت هرگز اعمال نمی‌شود — فیلد به سادگی هر جریان ظاهری را که آخرین بار با آن ذخیره شده نشان می‌دهد، که دقیقاً به همین دلیل پختن /AP /N اهمیت دارد. با لایه *AF به عنوان یک تسهیلات برای گروهی از کاربران در یک خواننده با قابلیت اجرای اسکریپت رفتار کنید، نه به عنوان یک تضمین. هر مجموعی که باید در سرور صحیح باشد — مانند مبلغ فاکتوری که بر اساس آن صورت‌حساب صادر می‌کنید، یا رقم مالیاتی که ثبت می‌نمایید — باید پس از بازگشت فرم در سمت شما دوباره محاسبه شود، زیرا نمی‌توانید فرض کنید خواننده‌ای که آن را پر کرده است، حتی یک خط از اسکریپت شما را اجرا کرده باشد

یک مورد دیگر نیز ارزش نام بردن دارد: فرم‌هایی که به صورت XFA ارائه شده‌اند. یک فرم پویای XFA موتور محاسباتی خود را در یک بار داده XML حمل می‌کند که ابزارهای کمکی *AF چیزی از آن نمی‌دانند، بنابراین متصل کردن AFSimple_Calculate به یک فیلد XFA هیچ کاری انجام نمی‌دهد. ابتدا فرم XFA را به یک AcroForm ساده تخت کنید، همان‌طور که در بخش تخت‌سازی XFA به AcroForm در دلفی پوشش داده شده است، و سپس سه متد در اینجا دقیقاً مانند هر فرم بارگذاری‌شده دیگری روی فیلدهای AcroForm حاصل اعمال می‌شوند

محاسبه خودکار و فرمت‌بندی خودکار AcroFormها به سه فراخوانی روی یک سند بارگذاری‌شده خلاصه می‌شود — متصل کردن محاسبه، متصل کردن فرمت و اعتبارسنجی، و پختن ظاهر — و در نهایت فراخوانی SaveLoadedDocument. API فرم بارگذاری‌شده نشان‌داده‌شده در اینجا بخشی از HotPDF Component برای دلفی و سی‌پلاس‌پلاس‌بیلدر است که مستندات شمارشگرهای کامل THPDFFieldValidationKind و THPDFFieldCalcOp را در کنار بقیه ساختار AcroForm ارائه می‌دهد