مقاله فنی

تبدیل PDF به Markdown و DOCX در Delphi با PDFlibPas

PDFlibPas محتوای PDF را بدون اتوماسیون Office به دو قالب قابل‌ویرایش تبدیل می‌کند. ExportPageMarkdown و ExportDocumentMarkdown یک Markdown معنایی با تیترهای استنتاج‌شده، فهرست‌های شماره‌دار و بدون‌شماره و جدول‌های خط‌لوله‌ای بازمی‌گردانند، در حالی که SaveDOCXToFile و SaveDOCXToStream یک بسته WordprocessingML شامل پاراگراف‌ها، تیترها، شماره‌گذاری فهرست بومی، جدول‌های شناسایی‌شده، سبک‌دهی فونت، شکست صفحه و تصاویر PNG جای‌گذاری‌شده می‌نویسند

هر دو کاملاً در Pascal، روی یک سرور، بدون نصب Word و بدون COM اجرا می‌شوند. همین محدودیت دلیل وجود این قابلیت در یک کتابخانه PDF است، نه در یک ابزار دسکتاپ

چرا «تبدیل PDF به Word» واقعاً دشوار است؟

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

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

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

خروجی‌گیری Markdown، یک صفحه در هر بار

مسیر Markdown همان چیزی است که باید وقتی مقصد یک پایپ‌لاین متنی است سراغش بروید: یک سایت مستندات، یک نمایه جست‌وجو، یک پیکره بازیابی برای یک دستیار. گزینه‌ها یک بیت‌ماسک هستند: PDF_MARKDOWN_INCLUDE_PAGE_MARKERS، PDF_MARKDOWN_DETECT_HEADINGS، PDF_MARKDOWN_PRESERVE_STYLES، و PDF_MARKDOWN_DEFAULT که هر سه را ترکیب می‌کند

var
  Pdf: TPDFlib;
  Md: WideString;
begin
  Pdf := TPDFlib.Create;
  try
    Pdf.LoadFromFile('handbook.pdf', '');

    // یک صفحه، به‌صورت رشته
    Md := Pdf.ExportPageMarkdown(1, PDF_MARKDOWN_DEFAULT);

    // یک بازه صفحه، به‌صورت جریانی روی دیسک به‌صورت UTF-8 بدون BOM
    Pdf.SaveMarkdownToFile('1-40',
      PDF_MARKDOWN_DETECT_HEADINGS or PDF_MARKDOWN_PRESERVE_STYLES,
      'handbook.md');
  finally
    Pdf.Free;
  end;
end;

نشانگرهای صفحه در کار بازیابی ارزش خود را ثابت می‌کنند. تکه‌ای از متن که صفحه مبدأ خود را حمل می‌کند می‌تواند دقیقاً ارجاع داده شود، و خواننده‌ای که ارجاع را دنبال می‌کند دقیقاً جایی فرود می‌آید که ادعا واقعاً آنجاست. وقتی Markdown برای خواندن انسانی مقصد است، آن‌ها را خاموش کنید، جایی که مرزهای صفحه از چیدمان مبدأ صرفاً نویز هستند

نقاط ورودی جریانی برای اسناد بزرگ اهمیت دارند. SaveMarkdownToStream و SaveMarkdownToFile هر بار یک صفحه UTF-8 می‌نویسند و خروجی کامل را بافر نمی‌کنند، بنابراین یک راهنمای 900 صفحه‌ای ابتدا به یک رشته 900 صفحه‌ای در حافظه تبدیل نمی‌شود. نبود یک نشانه ترتیب بایت هم عمدی است: یک BOM روی یک فایل Markdown تعداد شگفت‌آوری از تولیدکننده‌های سایت استاتیک و ابزارهای diff را گیج می‌کند

DOCX بدون نصب Office روی ماشین

نویسنده DOCX خود بسته را تولید می‌کند: مدخل‌های ZIP نوشته‌شده به‌صورت Deflate خام با بررسی‌های CRC، بخش‌های WordprocessingML، و روابطی که آن‌ها را به هم متصل می‌کنند. هیچ‌چیز به Word فراخوانی نمی‌کند، که یعنی تبدیل روی یک سرور بدون رابط گرافیکی، درون یک حساب سرویس، درون یک کانتینر، در تمام جاهایی که اتوماسیون Office یا بدون مجوز، یا ناپایدار یا ممنوع است اجرا می‌شود

var
  Pdf: TPDFlib;
  Target: TFileStream;
begin
  Pdf := TPDFlib.Create;
  Target := TFileStream.Create('handbook.docx', fmCreate);
  try
    Pdf.LoadFromFile('handbook.pdf', '');
    Pdf.SaveDOCXToStream('1-40',
      PDF_DOCX_INCLUDE_IMAGES or PDF_DOCX_DETECT_HEADINGS or
      PDF_DOCX_PRESERVE_STYLES or PDF_DOCX_PRESERVE_PAGE_BREAKS,
      Target);
  finally
    Target.Free;
    Pdf.Free;
  end;
end;

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

بسته‌بندی قطعی چه چیزی به شما می‌دهد؟

بازتولیدپذیری بایت‌به‌بایت. دو تبدیل از یک ورودی یکسان با گزینه‌های یکسان، یک بسته یکسان تولید می‌کنند، که یعنی می‌توانید خروجی را هش کنید تا تغییر را تشخیص دهید، دو ساخت از یک سند تولیدشده را diff بگیرید، و به‌طور تهاجمی کش کنید بدون نگرانی از اینکه یک ورودی یکسان یک خروجی متفاوت تولید کرده باشد

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

کجا خروجی خوب است، و کجا نیست

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

اسناد اسکن‌شده کاملاً یک مورد جداگانه هستند. صفحه‌ای که یک تصویر بزرگ است هیچ شیء متنی ندارد، بنابراین تا زمانی که یک لایه متنی وجود نداشته باشد چیزی برای خروجی‌گیری نیست؛ مسیر OCR که چنین لایه‌ای تولید می‌کند یک پیش‌نیاز است نه یک گزینه. پیش از اجرای یک دسته بزرگ، حدود دوازده فایل نماینده را نمونه‌گیری کنید و به خروجی نگاه کنید، و شمارش عناصر صفحه را ابتدا، مطابق جست‌وجوی متن و شمارش عناصر صفحه، در نظر بگیرید تا ببینید صفحات واقعاً حاوی چه چیزی هستند

برای پایپ‌لاین‌های دستیار و بازیابی، مسیر Markdown معمولاً هدف بهتری است: تیترها به مرزهای تکه تبدیل می‌شوند، جدول‌ها به‌صورت جدول‌های خط‌لوله‌ای خوانا باقی می‌مانند، و نشانگرهای صفحه به هر تکه یک مکان قابل‌ارجاع می‌دهند. برای ویرایش انسانی، DOCX پاسخ است، چون آنچه کاربر می‌خواهد متن نیست بلکه توانایی تغییر آن است

PDFlibPas یک کتابخانه PDF برای Delphi، C++Builder و Lazarus با رابط‌های DLL و ActiveX متناظر است، بنابراین همان فراخوانی‌های خروجی‌گیری از C#، C++ یا میزبان‌های اسکریپتی نیز در دسترس‌اند. مستندات کامل و یک نسخه آزمایشی در صفحه کتابخانه PDF Delphi PDFlibPas موجود است