1094 - Czcionkami Pisane cz.2

Czcionki typu DefaultFont nie zachwycają swym pięknem. Na szczęście procedura OutText pozwala prezentować tekst w grafice w bardziej efektownej postaci za pomocą czcionek wektorowych. Cóż to takiego czcionki wektorowe? Są to czcionki, których nie tworzy się poprzez wyświetlenie ich w postaci siatki punktów (matrycy), lecz przez narysowanie jako grupy linii. Taki typ reprezentacji pozwala na bardziej finezyjny sposób ich skalowania. Powiększając literę uzyskujemy lepszy obraz jej struktury, nie zaś ogromne, toporne i kanciaste kształty, jak w wypadku czcionek rastrowych (do takiego bowiem typu czcionek należy zaliczyć DefaultFont).

Poczynając od wersji 5.5 Turbo Pascala w środowisku graficznym korzystać mozna z 9 rodzajów czcionek wektorowych. Ich definicje znajdują się w zbiorach dyskowych o rozszerzeniu *.CHR. Oto lista tych zbiorów w kolejności odpowiadającej ich numerom identyfikacyjnym, rozpoznawanym przez procedurę SetTextStyle :

  • TRIP.CHR,
  • LITT.CHR,
  • SANS.CHR,
  • GOTH.CHR,
  • SCRI.CHR (pismo ręczne),
  • SIMP.CHR,
  • TSCR.CHR (pochylony TRIP),
  • LCOM.CHR,
  • EURO.CHR

Pierwsza kolumna to czcionki, które są zapewne wszystkim znane, gdyż zdefiniowano stałe określające ich numery identyfikacyjne i są one związane z Turbo Pascalem od dawna. W drugiej kolumnie wyszczególnione są zbiory, które pojawiły się jako predefiniowane od wersji 5.5 Turbo Pascala. Nie miały one swoich stałych identyfikacyjnych, ale można było zainstalować je bez użycia procedury InstallUserFont, podając odpowiadające im numery. Nie były jednak dostarczane razem z pakietem. Dopiero od wersji 7.0 Turbo Pascala czcionki te są udostępniane razem z pakietem, uzupełnione dodatkowym zbiorem o nazwie BOLD.CHR.

Firma Borland zaczęła też rozprowadzać - niezależnie od pakietu Turbo Pascala - program do edycji czcionek wektorowych o nazwie FE.EXE. Pozwalał on tworzyć swoje definicje czcionek i używać ich później we własnych programach za pomocą procedury InstallUserFont. Do pracy wymagał karty graficznej EGA lub lepszej oraz myszy. Nie działał niestety na Herculesie. Program posiadał bardzo rozbudowany zestaw narzędzi do tworzenia czcionek, m.in. możliwość kopiowania pojedynczych znaków i ich fragmentów, przenoszenie czcionek między zbiorami. Wspomniałem o tym programie, gdyż pewne konwencje przyjęte w opisywaniu zbioru czcionek wektorowych pochodzą od niego. Zapewne każdy domyśla się, iż jestem posiadaczem takiego edytora. Niestety, nie mogę przesłać go do PCKuriera, gdyż nie jest to program do dystrybucji shareware'owej (jest to wyraźnie zaznaczone w dokumentacji). Być może istnieje jakaś wersja shareware'owa, warto poszukać w sieci Internet. Najlepiej zaś, gdyby któryś z czytelników pokusił się o napisanie takiego programu na podstawie informacji, które zamieszczę poniżej.

To, co charakteryzuje każdy znak to jego szerokość i wysokość. Szerokość określa, ile punktów na ekranie zajmuje znak. W wypadku czcionki rastrowej była ona stała dla wszystkich znaków, tutaj może być inna dla każdego znaku i nie musi być równa faktycznej szerokości znaku. Natomiast wysokość jest ustalona dla wszystkich znaków jednakowo. Dla określania szerokości potrzebna jest więc tablica równa liczbie znaków w danym zbiorze; wysokość określa jedna liczba. Mimo to, w zbiorze znajdują się trzy liczby związane z wysokością znaku. Dlaczego aż trzy ? By to sobie wyjaśnić, należy sięgnąć pamięcią do pierwszej klasy szkoły podstawowej. Zapewne każdy pamięta zeszyty w tzw. "cienką" linię, które w znacznej mierze ułatwiały naukę pisania. Tę samą rolę pełnią w zbiorach *.CHR owe trzy liczby. Jeśli ustalimy dla naszego znaku jakąś linię (nazwijmy ją linią oryginalną), to pierwsza liczba - określa odległość najwyższego elementu znaku od tej linii (np. odległość kreski nad Ć), druga - odległość podstawy znaku od linii oryginalnej (najczęściej jest równa zero), trzecia - odległość od linii oryginalnej ogonków liter (np. w znakacg p,q,y). Jeśli powrócimy do naszego zeszytu z pierwszej klasy, to liczba pierwsza określa nam górną linię, liczba druga linię dolną, liczba trzecia odległość pomiędzy grupami trzech linii. A co z linią środkową? Jej pozycja jest przyjmowana jako połowa wartości pierwszej liczby, czyli jak wskazuje nazwa jest umieszczana na środku.

Czy wartości tych linii są stosowane przez procedurę OutText? Okazuje się, że nie. Liczona jest tylko różnica między linią trzecią i linią pierwszą, która jest później traktowana jako wysokość znaków z danego zbioru. Znaki zawsze są wyświetlane względem podanej pozycji tak samo niezależnie od wartości owych trzech linii, a położenie względem współrzędnych można zmienić jedynie procedurą SetTextJustify.

Po co więc wprowadzono taką reprezentację wysokości znaku? Wartości tych linii są wykorzystywane przez program do edycji czcionek FE. Dzięki temu zbędne staje się ustawianie ich pozycji dla każdego zbioru czcionek, pobieranego z dysku. Konieczność taka dotyczy jedynie nowo tworzonego zbioru.

Czytelnikom, którzy chcieliby napisać edytor czcionek, przydać się może jeszcze kilka informacji właśnie na temat owych trzech, a właściwie czterech linii. W omawianym edytorze, by zabezpieczyć wielkość czcionek przed absurdalną wartością, przyjęto następujące konwencje. Wartość linii górnej znaku (nazywanej w programie linią "Capital") jest zawsze sprowadzana podczas nagrywania zbioru na dysk do wysokości znaku #144("Ę"), będącego w tabeli ASCII największą literą, lub - gdy nie została ona zdefiniowana - do wysokości litery "M". Linia dolna (o nazwie "Descender") jest zawsze nie mniejsza niż najniższy punkt ogonka litery "q". Linia bazowa znaku (nazywana "Base") jest zawsze ustawiana jako zero. Linia środkowa (nazywana "X- line") jest określana - jak już wspomniałem powyżej - jako połowa odległości między linią górną a bazową. Jeśli jednak jest zdefiniowana czcionka odpowiadająca literze "x", to wartość owej linii jest przyjmowana jako wysokość tej czcionki. W przypadku pierwszych dwóch konwencji bardziej uzasadnione wydawałoby się poszukanie w zbiorze czcionek najwyższej oraz najniższej i przyjęcie ich wartości jako granic. Byłoby to przydatne szczególnie w wypadku definiowania polskich liter, które nie figurują w tabeli ASCII, a są większe poprzez swe ogonki od litery "M" oraz "Ę".

Na początku zbioru czcionek wektorowych znajduje się jego nagłówek, zawierający ogólne informacje na temat zawartości i przeznaczenia zbioru.

Pierwszym elementem nagłówka jest tekst informacyjny (może mieć zmienną długość w zależności od wersji Turbo Pascala, dla którego były przeznaczone zbiory czcionek), w którym zazwyczaj podana jest wersja czcionek, data, od której wersja obowiązuje, producent. Na początku tekstu umieszczony jest łańcuch 'PK', będący znacznikiem służącym modułowi Graph do rozpoznawania, czy dany zbiór z rozszerzeniem .CHR jest rzeczywiście zbiorem czcionek wektorowych. Oto jego przykładowa postać:

  'PK'#8#8'BGI Stroked Font V1.1 - Jul 11, 1988'#13#10 
  'Copyright (c) 1987,1988 Borland International'#13#10#26

W tekście umieszczone są znaki specjalne (wyszczególnione powyżej znakiem #) o następującym znaczeniu:

  • #8 - użyty w tekście jest interpretowany przez system DOS jako polecenie cofnięcia kursora o jedną pozycję w lewo;
  • #13 - jest interpretowany przez DOS jako polecenie przeniesienia kursora do pierwszej kolumny ekranu;
  • #10 - jest interpretowany przez DOS jako polecenie przeniesienia kursora do następnej linii ekranu;
  • #26 - oznacza dla systemu operacyjnego DOS koniec zbioru tekstowego.

Jaki jest sens użycia tych znaków w zbiorze z definicjami czcionek ? Jeśli spróbuje się wyświetlić poleceniem TYPE zawartości jednego z takich zbiorów, to na ekranie pojawi się tylko informacja zapisana na jego początku, gdyż dalszą część, nie będącą tekstem, poprzedza znak końca zbioru. Taka organizacja nagłówka pozwala na wykorzystanie zbioru .CHR zgodnie z jego przeznaczeniem, a jednocześnie - w wypadku potraktowania go jako zbiór tekstowy - otrzymujemy informację co do roli, jaką pełni zbiór z rozszerzeniem .CHR, z pominięciem pozostałej części zbioru, której nie można interpretować jako tekst. Jest to dość często stosowany zabieg - szczególnie w zbiorach z rozszerzeniem .COM - które można uruchomić lub - w wypadku użycia polecenia TYPE - wyświetlić informację o zadaniach, jakie wykonują. Dwukrotne użycie znaku #8 w tekście ma na celu zamazanie podczas wyświetlania zawartości zbioru znacznika 'PK' poprzez cofnięcie kursora o dwie pozycje do tyłu. Znak końca zbioru #26 określa koniec części informacyjnej nagłówka.

Kolejnym elementem nagłówka jest dana dwubajtowa, znajdująca się zaraz za znakiem #26 i określająca jego rozmiar; jej wartość to zazwyczaj $80 (128). Potem znajduje się czterobajtowe pole zawierające pierwsze cztery litery z nazwy zbioru, a następnie dana dwubajtowa określająca rozmiar zbioru danych minus rozmiar nagłówka. Za tymi danymi ulokowane są dwa pola dwubajtowe zawierające odpowiednio: numer wersji czcionek dla tego zbioru oraz minimalną, istniejącą wersję czcionek, jaka powstała. Wersje są zapisywane w ten sposób, że część przed przecinkiem zapisana jest na pierwszym bajcie, część po przecinku na drugim. Pozostała część nagłówka jest wypełniona zerami.

Poczynając od pozycji $80 (128) zbioru czcionek, rozpoczyna się blok danych, w którym wyróżniamy część zawierającą ogólne informacje dotyczące danego typu czcionek i części odpowiedzialne za poszczególne znaki zbioru. Ponieważ struktura bloku danych jest statyczna w przeciwieństwie do nagłówka, będę podawał konkretne przesunięcie względem początku zbioru dla odpowiednich pól:

  • $80'+' - znacznik bloku danych zbioru czcionek,
  • $81-$82 - ilość znaków w zbiorze (n),
  • $83 - niezdefiniowane,
  • $84 - numer pierwszego zdefiniowanego znaku w zbiorze,
  • $85-$86 - przesunięcie początku bloku definicji czcionek względem początku bloku danych. By otrzymać przesunięcie względem początku zbioru należy do tej wartości dodać 128. Przesunięcie może być też wyliczone z zależności 144+3n ($90+3n), gdzie n oznacza liczbę znaków w zbiorze (patrz wyżej).
  • $87 - znacznik przeszukiwania (normalnie równy 0),
  • $88 - odległość linii górnej od oryginalnej,
  • $89 - odległość linii bazowej od oryginalnej,
  • $8A - odległość linii dolnej od oryginalnej,
  • $8B-$8F - niezdefiniowane,

Na tym adresie kończy się statyczny obszar bloku danych. Dalsze jego elementy mają wielkość zależną od liczby znaków zdefiniowanych w zbiorze.

  • $90 - od tego miejsca zaczyna się tablica, określająca przesunięcie definicji poszczególnych znaków zbioru względem początku bloku tychże definicji. Na pierwszym miejscu znajduje się przesunięcie znaku określonego przez pole $84 bloku danych, na miejscu kolejnym - znaku o kodzie ASCII większym o jeden itd. Tablica zawiera n pozycji określonych przez pole $85-$86. Ponieważ przesunięcie jest reprezentowane przez daną typu Word, więc tablica zajmuje 2n bajtów.
  • $90+2n - od tego miejsca zaczyna się tablica określająca szerokość poszczególnych znaków. Indeksowana jest ona podobnie jak tablica poprzedzająca ją, z tym, że szerokość znaku jest reprezentowana przez daną typu Byte, a zatem zajmuje ona n bajtów.
  • $90+3n - jest to ostatnia część bloku danych zawierająca indywidualne definicje poszczególnych znaków, których postać zostanie omówiona poniżej.

Indywidualne definicje znaków składają się ze zmiennej liczby par bajtów, opisujących operacje i ich argumenty potrzebne do utworzenia znaku. Para taka określa współrzędne (x,y) i dwubitowy kod operacji. Najstarsze bity obu bajtów określają razem, jaką operację należy wykonać za pomocą współrzędnych opisanych na pozostałych bitach pary. Poniżej przedstawiam sposób kodowania informacji na obu bajtach i listę operacji wraz z ich binarną reprezentacją.

Bajt 1	7	6	5	4	3	2	1	0	<- nr bitu

	op1	(			współrzędna X			)	

Bajt 2	7	6	5	4	3	2	1	0	<- nr bitu
            
	op2	(			współrzędna Y			)	

Kody operacji:

op1=0	op2=0	koniec definicji znaku,
        
op1=1	op2=0	przesuń punkt do (x,y),
        
op1=1	op2=1	rysuj z bieżącego punktu do (x,y).

Obie współrzędne są liczbami ze znakiem. Turbo Pascal nie ma standardowego typu całkowitego, który reprezentowałby liczbę ze znakiem na siedmiu bitach (typ ShortInt używa ośmiu bitów). Należy więc tak przetransformować reprezentowaną w ten sposób współrzędną, by otrzymać liczbę ze znakiem widzianą przez TP. Fakt reprezentowania danej ze znakiem na siedmiu bitach ogranicza jej zakres wartości do przedziału - 64...63. Wynika stąd od razu ograniczenie dla naszych trzech linii charakteryzujących znak. Przyporządkowane im odległości nie mogą wykraczać poza ten przedział. Bez przeszkód mogą być jednak reprezentowane na zmiennej typu ShortInt. Jak przetransformować daną całkowitą z reprezentacji siedmiobitowej na ośmiobitową? Ja realizuję to w następujący sposób :

 I := ShortInt(Dana Shl 1) Div 2; 

Zmienna I jest typu całkowitego, zmienna Dana - jednobajtowa - może być dowolnego typu. Przesunięcie w lewo o jedną pozycję bitów zmiennej Dana powoduje zapalenie bitu ósmego, który, w wyniku konwersji na typ ShortInt, interpretowany jest jako bit znaku. Ponieważ operacja przesuwania bitów zmiennej w lewo powoduje równocześnie pomnożenie jej wartości przez 2, koniecznym staje się podzielenie tej danej po zmianie typu przez 2 (stąd operacja dzielenia całkowitego).

Oprócz indywidualnych definicji znaków w bloku danych znajdują się jeszcze dwie tablice, o których już wspominałem powyżej. Jako pierwsza występuje tablica przesunięć definicji znaków. Na pierwszej pozycji tablicy znajduje się przesunięcie pierwszego zdefiniowanego znaku w zbiorze, na pozycji ostatniej - ostatniego. Pomiędzy tymi znakami mogą być jednak takie, które nie mają swojej reprezentacji i wtedy do tablicy przesunięć wpisywana jest wartość zero. Ponieważ również pierwszy znak posiada przesunięcie zerowe, konieczną staje się kontrola wyświetlania niezdefiniowanych znaków tak, by nie wyświetlać ich jako znaku pierwszego. Druga tablica określa szerokość znaków w zbiorze. Po wyrysowaniu każdego znaku należy ustawić kursor graficzny na pozycji określonej przez tę tablicę względem początku właśnie wypisanego znaku. Dzięki takiemu rozwiązaniu możliwe jest np. zdefiniowanie znaku pierwiastka tak, by następny znak znalazł się pod jego daszkiem.

Myślę, że przedstawiony opis formatu zbiorów *.CHR jest zrozumiały i przejrzysty. Dodatkowo dołączam na wydruku tekst programu wyświetlającego pojedyncze czcionki na siatce punktów z uwzględnieniem czterech linii charakteryzujących ich wysokość i zaznaczeniem ich szerokości. Analiza tego programu pozwoli - mam nadzieję - na jeszcze lepsze zapoznanie się z formatem omawianych zbiorów. Forma, w jakiej program ten prezentuje czcionkę na ekranie jest identyczna z tą, jaką stosuje wspominany już program FE.

Zadania, jaki pełnią poszczególne procedury (w kolejności ich deklaracji) są następujące :

  • DrawText oblicza współrzędne czterech lini charakteryzujących znak i wyświetla na tych współrzędnych nazwy linii;
  • DrawMesh rysuje owe cztery linie i siatkę punktów, na której "rozpięty" będzie znak;
  • MarkWidth zaznacza na siatce znaku jego szerokość;
  • ShowChar odpowiedzialna jest za wyświetlenie znaku;
  • CalcParameters służy do określenia wartości linii środkowej, a także do znalezienia największej szerokości znaku; dodatkowo obliczane są współrzędne rogów tabeli ASCII i siatki znaku oraz linii oryginalnej znaku;
  • LoadFont otwiera zbiór o nazwie podanej w jej "głowie" i pobiera kolejno: ogólne informacje na temat czcionek, tablice przesunięć, tablicę szerokości i na końcu same definicje czcionek.

W programie głównym realizowany jest wybór znaku z tabeli ASCII lub bezpośrednio przez naciśnięcie klawisza odpowiadającego danemu znakowi. Przy odczycie klawiatury program korzysta z modułu TextMenu, w którym są zdefiniowane stałe odpowiadające klawiszom "strzałek" oraz procedura ReadKbd. Moduł ten był prezentowany w [4], nie zmuszam jednakże nikogo do przepisywania całej treści modułu. Można jedynie - zamiast jego deklaracji - umieścić w programie stałe i procedurę, przepisując je z tego modułu. Program używa również omówionego wcześniej modułu GrFonts, by otrzymać polskie litery w grafice. Nazwę zbioru, który będzie przeglądany należy podać jako parametr programu. Nazwa musi zawierać rozszerzenie.

Zachęcam czytelników do napisania polskiej wersji edytora czcionek *.CHR, zaś wszystkich zaintersowanych polskimi czcionkami informuję, że razem z artykułem przesłałem do PCKuriera również zbiory czcionek wektorowych z polskimi literami w standardzie Mazovii (wszystkie dziewięć). Jeśli komuś nie odpowiada ten standard, to na podstawie niniejszego artykułu może dokonać transformacji owych zbiorów na standard preferowany.

 Program DisplayStrokedFont;

 Uses TextMenu, Graph, GrFonts;

 Type BlockDataType = Record 
        Ident : Char; 
        ChCnt : Word; 
        UnDef1 : Byte; 
        FirstCh : Char; 
        DefOfs : Word; 
        ScanFlag : Byte; 
        ToCap : ShortInt; 
        ToBase : ShortInt; 
        ToDesc : ShortInt; 
        UnDef2 : Array[$8B..$8F] Of Byte; 
      End; 

      TabOfsType = Array[#0..#255] Of Word; 
      TabWidthType = Array[0..255] Of Byte; 
      
      TabDefType = Array[1..32000] Of Record 
        B1,B2 : Byte; 
      End; 

Var Plik : File; 
    BlockData : BlockDataType; 
    TabOfs : TabOfsType; 
    TabWidth : TabWidthType; 
    TabDef : ^TabDefType; 
    F,N,MaxY,MaxX : Word; 
    Orig,Xa,Ya,X,Y,X1,X2 : Integer; 
    XAscii,YAscii,XChar,YChar : Integer; 
    L : Array[1..4] Of Integer; 
    Scale,Op,Size : Byte; 
    ToXl : ShortInt;
    St,Tr : Integer;
    C : Char;

 Procedure DrawText; 
 Begin 
   x1 := xChar-2*Scale; 
   x2 := xChar+Size*Scale; 
   With BlockData 
   Do Begin 
     L[1] := Orig - ToDesc*Scale; 
     L[2] := Orig - ToBase*Scale; 
     L[3] := Orig - ToCap*Scale; 
     L[4] := Orig - ToXl*Scale; 
   End; 
   SetTextJustify(CenterText,CenterText); 
   SetTextStyle(0,1,1); 
   SetColor(Yellow); 
   OutTextXY(8,l[1],'Dolna'); 
   OutTextXY(8,l[2],'Bazowa'); 
   OutTextXY(8,l[3],'Górna'); 
   OutTextXY(8,l[4],'Środkowa'); 
   SetColor(White); 
   SetTextStyle(0,0,1); 
   SetTextJustify(LeftText,TopText); 
 End; 

 Procedure DrawMesh; 
 Begin 
   SetColor(Brown); 
   For F := 1 To 4 
   Do Line(x1, L[F], x2, L[F]); 
   Y := MaxY - YChar; 
   Line(xChar, Y+Scale, xChar, Y-(Size+1)*Scale); 
   SetWriteMode(OrPut); 
   For Y := Size+1 DownTo -1 
   Do For X := -2 To Size 
   Do PutPixel(xChar + X*Scale, MaxY - yChar - Y*Scale,7); 
   SetWriteMode(CopyPut); 
 End; 

 Procedure MarkWidth(C: Char); 
 Begin
   Y := MaxY - yChar + BlockData.ToDesc * Scale; 
   X := xChar + (TabWidth[Ord(C)] * Scale); 
   SetColor(Yellow); 
   Line(X-5, Y+5, X, Y); 
   Line(X+5, Y+5, X, Y); 
   Line(X+5, Y+5, X-5, Y+5); 
   SetColor(White); 
 End; 

 Procedure ShowChar(C: Char); 
 Var Ok : Boolean; 
 Begin 
   Bar(xChar-2 * Scale,0, xChar+(Size+1) * 
     Scale, MaxY - yChar + 3 * Scale); 
   DrawMesh;
   SetColor(White); 
   Key := BlockData.FirstCh; 
   N := TabOfs[Key]; 
   F := TabOfs[C]; 
   If Not ((KeyC) And (F = N)) 
   Then With BlockData 
   Do Begin 
     F := F Shr 1; 
     Ok := False; 
     If (FirstCh <= C) 
     Then Repeat 
       Inc(F); 
       With TabDef^[F] 
       Do Begin 
         Op := (B1 Shr 7) + (B2 Shr 7); 
         X := B1 And $7F; 
         Y := B2 And $7F; 
         X := ShortInt(X Shl 1) Div 2; 
         X := X * Scale; 
         Y := ShortInt(Y Shl 1) Div 2; 
         Y := Y * Scale; 
         Inc(X, xChar); 
         Dec(Y, BlockData.ToDesc * Scale); 
         Y := MaxY - Y - yChar; 
         Case Op Of 
           0 : Ok := True; 
           1 : MoveTo(X,Y); 
           2 : LineTo(X,Y); 
         End; 
       End;
     Until Ok; 
   End; 
   MarkWidth(C); 
 End; 

 Procedure CalcParameters; 
 Begin 
   Size := 0; 
   With BlockData 
   Do Begin 
     ToXl := 0; 
     F := TabOfs['x'] Shr 1; 
     If (FirstCh <'x') And (f0) 
     Then Repeat 
       Inc(F); 
       With TabDef^[F] 
       Do Begin 
         Op := (B1 Shr 7) + (B2 Shr 7); 
         Y := B2 And $7F; 
       End; 
       Y := ShortInt(Y Shl 1) Div 2; 
       If Y > ToXl Then ToXl := Y; 
     Until Op = 0 
     Else ToXl := Abs(ToCap) Shr 1; 
     For F := Ord(FirstCh) To ChCnt 
     Do If Size < TabWidth[F] 
     Then Size := TabWidth[F]; 
     xChar := Abs(ToCap) + Abs(ToDesc); 
   End; 
   If Size < xChar Then Size := xChar; 
   Inc(Size, 2); 
   xAscii := MaxX - 272; 
   yAscii := 32; 
   Scale := 282 Div Size; 
   yChar := MaxY - (Size+4) * Scale; 
   xChar := 5*Scale; 
   Orig := MaxY-YChar + BlockData.ToDesc*Scale; 
 End; 

 Procedure LoadFont(S: String); 
 Begin 
   Assign(Plik, S); 
   Reset(Plik, 1); 
   Seek(Plik, $80); 
   BlockRead(Plik, BlockData, SizeOf(BlockData)); 
   FillChar(TabOfs, SizeOf(TabOfs),0); 
   FillChar(TabWidth, SizeOf(TabWidth),0); 
   With BlockData Do
   Begin 
     BlockRead(Plik, TabOfs[FirstCh], ChCnt*2); 
     BlockRead(Plik, TabWidth[Ord(FirstCh)], ChCnt); 
   End; 
   F := FilePos(Plik); 
   F := FileSize(Plik)-F; 
   GetMem(TabDef, F); 
   BlockRead(Plik, TabDef^, F); 
   Close(Plik); 
   CalcParameters; 
 End; 

 { - Program Główny - } 

 Begin 
   If ParamCount = 0 Then Halt; 
   InstallHardFonts; 
   St := Detect; 
   InitGraph(St, Tr, ''); 
   MaxY := GetMaxY; 
   MaxX := GetMaxX; 
   LoadFont(ParamStr(1)); 
   Rectangle(xAscii - 11, yAscii - 11, 
     MaxX - 10, yAscii + 258); 
   SetColor(LightGray); 
   For Y := 0 To 15 
   Do For X := 0 To 15 
   Do OutTextXY(xAscii + X*16, yAscii + Y*16, 
     Chr(Y*16+X)); 
   SetFillStyle(1,0); 
   DrawText; 
   C := 'A'; 
   Repeat 
     Ya := Ord(C) Shr 4; 
     Ya := yAscii + (Ya Shl 4); 
     Xa := Ord(C) Mod 16; 
     Xa := xAscii + (Xa Shl 4); 
     SetColor(White); 
     Rectangle(Xa-3, Ya-3, Xa+10, Ya+10); 
     OutTextXY(Xa, Ya, C); 
     ShowChar(C); 
     ReadKbd(Key, NKey); 
     SetColor(Black); 
     Rectangle(Xa-3, Ya-3, Xa+10, Ya+10); 
     SetColor(LightGray); 
     OutTextXY(Xa,Ya,C); 
     If Key = #0 
     Then Case nKey Of 
       Right : Inc(C); 
       Left : Dec(C); 
       Up : Dec(C,16); 
       Down : Inc(C,16); 
     End Else C := Key; 
   Until (Key = ESC); 
   CloseGraph; 
 End.

Literatura:

  • [1] Bułhak L., Goczyński R., Tuszyński M., "DOS 5.0 od środka" HELP, Warszawa 1992.
  • [2] Z.Jasiak, "PC/XT/AT. Najważniejsze dane i tabele. Tom 1/2" M & M Agencja Wydawnicza.
  • [3] J.K.Kowalski, "Turbo Pascal. Moduły użytkowe", PLJ, Warszawa 1992.
  • [4] Paweł Skolimowski, "Własne Turbo Vision", PCKurier 17/93

Paweł Skolimowski - PCKurier - Październik 1994r.