مقاله فنی

عبارات مسیر XFA SOM در دلفی: داده‌های فرم پویا

کتابخانه PDF losLab گره‌های داده تکراری در فرم‌های پویای XFA را از طریق عبارات مسیر XFA 3.3 SOM می‌خواند و می‌نویسد: توابع GetXFAFormFieldValue و SetXFAFormFieldValue ریشه‌های $data، $record و !data، کاراکترهای عمومی رخداد [*]، انتخابگرهای فرزندان (descendant) و فرزند عمومی (child-wildcard)، ویژگی parent، انتخابگرهای کلاس #dataGroup/#dataValue، و گزاره‌های ساده به سبک FormCalc را می‌پذیرند، به طوری که یک برنامه دلفی می‌تواند با یک فراخوانی، هر سطر فاکتور را در یک فرم مالیاتی دولتی به‌روزرسانی کند. این مقاله مرجع آدرس‌دهی است؛ برای جابجایی مجموعه‌های داده کامل بین فایل‌ها، به مقاله همراه در مورد تبادل داده‌های فرم FDF، XFDF و XFA مراجعه کنید

مشکل درست از زمانی شروع می‌شود که یک فرم از حالت مسطح خارج می‌شود. یک اظهارنامه مالیات بر ارزش افزوده یا اظهارنامه گمرکی ساخته شده به عنوان فرم پویای XFA دارای دوازده فیلد با نام‌های Total_Price_1 تا Total_Price_12 نیست؛ بلکه دارای یک ساب‌فرم Detail است که یک بار در DOM الگو تعریف شده و به تعداد دفعات مورد نیاز داده‌ها، نمونه‌سازی می‌شود. مقادیر در درخت دوم زندگی می‌کنند، یعنی Data DOM در داخل بسته datasets، جایی که هر سطر آیتم یک عنصر تکراری <Detail> در زیر <Receipt> است. نام‌های فیلد مسطح نمی‌توانند مواردی مانند "قیمت در سطر سوم" یا "هر سطر با مبلغ بالای ۲۰۰" را آدرس‌دهی کنند؛ این دقیقاً کاری است که فصل Scripting Object Model از مشخصات Adobe XFA 3.3 به عبارات SOM می‌دهد و این همان سینتکسی است که APIهای مقدار فیلد XFA کتابخانه PDF losLab با آن صحبت می‌کنند

چگونه گره‌های داده XFA را از دلفی آدرس‌دهی کنیم؟

هر مسیر datasets از یک ریشه شروع می‌شود و کتابخانه PDF losLab دیکته‌های استاندارد را به صورت متقابل می‌پذیرد: $data شکل کوتاه شده XFA 3.3 برای xfa.datasets.data است، !data شکل کوتاه شده ریشه‌دار در xfa.datasets است، و در بسته‌هایی که از پردازش رکورد استفاده نمی‌کنند $record به رکورد داده بیرونی، یعنی اولین عنصر زیر گره داده تبدیل می‌شود. APIهای سمت الگو مانند SetXFAFormFieldAccess به همین ترتیب $template و xfa.template را می‌پذیرند. بخش‌ها می‌توانند با نقطه یا با اسلش جدا شوند ($data.Receipt.Tax یا $data/Receipt/Tax)، که این امر اهمیت دارد زیرا GetXFAFormFieldNames فیلدها را به عنوان مسیرهای جدا شده با اسلش شمارش می‌کند که مستقیماً به APIهای مقدار و الگو بازخورد داده می‌شوند. شاخص‌های رخداد طبق مشخصات صفر‌محور هستند، بنابراین Detail[0] ردیف اول است. دو قانون گریز (escaping) نام‌های غیرمعمول را قابل آدرس‌دهی نگه می‌دارد: در یک مسیر جدا شده با نقطه \. نشان‌دهنده یک نقطه واقعی است (Line\.Item)، در حالی که مسیرهای جدا شده با اسلش با نقطه‌ها به عنوان کاراکترهای معمولی رفتار می‌کنند. داده‌های تجاری که پیشوند فضای نام XML خود را حمل می‌کنند، با نام محلی مطابقت داده می‌شوند، بنابراین <m:Receipt> همچنان به Receipt پاسخ می‌دهد

var
  Lib: TPDFlib;
  Tax: WideString;
begin
  Lib := TPDFlib.Create;
  try
    Lib.LoadFromFile('vat-return.pdf', '');
    // Equivalent addresses for the same data node
    Tax := Lib.GetXFAFormFieldValue('Receipt.Tax');
    Tax := Lib.GetXFAFormFieldValue('$data.Receipt.Tax');
    Tax := Lib.GetXFAFormFieldValue('xfa.datasets.data.Receipt.Tax');
    Tax := Lib.GetXFAFormFieldValue('$record.Tax');
    // Slash form with a zero-based occurrence index
    Lib.SetXFAFormFieldValue('$record/Detail[0]/Total_Price', '251.00');
    Lib.SaveToFile('vat-return-updated.pdf');
  finally
    Lib.Free;
  end;
end;

چگونه ردیف‌های تکراری را در یک فرم XFA به صورت برنامه‌نویسی پر کنیم؟

کاراکتر عمومی رخداد [*] ابزار دسته‌ای است. در حالی که یک شاخص عددی یک فرزند هم‌سطح (sibling) را انتخاب می‌کند، [*] همه فرزندان هم‌سطح با نام یکسان را انتخاب می‌کند، بنابراین $data.Receipt.Detail[*].Total_Price فیلد قیمت را در هر سطر آیتم به طور همزمان آدرس‌دهی می‌کند. در هنگام خواندن، GetXFAFormFieldValue مقادیر تمام گره‌های مطابقت‌یافته را که با جداکننده | به هم متصل شده‌اند برمی‌گرداند؛ در هنگام نوشتن، SetXFAFormFieldValue هر گره مطابقت‌یافته را با همان مقدار به‌روزرسانی می‌کند و در صورت موفقیت ۱ را برمی‌گرداند. مسیری که با هیچ چیز مطابقت ندارد به عنوان یک رشته خالی خوانده می‌شود، که روشی ساده برای بررسی وجود یک شاخه قبل از نوشتن در آن است

var
  Lib: TPDFlib;
  Prices: WideString;
begin
  Lib := TPDFlib.Create;
  try
    Lib.LoadFromFile('invoice.pdf', '');
    // All Detail rows at once: '250.00|60.00'
    Prices := Lib.GetXFAFormFieldValue('$data.Receipt.Detail[*].Total_Price');
    // Reset one column across every repeated row in a single call
    Lib.SetXFAFormFieldValue('$data.Receipt.Detail[*].Surcharge', '0.00');
    Lib.SaveToFile('invoice-updated.pdf');
  finally
    Lib.Free;
  end;
end;

انتخابگرهای فرزندان، فرزند عمومی و والد

سه انتخابگر ساختاری مواردی را پوشش می‌دهند که شما نام فیلد را می‌دانید اما عمق دقیق آن را نمی‌دانید. انتخابگر فرزندان .. با هر عمقی در زیر گره فعلی مطابقت دارد: $data..Total_Price اولین Total_Price را در هر کجای زیر ریشه داده پیدا می‌کند و نوشتن از طریق مسیر فرزندان، آن اولین گره مطابقت‌یافته را به‌روزرسانی می‌کند. به این مرز توجه کنید: یک شاخص رخداد بعد از یک مطابقت فرزندان، بین شاخه‌های هم‌سطح حرکت نمی‌کند، بنابراین اگر $data..Total_Price[0] در داخل اولین سطر Detail حل شود، $data..Total_Price[1] به جای رفتن به سطر بعدی، مقدار خالی برمی‌گرداند؛ زمانی که همه آنها را می‌خواهید از Detail[*] استفاده کنید. کاراکتر عمومی فرزند .* با هر فرزند مستقیم مطابقت دارد و به بخش‌های باقی‌مانده اجازه فیلتر کردن می‌دهد: $data.Receipt.*.Total_Price به Total_Price در داخل هر شاخه فرزند Receipt می‌رسد، بدون اینکه فیلد خلاصه با نام یکسانی را که مستقیماً روی خود Receipt قرار دارد انتخاب کند. در نهایت، ویژگی parent یک سطح به بالا می‌رود، که XFA 3.3 به طور تعمدی آن را از .. جدا می‌کند: مسیر $data.Receipt.Detail[1].parent.Tax از دومین سطر آیتم شروع می‌شود، یک سطح به Receipt بالا می‌رود و روی مقدار هم‌سطح آن یعنی Tax قرار می‌گیرد

انتخابگرهای کلاس و گزاره‌ها: #dataGroup، #dataValue، .[expression]

سینتکس #class گره‌های Data DOM را به جای نام، بر اساس کلاس شیء آدرس‌دهی می‌کند، که این مسیر زمانی کاربردی است که تگ‌های XML حاوی کاراکترهایی باشند که املا کردن آنها به عنوان نام‌های اسکریپت دشوار است. کتابخانه PDF losLab کلمه #dataGroup را به عناصری که فرزندان عنصری دارند و #dataValue را به عناصر برگ (leaf elements) بدون فرزند عنصری نگاشت می‌کند، و هر دو با رخدادهای عددی یا [*] ترکیب می‌شوند: $data.Receipt.#dataGroup[0].#dataValue[0] اولین مقدار را در اولین گروه زیر Receipt انتخاب می‌کند. انتخابگر گزاره .[expression] فرزندان هم‌سطح با نام یکسان را بر اساس محتوا فیلتر می‌کند. گزاره‌های پشتیبانی‌شده مقایسه‌های ساده یک مقدار فرزند در برابر یک مقدار ثابت با عملگرهای رابطه‌ای مانند >، < و <= هستند؛ وقتی یک گزاره با چندین ردیف مطابقت دارد، خواندن‌ها به صورت متصل با جداکننده | برمی‌گردند و نوشتن‌ها هر تطابق را به‌روزرسانی می‌کنند

// Flag every line whose Total_Price exceeds 200
Lib.SetXFAFormFieldValue(
  '$data.Receipt.Detail.[Total_Price > 200].Review_Flag', '1');

// Descriptions of all low-value lines, '|'-joined when several match
Desc := Lib.GetXFAFormFieldValue(
  '$data.Receipt.Detail.[Total_Price <= 200].Description');

// Class-based addressing when tag names resist script spelling
Total := Lib.GetXFAFormFieldValue(
  '$data.Receipt.#dataGroup[0].#dataValue[0]');

کدام ویژگی‌های XFA SOM پشتیبانی نمی‌شوند؟

پشتیبانی SOM در کتابخانه PDF losLab به مجموعه‌های داده و مسیرهای فیلد الگو محدود شده است و مرزهای آن به جای تلاش برای بهترین نتیجه، صریح هستند. دانستن آنها از ابتدا، شما را از اشکال‌زدایی مسیری که بی سروصدا یک رشته خالی را برمی‌گرداند نجات می‌دهد

  • گزاره‌ها دستورات دلخواه FormCalc یا JavaScript را اجرا نمی‌کنند: هیچ فراخوانی تابعی وجود ندارد (یک گزاره contains(...) مقدار خالی برمی‌گرداند)، هیچ عبارت بولین ترکیبی وجود ندارد، و فقط یک مقایسه ساده child op literal انجام می‌شود
  • کلید $record فقط در بسته‌های بدون پردازش رکورد کار می‌کند؛ صفحه‌بندی رکورد dataWindow و چرخش گروه رکورد پیاده‌سازی نشده‌اند
  • وضوح مستقیماً در برابر Data DOM و DOM الگو است؛ وضوح نسبی Form DOM، یعنی نمای ادغام‌شده‌ای که نمایشگر در زمان اجرا می‌سازد، انجام نمی‌شود
  • معناشناسی بارگذاری ویژگی Data DOM اعمال نمی‌شود؛ کتابخانه بسته datasets را به عنوان XML خام می‌خواند، بنابراین مسیرها عناصر را آدرس‌دهی می‌کنند، نه ویژگی‌هایی که به گره ارتقا یافته‌اند

این مرزها به ندرت در کارهای پر کردن دسته‌ای مشکل‌ساز می‌شوند، زیرا یک پرکننده داده‌ها را بر اساس ساختار آدرس‌دهی می‌کند تا منطق اسکریپت‌شده. زمانی که این اتفاق می‌افتد، راه نجات سطح بسته است: کل XML مجموعه‌های داده را بخوانید، آن را در دلفی تبدیل کنید و دوباره بنویسید

نوشتن کل بسته با SetXFAFromString

متد SetXFAFromString همان نقطه ورود در سطح بسته است: این متد یک رشته کامل XDP، شامل الگو و datasets را با هم نصب می‌کند و کانتینر AcroForm را در یک سند جدید (زمانی که هنوز وجود ندارد) ایجاد می‌کند، و بسته‌های کدگذاری شده با UTF-16 به همراه علائم ترتیب بایت و همچنین UTF-8 را می‌پذیرد. یک ساختار تولید متداول این است که XDP صادر شده توسط سازمان را به عنوان یک ثابت نگه دارید، آن را با SetXFAFromString بارگذاری کنید، و سپس قبل از ذخیره، فراخوانی‌های SetXFAFormFieldValue با آدرس SOM را برای مقادیر فرار هر فاکتور اجرا کنید. از آنجا که XFA یکی از دو مدل فرمی است که یک PDF می‌تواند حمل کند، بخش کلاسیک AcroForm داستان اتوماسیون خود را دارد که در مقاله اکشن‌های فرم تعاملی و جاوا اسکریپت پوشش داده شده است

فیلد-مقدار XFA، شمارش و APIهای بسته‌ای که در اینجا نشان داده شده‌اند در losLab PDF Library برای دلفی، سی‌شارپ و VB.NET ارائه می‌شوند؛ صفحه محصول حاوی مرجع کامل مدیریت فرم است