مقاله فنی

هایفن‌گذاری و ستون‌های متوازن متن در PDFlibPas Delphi

PDFlibPas یک قطعه متن نگه‌داشته‌شده را با DrawTextFlowColumns در یک تا 64 ستون هم‌عرض جاری می‌کند، و با فراخوانی SetTextFlowLanguage و SetTextFlowHyphenation خطوط آن را با هایفن‌گذاری زبان‌آگاه و محدود می‌شکند. نه زبان پشتیبانی می‌شود، و به‌جای تنظیم زبان برای هر جریان، می‌توان آن را از مقدار /Lang در Catalog سند به ارث برد

هر دو ویژگی به یک دلیل وجود دارند: ستون باریک جایی است که شکستن ساده‌لوحانه خطوط دیگر شبیه حروفچینی به نظر نمی‌رسد و بیشتر شبیه یک گزارش باگ می‌شود

چرا متن هم‌تراز در ستون‌های باریک از هم می‌پاشد؟

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

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

کدام زبان‌ها، و زبان از کجا تعیین می‌شود؟

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

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

var
  Lib: TPDFlib;
  Flow, Drawn: Integer;
begin
  Lib := TPDFlib.Create;
  try
    Lib.SetOrigin(1);
    Lib.AddTrueTypeFont('Georgia', 1);
    Lib.SetTextSize(10.5);

    Flow := Lib.NewTextFlow(ArticleBody);
    try
      Lib.SetTextFlowLanguage(Flow, 'de');
      // فعال‌سازی، حداقل 3 کاراکتر پیش از شکست، 3 کاراکتر پس از آن
      Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);
      Lib.SetTextFlowMinLines(Flow, 2);   // هرگز یک خط تنها را رها نکن

      repeat
        // سه ستون در یک ناحیه 480 پوینتی، شیارهای 18 پوینتی، متوازن
        Drawn := Lib.DrawTextFlowColumns(Flow, 72, 720, 480, 620, 3, 18, 1);
        if (Drawn = 0) or (Lib.TextFlowFinished(Flow) = 1) then
          Break;
        Lib.NewPage;
      until False;
    finally
      Lib.ReleaseTextFlow(Flow);
    end;

    Lib.SaveToFile('newsletter.pdf');
  finally
    Lib.Free;
  end;
end;

MinPrefix و MinSuffix مسئله حروفچینی هستند، نه اعتبارسنجی

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

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

منظور از «متوازن» در اینجا واقعاً چیست؟

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

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

شکستن خط کلمات کامل را اندازه می‌گیرد

الگوریتم شکستن به‌جای انباشتن عرض کاراکترها، کلمات کامل را اندازه می‌گیرد، و یک جست‌وجوی محدود را برای توکن‌های بیش‌ازحد بزرگی که اصلاً در یک خط جا نمی‌شوند، مثل یک URL یا یک شماره پرونده، رزرو می‌کند. همین است که حالت متداول را سریع و حالت بیمارگونه را محدود نگه می‌دارد، نه برعکس

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

var
  Lib: TPDFlib;
  Flow, Needed: Integer;
begin
  // چیدمان را پیش از رسم هرچیزی تعیین کن
  Flow := Lib.NewTextFlow(ArticleBody);
  try
    Lib.SetTextFlowLanguage(Flow, 'fr');
    Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);

    // تعداد خطوطی که باقی قطعه متن در عرض یک ستون نیاز دارد
    Needed := Lib.MeasureTextFlow(Flow, 148);
    if Needed > 3 * LinesPerColumn then
      UseTwoPageSpread
    else
      UseSinglePage;

    Lib.DrawTextFlowColumns(Flow, 72, 720, 480, 620, 3, 18, 1);

    if Lib.TextFlowFinished(Flow) <> 1 then
      CarryOver(Lib.GetTextFlowRemaining(Flow));
  finally
    Lib.ReleaseTextFlow(Flow);
  end;
end;

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

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

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

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

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