مقاله فنی

نشانه‌گذاری خودکار ساختار برای PDF دسترس‌پذیر در Delphi

PDFlibPas می‌تواند یک سند را در حین رسم نشانه‌گذاری کند. SetAutoTagMode را روشن کنید و فراخوانی‌های معمولی DrawText به پاراگراف تبدیل می‌شوند، متنی که بلافاصله پس از RegisterHeading رسم می‌شود به عنوان آن سطح عنوان می‌شود، سرصفحه‌ها و پاصفحه‌های روان به artifact تبدیل می‌شوند که خواننده آن‌ها را رد می‌کند، تصاویر به figure تبدیل می‌شوند، و DrawTableRows جدول، ردیف‌ها و سلول‌هایش را به درخت ساختار می‌برد

جایگزین — و تا همین اواخر تنها گزینه — پیچیدن هر فراخوانی رسم در BeginTag و EndTag به‌صورت دستی بود. این کار می‌کند، و برای اسناد با ساختار غیرمعمول هنوز ابزار درستی است. برای یک گزارش، فاکتور یا صورت‌حساب معمولی یعنی دسترس‌پذیری خروجی به این بستگی دارد که هیچ‌کس در هیچ مسیر کدی که چیزی رسم می‌کند یک جفت را فراموش نکند

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

SetAutoTagMode یک بیت‌ماسک می‌گیرد و حالتی را که قبلاً فعال بوده برمی‌گرداند. AUTOTAG_TEXT (1) متن را به‌عنوان پاراگراف نشانه‌گذاری می‌کند، یا به‌عنوان عنوان وقتی یکی در انتظار باشد. AUTOTAG_FURNITURE (2) سرصفحه‌ها، پاصفحه‌ها و شماره صفحه‌های روان را به‌عنوان artifact علامت‌گذاری می‌کند. AUTOTAG_FIGURE (4) یک تصویر رسم‌شده را به figure تبدیل می‌کند، یا به artifact وقتی به‌عنوان تزئینی اعلام شده باشد. AUTOTAG_TABLE (8) جدول‌های رسم‌شده را به درخت ساختار می‌برد. AUTOTAG_DEFAULT برابر 15 است، یعنی هر چهار مورد

روشن کردن حالت همچنین سند را به‌عنوان tagged علامت‌گذاری می‌کند، و این مرحله کم‌اهمیت‌تر از آنچه به‌نظر می‌رسد نیست. یک خواننده سند را تا زمانی که کاتالوگ چیز دیگری نگوید untagged می‌داند (ISO 32000-1 §14.7.1)، پس یک فایل که یک درخت ساختار کامل با هیچ اعلان /MarkInfo حمل می‌کند توسط فناوری کمکی به‌عنوان چیزی که اصلاً ساختاری ندارد اعلام می‌شود. درخت آنجا هست؛ هیچ‌چیز آن را نمی‌خواند

var
  Lib: TPDFlib;
begin
  Lib := TPDFlib.Create;
  try
    Lib.SetOrigin(1);
    Lib.SetAutoTagMode(AUTOTAG_DEFAULT);   // text + furniture + figures + tables
    Lib.AddStandardFont(4);
    Lib.SetTextSize(18);
    Lib.RegisterHeading(1, 'Annual service report');
    Lib.DrawText(72, 96, 'Annual service report');   // becomes H1
    Lib.SetTextSize(11);
    Lib.DrawText(72, 130, 'Every unit installed before 2024 was inspected.');
    Lib.SaveToFile('report.pdf');
  finally
    Lib.Free;
  end;
end;

یک عنوان از کجا می‌داند به کدام متن تعلق دارد؟

RegisterHeading سطح را برای متن بعدی که رسم می‌شود نام‌گذاری می‌کند، و آن منتظر متن می‌ماند. اگر یک تصویر در میان آن رسم شود، تصویر به figure تبدیل می‌شود و عنوان برای متنی که بعد می‌آید معلق می‌ماند. آن رفتار عمدی است: جایگزین، جایی که تصویر سطح عنوان را می‌گرفت، اسنادی تولید می‌کرد که یک خط تزئینی زیر یک عنوان به‌عنوان عنوان اعلام می‌شد

قانون مشابه «روی یک مورد مصرف می‌شود» بر figureها حاکم است. RegisterFigure توضیحی که تصویر بعدی حمل می‌کند را فراهم می‌کند، و RegisterDecoration تصویر بعدی را به‌عنوان یک خط، حاشیه یا پس‌زمینه‌ای که معنایی حمل نمی‌کند اعلام می‌کند. هر دو توسط یک تصویر مصرف می‌شوند، پس یک تصویر بعدی هرگز توضیحی که برای تصویر قبلی در نظر گرفته شده را به ارث نمی‌برد — که دقیقاً همان چیزی است که باعث می‌شود متن جایگزین به تصویر اشتباه در کد نشانه‌گذاری‌شده دستی بچسبد

توضیح بیشتر از هر رشته دیگری در یک سند دسترس‌پذیر اهمیت دارد. یک خواننده نابینا توضیح را به‌جای تصویر دریافت می‌کند، و تمام چیزی که دریافت می‌کند همین است. «نمودار» یک توضیح نیست؛ «درآمد فصلی به تفکیک منطقه، با منطقه شرقی در Q3 بالاترین» یک توضیح است

Lib.RegisterFigure('Exploded view of the gearbox assembly');
Lib.AddImageFromFile('gearbox.png', 0);      // becomes a tagged Figure

Lib.RegisterDecoration;                       // meaningless rule
Lib.AddImageFromFile('divider.png', 0);       // drawn inside a layout artifact

جدول‌ها، سرصفحه‌ها و جایی که تصمیم تکرار قرار دارد

با بیت جدول روشن، DrawTableRows جدول، ردیف‌ها و سلول‌هایش را به درخت ساختار می‌برد، تا یک خواننده بتواند بگوید یک مقدار در کدام ستون قرار دارد به‌جای اینکه کل جدول را به‌عنوان یک رشته متن نامرتبط بخواند. SetTableHeaderRowCount مشخص می‌کند چند ردیف پیشرو سرصفحه هستند؛ آن ردیف‌ها به‌عنوان سلول‌های سرصفحه با یک scope ستونی نوشته می‌شوند، که همان چیزی است که به خواننده اجازه می‌دهد عنوان مقداری که کاربر روی آن است را اعلام کند

ردیف‌های سرصفحه‌ای که این‌طور نام‌گذاری می‌شوند سر جای خود می‌مانند. تکرار آن‌ها در بالای هر صفحه یک تصمیم چیدمان است، و همین‌طور می‌ماند: DrawTaggedTableRows یک آرگومان RepeatHeaderRows دقیقاً برای همان منظور می‌گیرد. جدا نگه‌داشتن این دو از اینکه درخت ساختار یک کپی دوم از سرصفحه برای هر صفحه‌شکنی کسب کند جلوگیری می‌کند، که دقیقاً همان چیزی است که یک تکرار خودکار تولید می‌کرد

var
  TableID: Integer;
begin
  TableID := Lib.CreateTable(40, 3);
  Lib.SetTableHeaderRowCount(TableID, 1);       // row 1 is the header band
  Lib.SetTableCellContent(TableID, 1, 1, 'Part');
  Lib.SetTableCellContent(TableID, 1, 2, 'Torque');
  Lib.SetTableCellContent(TableID, 1, 3, 'Unit');
  // ... fill the data rows ...
  // Draw rows 1..40 into a 600pt band, repeating one header row per page
  Lib.DrawTaggedTableRows(TableID, 72, 150, 600, 1, 40, 1);
end;

ترکیب نشانه‌گذاری خودکار و دستی

نشانه‌گذاری خودکار درون یک تگ که به‌صورت دستی باز شده کنار می‌ایستد. بخشی از یک سند می‌تواند توسط کد شما توصیف شود و بقیه به کتابخانه سپرده شود، بدون اینکه این دو در یکدیگر تودرتو شوند — که همان چیزی است که اکثر اسناد واقعی می‌خواهند. صفحه جلد و بلوک امضا ساختاری دارند که فقط شما می‌فهمید؛ دویست صفحه متن بدنه در میان آن‌ها ندارند

دو قانون ایمنی خروجی را تمیز نگه می‌دارند. هیچ‌چیز درون یک artifact نشانه‌گذاری نمی‌شود، چون محتوایی که به‌عنوان artifact علامت‌گذاری شده نباید هیچ عنصر ساختاری حمل کند. و متن خالی هیچ عنصری را باز نمی‌کند، پس یک DrawText سرگردان با یک رشته خالی نمی‌تواند یک عنصر ساختاری تولید کند که یک خواننده آن را به‌عنوان خالی اعلام کند. هر دو از همان نوع نقصی هستند که اسناد نشانه‌گذاری‌شده دستی به‌طور خاموش انباشته می‌کنند و یک اعتبارسنجی ماه‌ها بعد به‌صورت دسته‌ای گزارش می‌دهد

نشانه‌گذاری خودکار هنوز چه چیزی را برای شما تصمیم نمی‌گیرد

ترتیب خواندن فراتر از ترتیب رسم، نقش‌های معنایی که پاراگراف، عنوان، figure یا جدول نیستند، و اعلان‌های زبان. نشانه‌گذاری خودکار ساختار را به ترتیبی که محتوا رسم می‌شود اختصاص می‌دهد — اگر کد چیدمان شما نوار کناری را قبل از بدنه رسم کند، همان ترتیبی است که درخت ثبت می‌کند. برای اسنادی که ترتیب بصری و ترتیب خواندن واقعاً متفاوتند، API نشانه‌گذاری دستی هنوز ابزار درستی است، و مرور PDF تگ‌دار و ساختار دسترس‌پذیری نقش‌ها، scopeها و پیوندهای سرصفحه را به‌طور جزئی پوشش می‌دهد

وقتی سند تمام شد، اعتبارسنجی کنید به‌جای فرض کردن: یادداشت‌های preflight برای PDF/A و PDF/UA نشان می‌دهد چگونه یک حکم روی ساختاری که تولید کرده‌اید بگیرید، و مرور صادرات گزارش مبتنی بر dataset پوشش می‌دهد که این فراخوانی‌ها در یک موتور گزارش که چیدمانش را از داده تولید می‌کند کجا جای می‌گیرند

PDFlibPas یک کتابخانه PDF بومی Pascal برای Delphi، C++Builder و Lazarus بدون هیچ runtime خارجی PDF است، تا خروجی دسترس‌پذیر توسط همان کدی تولید شود که سند را رسم می‌کند — صفحه محصول PDFlibPas را برای لیست کامل API و پلتفرم‌ها ببینید