PDFlibPas rozmieszcza zachowany fragment tekstu w od jednej do 64 kolumn o równej szerokości za pomocą DrawTextFlowColumns i łamie jego wiersze przy użyciu ograniczonego dzielenia wyrazów zależnego od języka, gdy tylko wywołasz SetTextFlowLanguage i SetTextFlowHyphenation. Obsługiwanych jest dziewięć języków, a język można odziedziczyć z wartości /Lang katalogu dokumentu zamiast ustawiać go osobno dla każdego przepływu
Obie funkcje istnieją z tego samego powodu: wąska kolumna to miejsce, w którym naiwne łamanie wierszy przestaje wyglądać jak skład i zaczyna wyglądać jak zgłoszenie błędu
Dlaczego tekst wyjustowany rozpada się w wąskich kolumnach?
Ponieważ justowanie rozprowadza pozostałą przestrzeń w odstępy między wyrazami w wierszu, a ilość tej przestrzeni zależy od tego, co się zmieści. Przy szerokiej kolumnie nadmiar jest niewielki i oko go nie zauważa. Zmniejsz szerokość o połowę, a jedno długie słowo, które się nie zmieści, przechodzi do następnego wiersza, zostawiając poprzednie słowa do wchłonięcia całej tej przestrzeni. Trzy takie wiersze pod rząd tworzą pionowe białe kanały, które typografowie nazywają rzekami, a czytelnicy odbierają jako tekst trudny do śledzenia, nie wiedząc dlaczego
Dzielenie wyrazów usuwa przyczynę zamiast objawu, pozwalając na złamanie wewnątrz słowa. Niemieckie i niderlandzkie złożenia sprawiają, że jest to nieodzowne: 24-znakowy rzeczownik w 60-milimetrowej kolumnie nie ma dobrego rozwiązania bez punktu podziału. Angielski toleruje brak dzielenia lepiej, dlatego produkty projektowane najpierw pod język angielski często wysyłają kod układu, który wywraca się przy pierwszym niemieckim kliencie
Które języki i skąd bierze się język?
Dzielenie wyrazów obejmuje angielski, niemiecki, niderlandzki, francuski, hiszpański, włoski, portugalski, rosyjski i turecki. Ustaw go jawnie dla przepływu przez SetTextFlowLanguage albo pozwól, aby został odziedziczony z wpisu /Lang katalogu dokumentu, czyli wartości, którą oznaczony i dostępny dokument już posiada
To dziedziczenie warto wykorzystać zamiast je nadpisywać. Dokument, który deklaruje swój język w katalogu, przekazuje ten sam fakt czytnikom ekranu, indeksującym wyszukiwarkom i mechanizmowi dzielenia wyrazów z jednego miejsca, a jedno miejsce to właśnie tam, gdzie fakt powinien mieszkać. Jeśli już generujesz oznaczone wyjście opisane w automatycznym tagowaniu dla dostępnych plików PDF, wpis języka jest już ustawiony, a przepływ może po prostu z niego skorzystać
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');
// Włączone, co najmniej 3 znaki przed podziałem, 3 po
Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);
Lib.SetTextFlowMinLines(Flow, 2); // nigdy nie zostawiaj osamotnionego wiersza
repeat
// Trzy kolumny w obszarze 480 pt, 18 pt marginesu wewnętrznego, wyważone
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 i MinSuffix to typografia, nie walidacja
Dwie liczby całkowite po fladze włączającej ustawiają minimalną liczbę znaków, które muszą pozostać przed i po podziale. Trzy i trzy to konserwatywna wartość domyślna akceptowana przez większość reguł firmowych. Dwa i dwa daje więcej możliwości podziału i zauważalnie brzydsze rezultaty, ponieważ dwuliterowy fragment zwisający na końcu wiersza czyta się jak literówka
Podnoś minimalne wartości, gdy rozmiar czcionki jest duży, bo wtedy każdy fragment jest wizualnie wyeksponowany, a obniżaj je tylko wtedy, gdy kolumna jest naprawdę wąska i zdecydowałeś, że ciasny skład liczy się bardziej niż czysty. To decyzja stylu firmowego, a nie techniczna, i właśnie dlatego jest to parametr, a nie stała
Co właściwie oznacza tu „wyważenie”?
Parametr Balance zmienia zachowanie tylko na końcu fragmentu. Przy włączonym wyważaniu kolumny są skracane do dokładnie równej liczby wierszy, gdy wszystko, co pozostało, mieści się w obszarze, co właśnie zapobiega sytuacji, w której ostatnia strona pokazuje dwie pełne kolumny i trzecią z jednym osamotnionym wierszem. Gdy fragment się nie mieści, każda kolumna zachowuje pełną wysokość, więc strona niesie tyle tekstu, ile może, a reszta trafia na kolejną stronę
Ta asymetria jest właściwym ustawieniem domyślnym dla dokumentów ciągłych. Wyważanie w środku płynącego artykułu marnowałoby przestrzeń pionową na każdej stronie dla kosmetycznego efektu, którego nikt nie widzi, ponieważ kolumny i tak są pełne. Wyważanie na końcu to miejsce, gdzie oko rzeczywiście to zauważa, i właśnie tam ma ono zastosowanie
Łamanie wierszy mierzy całe słowa
Algorytm łamania mierzy kompletne słowa zamiast sumować szerokości znaków i rezerwuje ograniczone przeszukiwanie dla nadmiarowo dużych tokenów, które w ogóle nie mieszczą się w wierszu, takich jak adres URL czy numer referencyjny. Dzięki temu typowy przypadek pozostaje szybki, a przypadek patologiczny ograniczony, a nie odwrotnie
Miękkie łączniki opcjonalne i automatyczne łączniki są rysowane tylko wtedy, gdy oznaczony przez nie podział jest tym, który faktycznie zostaje wybrany. Brzmi to oczywiście, a jest klasycznym błędem: naiwna implementacja zapisuje znak łącznika już podczas mierzenia, a jeśli podział się przesunie, łącznik zostaje w środku wiersza. Nic nie wygląda bardziej jak zepsuty silnik tekstowy niż zabłąkany łącznik w środku słowa
var
Lib: TPDFlib;
Flow, Needed: Integer;
begin
// Zdecyduj o układzie, zanim cokolwiek narysujesz
Flow := Lib.NewTextFlow(ArticleBody);
try
Lib.SetTextFlowLanguage(Flow, 'fr');
Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);
// Liczba wierszy potrzebnych na resztę fragmentu przy szerokości jednej kolumny
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;
Zachowaj identyczne ustawienia czcionki we wszystkich polach
Jedna zasada rządzi każdym układem opartym na przepływie i warto powiedzieć to wprost: DrawTextFlow, DrawTextFlowColumns i MeasureTextFlow łamią wiersze, używając czcionki wybranej w chwili ich wywołania. Zmień czcionkę lub rozmiar między dwoma polami tego samego przepływu albo rozpocznij nową stronę bez ponownego wyboru czcionki, a drugie pole złamie się inaczej, niż wynikało z pomiaru pierwszego
Objaw jest wyjątkowo irytujący właśnie dlatego, że wygląda na przerywany: tekst, który mieści się na pierwszej stronie, wylewa się na drugiej, albo zmierzona liczba wierszy nie zgadza się z tym, co zostało narysowane. Wybierz czcionkę raz przed pętlą, wybierz ją ponownie po każdym NewPage, a przepływ będzie zachowywał się poprawnie. Gdy w tym samym fragmencie pojawiają się mieszane pisma, rozwiązanie opisane w automatycznym fallbacku czcionek dla tekstu CJK i emoji ma zastosowanie zarówno do pomiaru, jak i do rysowania, więc szerokości pozostają spójne również w przebiegach fallbacku
Dla układów raportów, gdzie przepływ jest jednym elementem obok nagłówków, stopek i bloków sterowanych danymi, wzorce kompozycji z silnika raportów opartego na zbiorze danych łączą się bez problemu z kolumnami przepływu: najpierw zmierz, umieść stały element strony, a potem daj przepływowi cały pozostały obszar
PDFlibPas to biblioteka PDF dla Delphi, C++Builder i Lazarus, a pełny cykl życia TextFlow, czyli tworzenie, rysowanie, pomiar, inspekcję, przewijanie wstecz i zwalnianie, jest udostępniony również przez interfejsy DLL i ActiveX. Pełna dokumentacja znajduje się na stronie biblioteki PDFlibPas dla Delphi