مقاله فنی

حاشیه‌نویسی‌های PDF در Delphi با HotPDF: انواع و مستطیل‌ها

حاشیه‌نویسی (annotation) محتوای صفحه نیست. هنگامی که TextOut را فراخوانی می‌کنید یا یک مستطیل رسم می‌کنید، علامت‌ها بخشی از جریان محتوای صفحه می‌شوند که در بایت‌هایی که یک رندرکننده (renderer) نقاشی می‌کند، پخته شده‌اند (baked). حاشیه‌نویسی یک فرهنگ لغت جداگانه است که از طریق آرایه /Annots خود با مستطیل، ظاهر و چرخه حیات خاص خود به صفحه متصل می‌شود. یک خواننده (reader) می‌تواند آن را باز کند، حرکت دهد، پنهان کند یا بدون لمس حتی یک گلیف از صفحه زیرین، آن را حذف کند. این جداسازی تمام دلیل وجود حاشیه‌نویسی‌ها است، و همچنین منبع دو چیزی است که در ابتدا افراد را شگفت‌زده می‌کند: جایی که حاشیه‌نویسی قرار می‌گیرد، و زمانی که یک نمایشگر خاص آن را به دست می‌گیرد، چگونه به نظر می‌رسد

HotPDF زیرگروه‌های حاشیه‌نویسی ISO 32000 را از طریق خانواده‌ای از فراخوانی‌های AddXxxAnnotation روی شیء صفحه (page object) در دسترس قرار می‌دهد. همه آن‌ها شکل یکسانی دارند: یک مستطیل که حاشیه‌نویسی را در صفحه در فضای کاربری PDF ثابت می‌کند، مقداری محموله (متن، نام مهر، یک جفت نقطه)، و یک رنگ. مستطیل را درست به دست آورید و بیشتر کار انجام شده است. بقیه موارد این است که بدانید کدام زیرگروه‌ها ظاهر خاص خود را دارند و کدام یک برای رسم آن‌ها به نمایشگر متکی هستند

A PDF page produced by HotPDF showing text note icons, free text boxes, square and line markups, and approval stamps placed across the page
یک صفحه که همزمان چندین زیرگروه حاشیه‌نویسی را حمل می‌کند: یادداشت‌های متنی، متن آزاد، نشانه‌گذاری‌های هندسی، و مهرها

مستطیل همان حاشیه‌نویسی است، نه متن

هر فراخوانی حاشیه‌نویسی یک TRect می‌گیرد، و آن مستطیل معنایی متفاوت از مختصاتی دارد که به TextOut پاس می‌دهید. برای یک یادداشت متنی، این نقطه حساس قابل کلیک (clickable hotspot) است، منطقه کوچکی که نماد یادداشت در آن قرار دارد و در آن کلیک کردن باعث باز شدن نظر (comment) می‌شود. برای یک مربع یا کادر متن آزاد (free text box)، وسعت قابل مشاهده نشانه‌گذاری (markup) است. برای یک مهر (stamp)، کادری است که هنر مهر (stamp art) در آن مقیاس‌بندی می‌شود. اعداد، نقاط فضای کاربری PDF هستند که از گوشه سمت چپ پایین صفحه با افزایش Y به سمت بالا اندازه‌گیری می‌شوند، همان قراردادی که بقیه HotPDF استفاده می‌کند

یک یادداشت متنی سبک‌ترین زیرگروه است. شما متن بدنه، یک مستطیل برای نماد (icon)، یک پرچم (flag) برای اینکه آیا به طور پیش‌فرض باز شود یا خیر، یک نام نماد، و یک رنگ را به آن می‌دهید

Pdf.CurrentPage.AddTextAnnotation(
  'Reviewer: confirm the totals on this line before sign-off.',
  Rect(120, 700, 140, 720),   // icon hotspot, ~20pt square
  False,                      // closed until the reader clicks it
  taComment,                  // bubble icon
  clBlue);

مستطیل در اینجا عمداً کوچک است، حدود بیست نقطه در هر طرف، زیرا یک یادداشت متنی فقط یک نماد است تا زمانی که کسی روی آن کلیک کند. مستطیل را بزرگ کنید و یک یادداشت بزرگ دریافت نخواهید کرد؛ شما یک هدف کلیک (click target) بسیار بزرگ با نمادی که در یک گوشه پین شده است دریافت می‌کنید. پرچم Open کنترل می‌کند که آیا هنگام بارگذاری سند پاپ‌آپ (popup) نمایش داده شود یا خیر. تعداد انگشت‌شماری از یادداشت‌ها را True تنظیم کنید و آن‌ها روی هم و در بالای محتوا روی هم چیده می‌شوند، بنابراین این را برای یادداشتی که واقعاً می‌خواهید خواننده فوراً ببیند، رزرو کنید

نام نماد از THPDFTextAnnotationType می‌آید، که به نمادهای یادداشت استاندارد نگاشت می‌شود: taComment، taKey، taNote، taHelp، taParagraph، taNewParagraph، و taInsert. نماد تنها چیزی است که نوع آن تغییر می‌کند. این رفتار را تغییر نمی‌دهد، و شایان ذکر است که هر نمایشگری هر هفت نماد را رسم نمی‌کند؛ نمادهای ایمن در میان خواننده‌های قدیمی و جدید taComment، taNote، و taHelp هستند

متن آزاد روی صفحه می‌نویسد، اما یک حاشیه‌نویسی باقی می‌ماند

یک حاشیه‌نویسی متن آزاد شبیه محتوا به نظر می‌رسد زیرا متن بدون کلیک قابل مشاهده است، و در مستطیل خود مانند یک کپشن (caption) قرار دارد. هنوز هم یک حاشیه‌نویسی است، با تمام جداسازی‌هایی که به همراه دارد، که دقیقاً همان چیزی است که برای مهر بررسی (review stamp) یا برچسب پیش‌نویس (draft label) که شخصی باید بتواند بعداً آن را حذف کند، می‌خواهید. امضا (signature) نماد و پرچم باز را با یک مقدار ترازسازی عوض می‌کند

Pdf.CurrentPage.AddFreeTextAnnotation(
  'DRAFT - not for distribution',
  Rect(200, 210, 400, 235),   // the box the text is laid into
  ftCenter,                   // ftLeftJust / ftCenter / ftRightJust
  clRed);

در اینجا مستطیل بیشتر از یک یادداشت متنی اهمیت دارد، زیرا متن در داخل آن می‌پیچد (wraps) و هم‌تراز می‌شود. اندازه جعبه را خیلی کوتاه کنید و متن در لبه پایین قطع می‌شود (clips)؛ خیلی باریک باشد و در جاهایی که قصد نداشتید می‌پیچد. ترازسازی از THPDFFreeTextAnnotationJust می‌آید و فقط سه مقدار دارد. از آنجا که متن آزاد یک حاشیه‌نویسی نشانه‌گذاری است، خواننده‌ای که فایل را در یک ویرایشگر باز می‌کند می‌تواند آن را انتخاب کند، حرکت دهد، یا آن را به عنوان یک واحد حذف کند، که تفاوتی است که تصمیم می‌گیرد آیا شما به سراغ متن آزاد می‌روید یا فقط کلمات را با TextOut رسم می‌کنید. اگر برچسب باید دائمی باشد، آن را رسم کنید. اگر ویراستاری (editorial) است و قرار است حذف شود، آن را یک حاشیه‌نویسی کنید

نشانه‌گذاری‌های هندسی و خط برای اشاره به چیزها

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

// A box drawn around a figure that needs attention
Pdf.CurrentPage.AddCircleSquareAnnotation(
  'Check this region against the source data',
  Rect(50, 300, 120, 360),
  csSquare,
  clGreen);

// A line, given two points rather than a rectangle
var
  StartPt, EndPt: THPDFCurrPoint;
begin
  StartPt.X := 130; StartPt.Y := 360;
  EndPt.X   := 250; EndPt.Y   := 320;
  Pdf.CurrentPage.AddLineAnnotation(
    'Points from the note to the figure',
    StartPt, EndPt,
    clBlue);
end;

توجه داشته باشید که حاشیه‌نویسی خط، الگوی مستطیل را می‌شکند: آن دو رکورد THPDFCurrPoint را می‌گیرد، یک شروع و یک پایان، زیرا یک خط با نقاط پایانی آن تعریف می‌شود، نه با یک کادر مرزی. رنگ، ضربه (stroke) را تنظیم می‌کند. اگر سرنیزه (arrowheads) می‌خواهید، HotPDF دارای اضافه‌بار (overloads) AddLineAnnotation است که سبک‌های پایان خط را می‌پذیرد، اما فرم ساده سه آرگومانی یک خط بدون علامت رسم می‌کند، که معمولاً همان چیزی است که یک خط فراخوانی (callout) می‌خواهد

زیرگروه‌های نشانه‌گذاری متن روی منطقه‌ای کار می‌کنند که قبلاً چیدمان کرده‌اید. AddHighlightAnnotation یک مستطیل، محتوای اختیاری، و رنگی که به طور پیش‌فرض زرد است را می‌گیرد، و منطقه را به همان روشی که قلم نشانگر (highlighter) رنگ می‌کند، رنگ‌آمیزی می‌کند. این برای قرار گرفتن روی متن واقعی طراحی شده است، بنابراین مستطیل باید با مرز کلماتی که رسم کرده‌اید مطابقت داشته باشد، به این معنی که عموماً آن را از همان مختصاتی که به TextOut پاس داده‌اید محاسبه می‌کنید تا اینکه حدس بزنید

مهرها برای رندر شدن به نمایشگر متکی هستند

حاشیه‌نویسی مهر، به احتمال زیاد حاشیه‌نویسی‌ای است که از یک خواننده به خواننده دیگر متفاوت به نظر می‌رسد، و ارزش درک دلیل آن را دارد. AddStampAnnotation یک مهر استاندارد را از طریق THPDFStampAnnotationType با مقادیری مانند satApproved، satConfidential، satFinal، satDraft، و satForComment نام می‌برد

Pdf.CurrentPage.AddStampAnnotation(
  'Approved for release on review',
  Rect(50, 400, 200, 440),
  satApproved,
  clGreen);

نام مهر یک درخواست است. PDF مجموعه‌ای از نام‌های مهر استاندارد را تعریف می‌کند اما اثر هنری پشت آن‌ها را تعریف نمی‌کند، بنابراین هر نمایشگری رندر خاص خود را از "APPROVED" یا "CONFIDENTIAL" ارائه می‌دهد، و تعداد کمی برای نام‌هایی که نمی‌شناسند اصلاً چیزی را رندر نمی‌کنند. مستطیل کادری را کنترل می‌کند که هنر در آن مقیاس‌بندی می‌شود، و رنگ یک راهنمایی است که ممکن است نمایشگر آن را رعایت کند یا نکند. اگر یک مهر باید در همه جا یکسان به نظر برسد، مسیر قابل اعتماد به هیچ وجه یک مهر استاندارد نیست: علامت را خودتان با TextOut و فراخوانی‌های رسم بکشید، یا آن را به عنوان یک حاشیه‌نویسی متن آزاد که ظاهر آن را کنترل می‌کنید قرار دهید. زمانی که به ظاهر آشنای نمایشگر نیاز دارید و می‌توانید با تغییرات کنار بیایید، به سراغ مهر استاندارد بروید

پیوست‌های فایل از همان شکل مستطیل به علاوه محموله پیروی می‌کنند. AddFileAttachmentAnnotation توضیحات، مسیر فایل برای جاسازی، یک مستطیل برای نماد گیره کاغذ (paperclip)، و یک رنگ را می‌گیرد. فایل درون PDF قرار می‌گیرد، و نماد دسته‌ای است که یک خواننده برای استخراج آن استفاده می‌کند

حاشیه‌نویسی‌ها چگونه با فیلدهای AcroForm تفاوت دارند

سردرگمی که بیشترین زمان را می‌گیرد، رفتار با حاشیه‌نویسی به گونه‌ای است که گویی یک فیلد فرم است. هر دو از طریق /Annots به صفحه متصل می‌شوند، و یک فیلد فرم در واقع یک زیرگروه حاشیه‌نویسی ویژه (یک ویجت) است، که به همین دلیل آن‌ها مرتبط به نظر می‌رسند. آن‌ها قابل تعویض نیستند. یک فیلد فرم دارای یک مقدار است، نام دارد، در ترتیب تب (tab order) شرکت می‌کند، و می‌تواند ارسال (submitted)، بازنشانی (reset) یا اسکریپت شود؛ شما آن‌ها را با فراخوانی‌های AddTextField، AddCheckBox، و AddPushButton ایجاد می‌کنید، نه فراخوانی‌های حاشیه‌نویسی در این صفحه. یک حاشیه‌نویسی نشانه‌گذاری دارای نظر یا شکل است، مقداری برای ارسال ندارد، و لحظه‌ای که نیاز به جمع‌آوری ورودی دارید، ابزار اشتباهی است

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

قرار دادن یک صفحه در کنار هم

قطعات به همان روشی که بقیه HotPDF انجام می‌دهد ترکیب می‌شوند. ویژگی‌های سند را تنظیم کنید، BeginDoc را فراخوانی کنید، هر محتوای صفحه‌ای را که نیاز دارید با فراخوانی‌های متنی و گرافیکی رسم کنید، حاشیه‌نویسی‌ها را در بالا اضافه کنید، و با EndDoc ببندید. حاشیه‌نویسی‌ها به CurrentPage متصل می‌شوند، بنابراین پس از یک AddPage آن‌ها روی صفحه جدید قرار می‌گیرند، و یادداشتی که برای صفحه یک در نظر گرفته‌اید، در صورت اضافه کردن پس از شکست (break)، بی‌سروصدا در صفحه دو ظاهر می‌شود

Pdf := THotPDF.Create(nil);
try
  Pdf.FileName := 'annotated.pdf';
  Pdf.Compression := cmFlateDecode;
  Pdf.FontEmbedding := True;
  Pdf.BeginDoc;

  Pdf.CurrentPage.SetFont('Arial', [], 11);
  Pdf.CurrentPage.TextOut(50, 740, 0, 'Quarterly figures, draft for review');

  Pdf.CurrentPage.AddTextAnnotation(
    'Confirm the totals before sign-off.',
    Rect(50, 720, 70, 740), False, taComment, clBlue);
  Pdf.CurrentPage.AddFreeTextAnnotation(
    'DRAFT', Rect(450, 720, 540, 745), ftCenter, clRed);
  Pdf.CurrentPage.AddStampAnnotation(
    'For comment', Rect(50, 660, 180, 695), satForComment, clGreen);

  Pdf.EndDoc;
finally
  Pdf.Free;
end;

یک عکس‌العمل (reflex) نهایی که وقتی خروجی اشتباه به نظر می‌رسد ارزش ایجاد کردن دارد: قبل از اینکه تصمیم بگیرید کد خراب است، فایل را در بیش از یک نمایشگر باز کنید. مهرها و نمادهای یادداشت نادرتر مقصران معمول هستند، و از آنجا که حاشیه‌نویسی به جای پیکسل‌های نقاشی شده، درخواستی از خواننده است، تفاوت بین Acrobat و یک نمایشگر سبک وزن اغلب مشخصاتی (spec) است که طبق طراحی کار می‌کند، نه باگی در فراخوانی شما

فراخوانی‌های حاشیه‌نویسی نشان داده شده در اینجا بخشی از کامپوننت HotPDF برای Delphi و C++Builder هستند