text.inc 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1993,98 by the Free Pascal development team
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {***************************************************************************}
  12. { Text output routines }
  13. {***************************************************************************}
  14. const
  15. maxfonts = 16; { maximum possible fonts }
  16. MaxChars = 255; { Maximum nr. of characters in a file }
  17. Prefix_Size = $80; { prefix size to skip }
  18. SIGNATURE = '+'; { Signature of CHR file }
  19. type
  20. pbyte = ^byte;
  21. pword = ^word;
  22. { Prefix header of Font file }
  23. PFHeader = ^TFHeader;
  24. TFHeader = packed record
  25. header_size: word; {* Version 2.0 Header Format *}
  26. font_name: array[1..4] of char;
  27. font_size: word; {* Size in byte of file *}
  28. font_major: byte; {* Driver Version Information *}
  29. font_minor: byte;
  30. min_major: byte; {* BGI Revision Information *}
  31. min_minor: byte;
  32. end;
  33. { Font record information }
  34. PHeader = ^THeader;
  35. THeader = packed record
  36. Signature: char; { signature byte }
  37. Nr_chars: integer; { number of characters in file }
  38. Reserved: byte;
  39. First_char: byte; { first character in file }
  40. cdefs : integer; { offset to character definitions }
  41. scan_flag: byte; { TRUE if char is scanable }
  42. org_to_cap: byte; { Height from origin to top of capitol }
  43. org_to_base:byte; { Height from origin to baseline }
  44. org_to_dec: byte; { Height from origin to bot of decender }
  45. _reserved: array[1..4] of char;
  46. Unused: byte;
  47. end;
  48. TOffsetTable =array[0..MaxChars] of Integer;
  49. TWidthTable =array[0..MaxChars] of byte;
  50. tfontrec = packed record
  51. name : string[8];
  52. header : THeader; { font header }
  53. pheader : TFHeader; { prefix header }
  54. offsets : TOffsetTable;
  55. widths : TWidthTable;
  56. instr : pchar;
  57. end;
  58. pStroke = ^TStroke;
  59. TStroke = packed record
  60. opcode: byte;
  61. x: integer; { relative x offset character }
  62. y: integer; { relative y offset character }
  63. end;
  64. TStrokes = Array[0..1000] of TStroke;
  65. opcodes = (_END_OF_CHAR, _DO_SCAN, _MOVE, _DRAW);
  66. var
  67. fonts : array[1..maxfonts] of tfontrec;
  68. Strokes: TStrokes; {* Stroke Data Base *}
  69. Stroke_count: Array[0..MaxChars] of integer; {* Stroke Count Table *}
  70. {***************************************************************************}
  71. { Internal support routines }
  72. {***************************************************************************}
  73. function testfont(p : pchar) : boolean;
  74. begin
  75. testfont:=(p[0]='P') and
  76. (p[1]='K') and
  77. (p[2]=#8) and
  78. (p[3]=#8);
  79. end;
  80. function InstallUserFont(const FontFileName : string) : integer;
  81. begin
  82. _graphresult:=grOk;
  83. { first check if we do not allocate too many fonts! }
  84. if installedfonts=maxfonts then
  85. begin
  86. _graphresult:=grError;
  87. InstallUserFont := DefaultFont;
  88. exit;
  89. end;
  90. inc(installedfonts);
  91. fonts[installedfonts].name:=FontFileName;
  92. fonts[installedfonts].instr := nil;
  93. InstallUserFont:=installedfonts;
  94. end;
  95. function Decode(byte1,byte2: char; var x,y: integer): integer;
  96. { This routines decoes a signle word in a font opcode section }
  97. { to a stroke record. }
  98. var
  99. b1,b2: shortint;
  100. Begin
  101. b1:=shortint(byte1);
  102. b2:=shortint(byte2);
  103. { Decode the CHR OPCODE }
  104. Decode:=integer(((b1 and $80) shr 6)+((b2 and $80) shr 7));
  105. { Now get the X,Y coordinates }
  106. { bit 0..7 only which are considered }
  107. { signed values. }
  108. {$R-}
  109. b1:=b1 and $7f;
  110. b2:=b2 and $7f;
  111. { Now if the MSB of these values are set }
  112. { then the value is signed, therefore we }
  113. { sign extend it... }
  114. if (b1 and $40)<>0 then b1:=b1 or $80;
  115. if (b2 and $40)<>0 then b2:=b2 or $80;
  116. x:=integer(b1);
  117. y:=integer(b2);
  118. {$R+}
  119. end;
  120. function unpack(buf: pchar; index: integer; var Stroke: TStrokes): integer;
  121. var
  122. pb: pword;
  123. po: TStrokes;
  124. num_ops: integer;
  125. opcode, i, opc: word;
  126. counter: integer;
  127. lindex: integer;
  128. jx, jy: integer;
  129. begin
  130. num_ops := 0;
  131. counter := index;
  132. lindex :=0;
  133. while TRUE do {* For each byte in buffer *}
  134. Begin
  135. Inc(num_ops); {* Count the operation *}
  136. opcode := decode( buf[counter], buf[counter+1] ,jx, jy );
  137. Inc(counter,2);
  138. if( opcode = ord(_END_OF_CHAR) ) then break; {* Exit loop at end of char *}
  139. end;
  140. counter:=index;
  141. for i:=0 to num_ops-1 do { /* For each opcode in buffer */ }
  142. Begin
  143. opc := decode(buf[counter], buf[counter+1], po[lindex].x, po[lindex].y); {* Decode the data field *}
  144. inc(counter,2);
  145. po[lindex].opcode := opc; {* Save the opcode *}
  146. Inc(lindex);
  147. end;
  148. Stroke:=po;
  149. unpack := num_ops; {* return OPS count *}
  150. end;
  151. procedure GetTextPosition(var xpos,ypos: longint; const TextString: string);
  152. begin
  153. if CurrentTextInfo.Font = DefaultFont then
  154. begin
  155. if Currenttextinfo.direction=horizdir then
  156. begin
  157. case Currenttextinfo.horiz of
  158. centertext : XPos:=(textwidth(textstring) shr 1);
  159. lefttext : XPos:=0;
  160. righttext : XPos:=textwidth(textstring);
  161. end;
  162. case Currenttextinfo.vert of
  163. centertext : YPos:=-(textheight(textstring) shr 1);
  164. bottomtext : YPos:=-textheight(textstring);
  165. toptext : YPos:=0;
  166. end;
  167. end else
  168. begin
  169. case Currenttextinfo.horiz of
  170. centertext : XPos:=(textheight(textstring) shr 1);
  171. lefttext : XPos:=textheight(textstring);
  172. righttext : XPos:=textheight(textstring);
  173. end;
  174. case Currenttextinfo.vert of
  175. centertext : YPos:=(textwidth(textstring) shr 1);
  176. bottomtext : YPos:=0;
  177. toptext : YPos:=textwidth(textstring);
  178. end;
  179. end;
  180. end
  181. else
  182. begin
  183. if Currenttextinfo.direction=horizdir then
  184. begin
  185. case CurrentTextInfo.horiz of
  186. centertext : XPos:=(textwidth(textstring) shr 1);
  187. lefttext : XPos:=0;
  188. righttext : XPos:=textwidth(textstring);
  189. end;
  190. case CurrentTextInfo.vert of
  191. centertext : YPos:=(textheight(textstring) shr 1);
  192. bottomtext : YPos:=0;
  193. toptext : YPos:=textheight(textstring);
  194. end;
  195. end else
  196. begin
  197. case CurrentTextInfo.horiz of
  198. centertext : XPos:=(textheight(textstring) shr 1);
  199. lefttext : XPos:=0;
  200. righttext : XPos:=textheight(textstring);
  201. end;
  202. case CurrentTextInfo.vert of
  203. centertext : YPos:=(textwidth(textstring) shr 1);
  204. bottomtext : YPos:=0;
  205. toptext : YPos:=textwidth(textstring);
  206. end;
  207. end;
  208. end;
  209. end;
  210. {***************************************************************************}
  211. { Exported routines }
  212. {***************************************************************************}
  213. function RegisterBGIfont(font : pointer) : integer;
  214. var
  215. hp : pchar;
  216. b : word;
  217. i,j: longint;
  218. Header: THeader;
  219. counter: longint;
  220. FontData: pchar;
  221. FHeader: TFHeader;
  222. begin
  223. RegisterBGIfont:=grInvalidFontNum;
  224. i:=0;
  225. { Check if the font header is valid first of all }
  226. if testfont(font) then
  227. begin
  228. hp:=pchar(font);
  229. { Move to EOF in prefix header }
  230. while (hp[i] <> chr($1a)) do Inc(i);
  231. move(hp[i+1],FHeader,sizeof(TFHeader));
  232. move(hp[Prefix_Size],header,sizeof(THeader));
  233. { check if the font name is already allocated? }
  234. i:=Prefix_Size+sizeof(THeader);
  235. for b:=1 to installedfonts do
  236. begin
  237. if fonts[b].name=FHeader.Font_name then
  238. begin
  239. move(FHeader,fonts[b].PHeader,sizeof(TFHeader));
  240. move(Header,fonts[b].Header,sizeof(THeader));
  241. move(hp[i],Fonts[b].Offsets[Fonts[b].Header.First_Char],Fonts[b].Header.Nr_chars*sizeof(integer));
  242. Inc(i,Fonts[b].Header.Nr_chars*sizeof(integer));
  243. move(hp[i],Fonts[b].Widths[Fonts[b].Header.First_Char],Fonts[b].Header.Nr_chars*sizeof(byte));
  244. Inc(i,Fonts[b].Header.Nr_chars*sizeof(byte));
  245. counter:=Fonts[b].PHeader.font_size+PREFIX_SIZE-i;
  246. { allocate also space for null }
  247. GetMem(FontData,Counter+1);
  248. move(hp[i],FontData^,Counter);
  249. { Null terminate the string }
  250. FontData[counter+1] := #0;
  251. if fonts[b].header.Signature<> SIGNATURE then
  252. begin
  253. _graphResult:=grInvalidFont;
  254. Freemem(FontData, Counter+1);
  255. exit;
  256. end;
  257. fonts[b].instr:=FontData;
  258. RegisterBGIfont:=b;
  259. end;
  260. end;
  261. end
  262. else
  263. RegisterBGIFont:=grInvalidFont;
  264. end;
  265. procedure GetTextSettings(var TextInfo : TextSettingsType);
  266. begin
  267. textinfo:=currenttextinfo;
  268. end;
  269. function TextHeight(const TextString : string) : word;
  270. begin
  271. if Currenttextinfo.font=DefaultFont
  272. then TextHeight:=8*CurrentTextInfo.CharSize
  273. else
  274. TextHeight:=fonts[Currenttextinfo.font].header.org_to_cap-
  275. round(fonts[Currenttextinfo.font].header.org_to_base * CurrentYRatio) ;
  276. end;
  277. function TextWidth(const TextString : string) : word;
  278. var i,x : Integer;
  279. c : byte;
  280. begin
  281. x := 0;
  282. { if this is the default font ... }
  283. if Currenttextinfo.font = Defaultfont then
  284. TextWidth:=length(TextString)*8*CurrentTextInfo.CharSize
  285. { This is a stroked font ... }
  286. else begin
  287. for i:=1 to length(TextString) do
  288. begin
  289. c:=byte(textstring[i]);
  290. dec(c,fonts[Currenttextinfo.font].header.first_char);
  291. if (c<0) or (c>=fonts[Currenttextinfo.font].header.nr_chars) then
  292. continue;
  293. x:=x+byte(fonts[Currenttextinfo.font].widths[c]);
  294. end;
  295. TextWidth:=round(x * CurrentXRatio) ;
  296. end;
  297. end;
  298. procedure OutTextXY(x,y : integer;const TextString : string);
  299. type
  300. Tpoint = record
  301. X,Y: Integer;
  302. end;
  303. var
  304. ch: char;
  305. b1,b2 : shortint;
  306. b3 : byte;
  307. c : byte;
  308. i,j,k : longint;
  309. oldvalues : linesettingstype;
  310. nextpos : word;
  311. xpos,ypos,offs: longint;
  312. counter : longint;
  313. FontBitmap : TBitmapChar;
  314. chr: char;
  315. cnt1,cnt2 : integer;
  316. cnt3,cnt4 : integer;
  317. charsize : word;
  318. TextBuffer : array[1..sizeof(string)*2] of Tpoint;
  319. WriteMode : word;
  320. CurX, CurY : integer;
  321. begin
  322. { save current write mode }
  323. WriteMode := CurrentWriteMode;
  324. CurrentWriteMode := NormalPut;
  325. GetTextPosition(xpos,ypos,textstring);
  326. X:=X-XPos; Y:=Y+YPos;
  327. XPos:=X; YPos:=Y;
  328. CharSize := CurrentTextInfo.Charsize;
  329. if Currenttextinfo.font=DefaultFont then
  330. begin
  331. c:=length(textstring);
  332. { We must a length strength which is ZERO based }
  333. Dec(c);
  334. if CurrentTextInfo.direction=HorizDir then
  335. { Horizontal direction }
  336. begin
  337. for i:=0 to c do
  338. begin
  339. chr := TextString[i+1];
  340. xpos:=x+(i shl 3)*Charsize;
  341. { we copy the character bitmap before accessing it }
  342. { this improves speed on non optimizing compilers }
  343. { since it is one less address calculation. }
  344. Fontbitmap:=TBitmapChar(DefaultFontData[chr]);
  345. { no scaling }
  346. if CharSize = 1 then
  347. Begin
  348. for j:=0 to 7 do
  349. for k:=0 to 7 do
  350. if Fontbitmap[j,k] <> 0 then PutPixel(xpos+k,j+y,CurrentColor);
  351. end
  352. else
  353. { perform scaling of bitmap font }
  354. Begin
  355. j:=0;
  356. cnt3:=0;
  357. while j < 7 do
  358. begin
  359. { X-axis scaling }
  360. for cnt4 := 0 to charsize-1 do
  361. begin
  362. k:=0;
  363. cnt2 := 0;
  364. while k < 7 do
  365. begin
  366. for cnt1 := 0 to charsize-1 do
  367. begin
  368. If FontBitmap[j,k] <> 0 then
  369. PutPixel(xpos+cnt1+cnt2,y+cnt3+cnt4,CurrentColor);
  370. end;
  371. Inc(k);
  372. Inc(cnt2,charsize);
  373. end;
  374. end;
  375. Inc(j);
  376. Inc(cnt3,charsize);
  377. end;
  378. end;
  379. end;
  380. end
  381. else
  382. { Vertical direction }
  383. begin
  384. for i:=0 to c do
  385. begin
  386. chr := TextString[i+1];
  387. Fontbitmap:=TBitmapChar(DefaultFontData[chr]);
  388. ypos := y-(i shl 3)*CharSize;
  389. { no scaling }
  390. if CharSize = 1 then
  391. Begin
  392. for j:=0 to 7 do
  393. for k:=0 to 7 do
  394. if Fontbitmap[j,k] <> 0 then PutPixel(xpos+j,ypos-k,
  395. CurrentColor);
  396. end
  397. else
  398. { perform scaling of bitmap font }
  399. Begin
  400. j:=0;
  401. cnt3:=0;
  402. while j < 7 do
  403. begin
  404. { X-axis scaling }
  405. for cnt4 := 0 to charsize-1 do
  406. begin
  407. k:=0;
  408. cnt2 := 0;
  409. while k < 7 do
  410. begin
  411. for cnt1 := 0 to charsize-1 do
  412. begin
  413. If FontBitmap[j,k] <> 0 then
  414. PutPixel(xpos+cnt3-cnt4,ypos+cnt1-cnt2,
  415. CurrentColor);
  416. end;
  417. Inc(k);
  418. Inc(cnt2,charsize);
  419. end;
  420. end;
  421. Inc(j);
  422. Inc(cnt3,charsize);
  423. end;
  424. end;
  425. end;
  426. end;
  427. end else
  428. { This is a stroked font which is already loaded into memory }
  429. begin
  430. getlinesettings(oldvalues);
  431. { reset line style to defaults }
  432. setlinestyle(solidln,oldvalues.pattern,normwidth);
  433. if Currenttextinfo.direction=vertdir then
  434. xpos:=xpos + Textheight(textstring);
  435. CurX:=xpos; CurY:=ypos; x:=xpos; y:=ypos;
  436. for i:=1 to length(textstring) do
  437. begin
  438. c:=byte(textstring[i]);
  439. Stroke_Count[c] := unpack( fonts[CurrentTextInfo.font].instr,
  440. fonts[CurrentTextInfo.font].Offsets[c], Strokes );
  441. counter:=0;
  442. while true do
  443. begin
  444. if CurrentTextInfo.direction=VertDir then
  445. begin
  446. xpos:=x-round(Strokes[counter].Y*CurrentXRatio);
  447. ypos:=y-round(Strokes[counter].X*CurrentYRatio);
  448. end
  449. else
  450. begin
  451. xpos:=x+round(Strokes[counter].X*CurrentXRatio) ;
  452. ypos:=y-round(Strokes[counter].Y*CurrentYRatio) ;
  453. end;
  454. case opcodes(Strokes[counter].opcode) of
  455. _END_OF_CHAR: break;
  456. _DO_SCAN: begin
  457. { Currently unsupported };
  458. end;
  459. _MOVE : Begin
  460. CurX := XPos;
  461. CurY := YPos;
  462. end;
  463. _DRAW: Begin
  464. Line(CurX,CurY,xpos,ypos);
  465. CurX:=xpos;
  466. CurY:=ypos;
  467. end;
  468. else
  469. Begin
  470. end;
  471. end;
  472. Inc(counter);
  473. end; { end while }
  474. if Currenttextinfo.direction=VertDir then
  475. y:=y-round(byte(fonts[CurrenttextInfo.font].widths[c])*CurrentXRatio)
  476. else
  477. x:=x+round(byte(fonts[Currenttextinfo.font].widths[c])*CurrentXRatio) ;
  478. end;
  479. setlinestyle( oldvalues.linestyle, oldvalues.pattern, oldvalues.thickness);
  480. end;
  481. { restore write mode }
  482. CurrentWriteMode := WriteMode;
  483. end;
  484. procedure OutText(const TextString : string);
  485. var x,y:integer;
  486. begin
  487. { Save CP }
  488. x:=CurrentX;
  489. y:=CurrentY;
  490. OutTextXY(CurrentX,CurrentY,TextString);
  491. { If the direction is Horizontal and the justification left }
  492. { then and only then do we update the CP }
  493. if (Currenttextinfo.direction=HorizDir) and
  494. (Currenttextinfo.horiz=LeftText) then
  495. inc(x,textwidth(TextString));
  496. { Update the CP }
  497. CurrentX := X;
  498. CurrentY := Y;
  499. end;
  500. procedure SetTextJustify(horiz,vert : word);
  501. begin
  502. if (horiz<0) or (horiz>2) or
  503. (vert<0) or (vert>2) then
  504. begin
  505. _graphresult:=grError;
  506. exit;
  507. end;
  508. Currenttextinfo.horiz:=horiz;
  509. Currenttextinfo.vert:=vert;
  510. end;
  511. procedure SetTextStyle(font,direction : word;charsize : word);
  512. var
  513. f : file;
  514. Prefix: array[0..Prefix_Size-1] of char; {* File Prefix Holder *}
  515. Length, Current: longint;
  516. FontData: Pchar;
  517. Base: longint;
  518. hp : pchar;
  519. i : longint;
  520. begin
  521. if font>installedfonts then
  522. begin
  523. _graphresult:=grInvalidFontNum;
  524. exit;
  525. end;
  526. Currenttextinfo.font:=font;
  527. if (direction<>HorizDir) and (direction<>VertDir) then
  528. direction:=HorizDir;
  529. Currenttextinfo.direction:=direction;
  530. { According to the Turbo Pascal programmer's reference }
  531. { maximum charsize for bitmapped font is 10 }
  532. if (CurrentTextInfo.Font = DefaultFont) and (Charsize > 10) then
  533. Currenttextinfo.charsize:=10
  534. else
  535. Currenttextinfo.charsize:=charsize;
  536. { This is only valid for stroked fonts }
  537. if (charsize <> usercharsize) then
  538. begin
  539. CurrentXRatio := charsize / 4;
  540. CurrentYRatio := charsize / 4;
  541. end;
  542. { if this is a stroked font then load it if not already loaded }
  543. { into memory... }
  544. if (font>DefaultFont) and not assigned(fonts[font].instr) then
  545. begin
  546. assign(f,bgipath+fonts[font].name+'.CHR');
  547. reset(f,1);
  548. if ioresult<>0 then
  549. begin
  550. _graphresult:=grFontNotFound;
  551. Currenttextinfo.font:=DefaultFont;
  552. exit;
  553. end;
  554. {* Read in the file prefix *}
  555. BlockRead(F, Prefix, Prefix_Size);
  556. hp:=Prefix;
  557. i:=0;
  558. while (hp[i] <> chr($1a)) do Inc(i);
  559. move(hp[i+1],fonts[font].PHeader,sizeof(TFHeader));
  560. (* Read in the Header file *)
  561. BlockRead(F,fonts[font].Header,Sizeof(THeader));
  562. Base := FilePos(F); {* Remember the address of table*}
  563. BlockRead(F,Fonts[font].Offsets[Fonts[font].Header.First_Char],Fonts[font].Header.Nr_chars*sizeof(integer));
  564. {* Load the character width table into memory. *}
  565. base := filePos( f );
  566. BlockRead(F,Fonts[font].Widths[Fonts[font].Header.First_Char],Fonts[font].Header.Nr_chars*sizeof(byte));
  567. {* Determine the length of the stroke database. *}
  568. current := FilePos( f ); {* Current file location *}
  569. Seek( f, FileSize(F)); {* Go to the end of the file *}
  570. length := FilePos( f ); {* Get the file length *}
  571. Seek( f, current); {* Restore old file location *}
  572. {* Load the stroke database. *}
  573. { also allocate space for Null character }
  574. Getmem(FontData, Length+1); {* Create space for font data *}
  575. BlockRead(F, FontData^, length-current); {* Load the stroke data *}
  576. FontData[length-current+1] := #0;
  577. if fonts[font].header.Signature<> SIGNATURE then
  578. begin
  579. _graphResult:=grInvalidFont;
  580. Currenttextinfo.font:=DefaultFont;
  581. Freemem(FontData, Length+1);
  582. exit;
  583. end;
  584. fonts[font].instr:=FontData;
  585. if not testfont(Prefix) then
  586. begin
  587. _graphresult:=grInvalidFont;
  588. Currenttextinfo.font:=DefaultFont;
  589. Freemem(FontData, Length+1);
  590. end;
  591. close(f);
  592. end;
  593. end;
  594. procedure SetUserCharSize(Multx,Divx,Multy,Divy : word);
  595. begin
  596. CurrentXRatio := MultX / DivX;
  597. CurrentYRatio := MultY / DivY;
  598. end;