{
UFonts содержит класс для работы с экранными шрифтами, которые поддерживают
Уникод (.ufn). Поддерживается: получение базовой информации о шрифте
(размер, начертание), вывод отдельных символов и символьных строк.
Copyright © 2016, 2019, 2022–2023 Малик Разработчик
Это свободная программа: вы можете перераспространять её и/или
изменять её на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она может быть полезна,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<http://www.gnu.org/licenses/>.
}
unit UFonts;
{$MODE DELPHI}
interface
uses
Lang,
IOStreams,
FileIO,
Images;
{%region public }
type
UnicodeFont = class(_Object)
strict private
style: int;
height: int;
baseLineHeight: int;
underlinePos: int;
strikeoutPos: int;
linesWidth: int;
codesCount: int;
widths: byte_Array1d;
codes: int_Array1d;
offsets: int_Array1d;
stream: DataInput;
name: UnicodeString;
function indexOfCharacter(code: int): int;
procedure seek(pos: long);
public
constructor create(const fontName, fileName: AnsiString); overload;
constructor create(const fontName, fileName: UnicodeString); overload;
function isCharacterExists(code: int): boolean; virtual;
function isBold(): boolean; virtual;
function isItalic(): boolean; virtual;
function isEmpty(): boolean; virtual;
function getStyle(): int; virtual;
function getHeight(): int; virtual;
function getBaseLineHeight(): int; virtual;
function getName(): UnicodeString; virtual;
function getCharacterWidth(code: int): int; virtual;
function getCharactersWidth(const chars: AnsiString): int; overload; virtual;
function getCharactersWidth(const chars: UnicodeString): int; overload; virtual;
function getCharactersWidth(const chars: int_Array1d): int; overload; virtual;
function getCharactersWidth(const chars: int_Array1d; offset, length: int): int; overload; virtual;
function drawCharacter(listener: GraphicListener; code: int; x, y, argb: int; underline, strikeout: boolean): int; virtual;
function drawCharacters(listener: GraphicListener; const chars: AnsiString; x, y, argb: int; underline, strikeout: boolean): int; overload; virtual;
function drawCharacters(listener: GraphicListener; const chars: UnicodeString; x, y, argb: int; underline, strikeout: boolean): int; overload; virtual;
function drawCharacters(listener: GraphicListener; const chars: int_Array1d; x, y, argb: int; underline, strikeout: boolean): int; overload; virtual;
function drawCharacters(listener: GraphicListener; const chars: int_Array1d; offset, length: int; x, y, argb: int; underline, strikeout: boolean): int; overload; virtual;
end;
{%endregion}
implementation
{%region UnicodeFont }
function UnicodeFont.indexOfCharacter(code: int): int;
var
left: int;
right: int;
c: int;
r: int;
begin
result := -1;
left := 0;
right := codesCount - 1;
while left <= right do begin
c := (left + right) shr 1;
r := code - codes[c];
if r > 0 then
left := c + 1
else if r < 0 then
right := c - 1
else begin
result := c;
break;
end;
end;
end;
procedure UnicodeFont.seek(pos: long);
var
stream: Input;
begin
stream := self.stream;
stream.seek(pos - stream.position());
end;
constructor UnicodeFont.create(const fontName, fileName: AnsiString);
begin
create(toUTF16String(fontName), toUTF16String(fileName));
end;
constructor UnicodeFont.create(const fontName, fileName: UnicodeString);
var
i: int;
codesCount: int;
widths: byte_Array1d;
codes: int_Array1d;
offsets: int_Array1d;
f: FileInputStream;
stream: DataInput;
begin
inherited create();
f := FileInputStream.create(fileName);
if f.isInvalidHandle() then begin
f.free();
exit;
end;
stream := DataInputStream.create(f);
if stream.readInt() <> $55464e54 then begin
exit;
end;
self.style := stream.readUnsignedByte() and $03;
self.height := stream.readUnsignedByte();
self.baseLineHeight := stream.readUnsignedByte();
stream.seek(2);
self.underlinePos := stream.readByte();
self.strikeoutPos := stream.readByte();
self.linesWidth := stream.readUnsignedByte();
codesCount := stream.readIntLE();
if (codesCount < 0) or (self.height = 0) then begin
self.codesCount := 0;
exit;
end;
self.codesCount := codesCount;
if self.linesWidth = 0 then begin
self.linesWidth := 1;
end;
widths := byte_Array1d_create(codesCount);
codes := int_Array1d_create(codesCount);
offsets := int_Array1d_create(codesCount);
for i := 0 to codesCount - 1 do begin
codes[i] := stream.readIntLE();
offsets[i] := stream.readIntLE();
end;
self.widths := widths;
self.codes := codes;
self.offsets := offsets;
self.stream := stream;
if System.length(fontName) > 0 then begin
self.name := fontName;
end else begin
self.name := 'Default';
end;
for i := 0 to codesCount - 1 do begin
seek(zeroExtend(offsets[i]));
widths[i] := byte(stream.readByte());
end;
end;
function UnicodeFont.isCharacterExists(code: int): boolean;
begin
if stream <> nil then begin
result := indexOfCharacter(code) >= 0;
end else begin
result := false;
end;
end;
function UnicodeFont.isBold(): boolean;
begin
result := (style and $01) <> 0;
end;
function UnicodeFont.isItalic(): boolean;
begin
result := (style and $02) <> 0;
end;
function UnicodeFont.isEmpty(): boolean;
begin
result := codesCount = 0;
end;
function UnicodeFont.getStyle(): int;
begin
result := style;
end;
function UnicodeFont.getHeight(): int;
begin
result := height;
end;
function UnicodeFont.getBaseLineHeight(): int;
begin
result := baseLineHeight;
end;
function UnicodeFont.getName(): UnicodeString;
begin
result := name;
end;
function UnicodeFont.getCharacterWidth(code: int): int;
var
index: int;
begin
index := indexOfCharacter(code);
if index < 0 then begin
index := indexOfCharacter(0);
end;
if index >= 0 then begin
result := widths[index] and $ff;
end else begin
result := 0;
end;
end;
function UnicodeFont.getCharactersWidth(const chars: AnsiString): int;
begin
result := getCharactersWidth(getCharCodes(chars));
end;
function UnicodeFont.getCharactersWidth(const chars: UnicodeString): int;
begin
result := getCharactersWidth(getCharCodes(chars));
end;
function UnicodeFont.getCharactersWidth(const chars: int_Array1d): int;
begin
result := getCharactersWidth(chars, 0, System.length(chars));
end;
function UnicodeFont.getCharactersWidth(const chars: int_Array1d; offset, length: int): int;
var
lim: int;
len: int;
i: int;
begin
lim := offset + length;
len := System.length(chars);
if (lim > len) or (lim < offset) or (offset < 0) or (offset > len) then begin
raise ArrayIndexOutOfBoundsException.create('UnicodeFont.getCharactersWidth: индекс элемента массива выходит из диапазона.');
end;
result := 0;
for i := offset to lim - 1 do begin
inc(result, getCharacterWidth(chars[i]));
end;
end;
function UnicodeFont.drawCharacter(listener: GraphicListener; code: int; x, y, argb: int; underline, strikeout: boolean): int;
var
b: int;
i: int;
j: int;
ix: int;
iy: int;
iw: int;
ih: int;
xl: int;
yl: int;
pos: int;
bytes: int;
index: int;
image: byte_Array1d;
stream: DataInput;
begin
stream := self.stream;
if (listener = nil) or (stream = nil) then begin
result := 0;
exit;
end;
index := indexOfCharacter(code);
if index < 0 then begin
index := indexOfCharacter(0);
if index < 0 then begin
result := 0;
exit;
end;
end;
result := widths[index] and $ff;
seek(zeroExtend(offsets[index] + 1));
ix := stream.readByte();
iy := stream.readByte();
iw := stream.readUnsignedByte();
ih := stream.readUnsignedByte();
bytes := iw div 8;
if (iw and $07) <> 0 then begin
inc(bytes);
end;
image := byte_Array1d_create(bytes * ih);
pos := 0;
b := 0;
for j := 1 to ih do for i := 0 to iw - 1 do begin
if (i and $07) = 0 then begin
b := stream.readUnsignedByte();
image[pos] := b;
inc(pos);
end else begin
b := b shr 1;
end;
if (b and 1) <> 0 then begin
listener.putPixel(x + ix + i, y - iy - j, argb);
end;
end;
if not underline and not strikeout then begin
exit;
end;
for j := 0 to linesWidth - 1 do begin
if underline then begin
yl := underlinePos - j;
for xl := 0 to result - 1 do begin
if (xl >= ix) and (xl < ix + iw) and (yl >= iy) and (yl < iy + ih) then begin
index := (((yl - iy) * bytes) shl 3) + (xl - ix);
if (image[index shr 3] and (1 shl (index and $07))) = 0 then begin
listener.putPixel(x + xl, y - yl - 1, argb);
end;
end else begin
listener.putPixel(x + xl, y - yl - 1, argb);
end;
end;
end;
if strikeout then begin
yl := strikeoutPos - j;
for xl := 0 to result - 1 do begin
if (xl >= ix) and (xl < ix + iw) and (yl >= iy) and (yl < iy + ih) then begin
index := (((yl - iy) * bytes) shl 3) + (xl - ix);
if (image[index shr 3] and (1 shl (index and $07))) = 0 then begin
listener.putPixel(x + xl, y - yl - 1, argb);
end;
end else begin
listener.putPixel(x + xl, y - yl - 1, argb);
end;
end;
end;
end;
end;
function UnicodeFont.drawCharacters(listener: GraphicListener; const chars: AnsiString; x, y, argb: int; underline, strikeout: boolean): int;
begin
result := drawCharacters(listener, getCharCodes(chars), x, y, argb, underline, strikeout);
end;
function UnicodeFont.drawCharacters(listener: GraphicListener; const chars: UnicodeString; x, y, argb: int; underline, strikeout: boolean): int;
begin
result := drawCharacters(listener, getCharCodes(chars), x, y, argb, underline, strikeout);
end;
function UnicodeFont.drawCharacters(listener: GraphicListener; const chars: int_Array1d; x, y, argb: int; underline, strikeout: boolean): int;
begin
result := drawCharacters(listener, chars, 0, System.length(chars), x, y, argb, underline, strikeout);
end;
function UnicodeFont.drawCharacters(listener: GraphicListener; const chars: int_Array1d; offset, length: int; x, y, argb: int; underline, strikeout: boolean): int;
var
lim: int;
len: int;
i: int;
w: int;
begin
lim := offset + length;
len := System.length(chars);
if (lim > len) or (lim < offset) or (offset < 0) or (offset > len) then begin
raise ArrayIndexOutOfBoundsException.create('UnicodeFont.drawCharacters: индекс элемента массива выходит из диапазона.');
end;
result := 0;
for i := offset to lim - 1 do begin
w := drawCharacter(listener, chars[i], x, y, argb, underline, strikeout);
inc(x, w);
inc(result, w);
end;
end;
{%endregion}
end.