Add TrueType font support with 4-tier font selection and documentation
Replace the inherited Font property with a FontSize property that drives a multi-tier font selection strategy for pixel-perfect OEM rendering: Terminal raster (exact size) > Perfect DOS VGA 437 (multiples of 8px) > Terminal raster (nearest) > stock OEM fallback. Add DxBuf uniform character spacing to ExtTextOut for correct TrueType monospace rendering. Bundle the Perfect DOS VGA 437 font (cmap converted from format 6 to format 0 for Win 3.1 compatibility). Size the test form dynamically from terminal dimensions. Add component documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d898e73213
commit
ec6eebb2a5
7 changed files with 703 additions and 58 deletions
2
.gitattributes
vendored
2
.gitattributes
vendored
|
|
@ -1,7 +1,6 @@
|
|||
# Git LFS - binary files
|
||||
*.bmp filter=lfs diff=lfs merge=lfs -text
|
||||
*.DRV filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
# Force LF line endings on source files
|
||||
*.c text eol=lf
|
||||
*.h text eol=lf
|
||||
|
|
@ -9,3 +8,4 @@
|
|||
*.def text eol=lf
|
||||
makefile text eol=lf
|
||||
*.TXT text eol=lf
|
||||
*.TTF filter=lfs diff=lfs merge=lfs -text
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ type
|
|||
{ Terminal dimensions }
|
||||
FCols: Integer; { Number of columns (default 80) }
|
||||
FRows: Integer; { Number of rows (default 25) }
|
||||
FFontSize: Integer; { Font point size for OEM terminal font }
|
||||
FScrollbackSize: Integer; { Max scrollback lines to retain (default 500) }
|
||||
|
||||
{ Cursor visibility (DEC ?25h/l) }
|
||||
|
|
@ -105,7 +106,6 @@ type
|
|||
FLiveDC: HDC; { Non-zero during render pass in WriteDeferredBuf }
|
||||
|
||||
procedure AllocLine(Line: PTermLine);
|
||||
procedure CMFontChanged(var Msg: TMessage); message cm_FontChanged;
|
||||
procedure CreatePaintFont;
|
||||
procedure DeleteChars(N: Integer);
|
||||
procedure DeleteLines(N: Integer);
|
||||
|
|
@ -128,15 +128,18 @@ type
|
|||
procedure ResizeScreen;
|
||||
procedure SetCols(Value: Integer);
|
||||
procedure SetCursorVisible(Value: Boolean);
|
||||
procedure SetFontSize(Value: Integer);
|
||||
procedure SetRows(Value: Integer);
|
||||
procedure SetScrollbackSize(Value: Integer);
|
||||
procedure TrimScrollback;
|
||||
procedure UpdateScrollbar;
|
||||
procedure WriteDeferred(const S: string);
|
||||
procedure WMEraseBkgnd(var Msg: TWMEraseBkgnd); message wm_EraseBkgnd;
|
||||
procedure WMGetDlgCode(var Msg: TMessage); message wm_GetDlgCode;
|
||||
procedure WMVScroll(var Msg: TWMScroll); message wm_VScroll;
|
||||
protected
|
||||
procedure CreateParams(var Params: TCreateParams); override;
|
||||
procedure CreateWnd; override;
|
||||
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
|
||||
procedure KeyPress(var Key: Char); override;
|
||||
procedure Paint; override;
|
||||
|
|
@ -148,10 +151,7 @@ type
|
|||
procedure FlipToScreen;
|
||||
procedure TickBlink;
|
||||
procedure Write(const S: string);
|
||||
procedure WriteDeferred(const S: string);
|
||||
procedure WriteDeferredBuf(Buf: PChar; Len: Integer);
|
||||
property CellWidth: Integer read FCellWidth;
|
||||
property CellHeight: Integer read FCellHeight;
|
||||
property CursorCol: Integer read FCursorCol;
|
||||
property CursorRow: Integer read FCursorRow;
|
||||
published
|
||||
|
|
@ -161,7 +161,7 @@ type
|
|||
write SetScrollbackSize default 500;
|
||||
property CursorVisible: Boolean read FCursorVisible
|
||||
write SetCursorVisible default True;
|
||||
property Font;
|
||||
property FontSize: Integer read FFontSize write SetFontSize default 12;
|
||||
property Color default clBlack;
|
||||
property OnKeyData: TKeyDataEvent read FOnKeyData write FOnKeyData;
|
||||
property TabStop default True;
|
||||
|
|
@ -194,8 +194,6 @@ const
|
|||
{ Blink toggle interval in milliseconds (cursor + text blink). }
|
||||
BlinkMs = 500;
|
||||
|
||||
{ OUT_RASTER_PRECIS may not be defined in Delphi 1.0 WinTypes }
|
||||
OutRasterPrecis = 6;
|
||||
|
||||
{ ExtTextOut option flag (may not be in Delphi 1.0 WinTypes) }
|
||||
ETO_OPAQUE = $0002;
|
||||
|
|
@ -305,12 +303,6 @@ begin
|
|||
end;
|
||||
|
||||
|
||||
procedure TKPAnsi.CMFontChanged(var Msg: TMessage);
|
||||
begin
|
||||
inherited;
|
||||
RecalcCellSize;
|
||||
end;
|
||||
|
||||
|
||||
constructor TKPAnsi.Create(AOwner: TComponent);
|
||||
var
|
||||
|
|
@ -331,14 +323,11 @@ begin
|
|||
FAttrFG := 7;
|
||||
FCellWidth := 8;
|
||||
FCellHeight := 18;
|
||||
FFontSize := 12;
|
||||
FBlinkOn := True;
|
||||
FLastBlinkTick := GetTickCount;
|
||||
FWrapMode := True;
|
||||
FAllDirty := True;
|
||||
{ Set a monospace font -- OEM charset selected in CreatePaintFont }
|
||||
Font.Name := 'Terminal';
|
||||
Font.Size := 12;
|
||||
Font.Pitch := fpFixed;
|
||||
|
||||
{ Allocate initial screen lines }
|
||||
for I := 0 to FRows - 1 do
|
||||
|
|
@ -355,6 +344,8 @@ var
|
|||
LF: TLogFont;
|
||||
ActualLF: TLogFont;
|
||||
NewFont: HFont;
|
||||
BaseH: Integer;
|
||||
Delta: Integer;
|
||||
begin
|
||||
{ Free previous font (stock fonts must not be deleted) }
|
||||
if (FPaintFont <> 0) and not FStockFont then
|
||||
|
|
@ -362,32 +353,89 @@ begin
|
|||
FPaintFont := 0;
|
||||
FStockFont := False;
|
||||
|
||||
{ Build LOGFONT requesting OEM_CHARSET with raster precision for CP437 }
|
||||
{ box-drawing, block elements, and other BBS ANSI art glyphs. Raster }
|
||||
{ precision prevents the font mapper from substituting a TrueType font }
|
||||
{ that might remap character codes through Unicode tables. }
|
||||
FillChar(LF, SizeOf(LF), 0);
|
||||
LF.lfHeight := Font.Height;
|
||||
LF.lfPitchAndFamily := FIXED_PITCH or FF_MODERN;
|
||||
LF.lfCharSet := OEM_CHARSET;
|
||||
LF.lfOutPrecision := OutRasterPrecis;
|
||||
if fsBold in Font.Style then
|
||||
LF.lfWeight := FW_BOLD
|
||||
else
|
||||
LF.lfWeight := FW_NORMAL;
|
||||
StrPCopy(LF.lfFaceName, Font.Name);
|
||||
LF.lfPitchAndFamily := FIXED_PITCH or FF_MODERN;
|
||||
BaseH := MulDiv(FFontSize, Screen.PixelsPerInch, 72);
|
||||
|
||||
{ Try Terminal raster at the exact requested size first. Terminal is }
|
||||
{ pixel-perfect but only available at specific cell heights. }
|
||||
LF.lfCharSet := OEM_CHARSET;
|
||||
LF.lfHeight := BaseH;
|
||||
StrPCopy(LF.lfFaceName, 'Terminal');
|
||||
NewFont := CreateFontIndirect(LF);
|
||||
if NewFont <> 0 then
|
||||
begin
|
||||
{ Verify Windows actually gave us an OEM charset font }
|
||||
GetObject(NewFont, SizeOf(ActualLF), @ActualLF);
|
||||
if ActualLF.lfCharSet = OEM_CHARSET then
|
||||
if (ActualLF.lfCharSet = OEM_CHARSET) and
|
||||
(ActualLF.lfHeight = BaseH) then
|
||||
FPaintFont := NewFont
|
||||
else
|
||||
DeleteObject(NewFont);
|
||||
end;
|
||||
|
||||
{ If Terminal doesn't have the exact size, try Perfect DOS VGA 437. }
|
||||
{ This TrueType CP437 font renders pixel-perfectly at cell height }
|
||||
{ multiples of 8 (8, 16, 24, 32...). Install via Control Panel > }
|
||||
{ Fonts before use. Request with DEFAULT_CHARSET so Win 3.1 doesn't }
|
||||
{ reject the TrueType font in favor of a raster OEM substitute. }
|
||||
if (FPaintFont = 0) and (BaseH >= 8) and (BaseH mod 8 = 0) then
|
||||
begin
|
||||
LF.lfCharSet := DEFAULT_CHARSET;
|
||||
LF.lfHeight := BaseH;
|
||||
StrPCopy(LF.lfFaceName, 'Perfect DOS VGA 437');
|
||||
NewFont := CreateFontIndirect(LF);
|
||||
if NewFont <> 0 then
|
||||
begin
|
||||
GetObject(NewFont, SizeOf(ActualLF), @ActualLF);
|
||||
if StrIComp(ActualLF.lfFaceName, LF.lfFaceName) = 0 then
|
||||
FPaintFont := NewFont
|
||||
else
|
||||
DeleteObject(NewFont);
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Fall back to Terminal raster at the nearest available size. Search }
|
||||
{ outward from the requested size (up then down). }
|
||||
if FPaintFont = 0 then
|
||||
begin
|
||||
LF.lfCharSet := OEM_CHARSET;
|
||||
StrPCopy(LF.lfFaceName, 'Terminal');
|
||||
for Delta := 1 to 12 do
|
||||
begin
|
||||
LF.lfHeight := BaseH + Delta;
|
||||
NewFont := CreateFontIndirect(LF);
|
||||
if NewFont <> 0 then
|
||||
begin
|
||||
GetObject(NewFont, SizeOf(ActualLF), @ActualLF);
|
||||
if (ActualLF.lfCharSet = OEM_CHARSET) and
|
||||
(ActualLF.lfHeight = BaseH + Delta) then
|
||||
begin
|
||||
FPaintFont := NewFont;
|
||||
Break;
|
||||
end;
|
||||
DeleteObject(NewFont);
|
||||
end;
|
||||
if (FPaintFont = 0) and (BaseH - Delta >= 4) then
|
||||
begin
|
||||
LF.lfHeight := BaseH - Delta;
|
||||
NewFont := CreateFontIndirect(LF);
|
||||
if NewFont <> 0 then
|
||||
begin
|
||||
GetObject(NewFont, SizeOf(ActualLF), @ActualLF);
|
||||
if (ActualLF.lfCharSet = OEM_CHARSET) and
|
||||
(ActualLF.lfHeight = BaseH - Delta) then
|
||||
begin
|
||||
FPaintFont := NewFont;
|
||||
Break;
|
||||
end;
|
||||
DeleteObject(NewFont);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Last resort: stock OEM font }
|
||||
if FPaintFont = 0 then
|
||||
begin
|
||||
FPaintFont := GetStockObject(OEM_FIXED_FONT);
|
||||
|
|
@ -406,6 +454,13 @@ begin
|
|||
end;
|
||||
|
||||
|
||||
procedure TKPAnsi.CreateWnd;
|
||||
begin
|
||||
inherited CreateWnd;
|
||||
RecalcCellSize;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKPAnsi.DeleteChars(N: Integer);
|
||||
var
|
||||
Line: PTermLine;
|
||||
|
|
@ -1846,11 +1901,16 @@ var
|
|||
RunBG: Byte;
|
||||
RunBuf: array[0..255] of Char;
|
||||
RunLen: Integer;
|
||||
DxBuf: array[0..255] of Integer;
|
||||
R: TRect;
|
||||
RowY: Integer;
|
||||
begin
|
||||
RowY := Row * FCellHeight;
|
||||
|
||||
{ Fill character spacing array -- forces uniform cell width for TrueType }
|
||||
for Col := 0 to FCols - 1 do
|
||||
DxBuf[Col] := FCellWidth;
|
||||
|
||||
{ Determine which line to render (handles scrollback view) }
|
||||
if FScrollPos <> 0 then
|
||||
begin
|
||||
|
|
@ -1926,7 +1986,7 @@ begin
|
|||
R.Right := (RunStart + RunLen) * FCellWidth;
|
||||
R.Bottom := RowY + FCellHeight;
|
||||
ExtTextOut(DC, RunStart * FCellWidth, RowY, ETO_OPAQUE, @R,
|
||||
@RunBuf[0], RunLen, nil);
|
||||
@RunBuf[0], RunLen, @DxBuf[0]);
|
||||
end;
|
||||
RunStart := Col;
|
||||
RunLen := 0;
|
||||
|
|
@ -1948,7 +2008,7 @@ begin
|
|||
R.Right := (RunStart + RunLen) * FCellWidth;
|
||||
R.Bottom := RowY + FCellHeight;
|
||||
ExtTextOut(DC, RunStart * FCellWidth, RowY, ETO_OPAQUE, @R,
|
||||
@RunBuf[0], RunLen, nil);
|
||||
@RunBuf[0], RunLen, @DxBuf[0]);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
|
@ -2019,6 +2079,20 @@ begin
|
|||
end;
|
||||
|
||||
|
||||
procedure TKPAnsi.SetFontSize(Value: Integer);
|
||||
begin
|
||||
if Value < 4 then
|
||||
Value := 4;
|
||||
if Value > 72 then
|
||||
Value := 72;
|
||||
if Value <> FFontSize then
|
||||
begin
|
||||
FFontSize := Value;
|
||||
RecalcCellSize;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKPAnsi.SetRows(Value: Integer);
|
||||
begin
|
||||
if Value < 1 then
|
||||
|
|
|
|||
333
delphi/KPANSI.md
Normal file
333
delphi/KPANSI.md
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
# TKPAnsi - ANSI BBS Terminal Emulation Component
|
||||
|
||||
TKPAnsi is a visual Delphi 1.0 component providing ANSI/VT100 terminal
|
||||
emulation with scrollback, cursor blinking, and ANSI music support. It is a
|
||||
TCustomControl descendant that renders incoming data using standard ANSI
|
||||
escape sequences for cursor positioning, color attributes, and screen
|
||||
manipulation.
|
||||
|
||||
Installs to the **KP** component palette tab.
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
### Published (Design-Time and Runtime)
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| FontSize | Integer | 12 | Font point size (4-72). Controls the OEM terminal font size. The control auto-sizes Width and Height from FontSize, Cols, and Rows. |
|
||||
| Cols | Integer | 80 | Terminal columns (1-256). |
|
||||
| Rows | Integer | 25 | Terminal rows (1-255). |
|
||||
| ScrollbackSize | Integer | 500 | Maximum scrollback history lines. |
|
||||
| CursorVisible | Boolean | True | Show or hide the cursor (also controlled by DEC mode ?25h/l). |
|
||||
| Color | TColor | clBlack | Control background color. |
|
||||
| OnKeyData | TKeyDataEvent | nil | Keyboard data callback. |
|
||||
| TabStop | Boolean | True | Accept keyboard focus. |
|
||||
|
||||
### Public (Read-Only)
|
||||
|
||||
| Property | Type | Description |
|
||||
|---|---|---|
|
||||
| CursorCol | Integer | Current 0-based cursor column. |
|
||||
| CursorRow | Integer | Current 0-based cursor row. |
|
||||
|
||||
|
||||
## Methods
|
||||
|
||||
### Public
|
||||
|
||||
| Method | Description |
|
||||
|---|---|
|
||||
| Create(AOwner) | Constructor. Allocates screen lines, initializes defaults. |
|
||||
| Destroy | Destructor. Frees paint font, screen, and scrollback. |
|
||||
| Clear | Moves the current screen to scrollback and allocates fresh blank lines. Resets the cursor to 0,0. |
|
||||
| Reset | Resets all attributes and parse state, then calls Clear. |
|
||||
| Write(S) | Parses the string and renders immediately. Convenience wrapper around WriteDeferredBuf + FlipToScreen. |
|
||||
| WriteDeferredBuf(Buf, Len) | Parses a PChar buffer into the cell buffer, then renders dirty rows. Used for bulk serial data. |
|
||||
| FlipToScreen | Renders all dirty rows to the screen DC. Handles coalesced scrolls, cursor ghost cleanup, and scrollbar updates. |
|
||||
| TickBlink | Toggles cursor and text blink state every 500 ms using GetTickCount. Call from the application's main loop. |
|
||||
|
||||
|
||||
## Events
|
||||
|
||||
### OnKeyData
|
||||
|
||||
```pascal
|
||||
TKeyDataEvent = procedure(Sender: TObject; const Data: string) of object;
|
||||
```
|
||||
|
||||
Fires when keyboard input produces data to send to the serial port.
|
||||
|
||||
| Key | Data sent |
|
||||
|---|---|
|
||||
| Printable characters | The character itself |
|
||||
| CR, LF, BS, TAB, ESC | The control character |
|
||||
| Arrow Up/Down/Right/Left | ESC[A / ESC[B / ESC[C / ESC[D |
|
||||
| Home / End | ESC[H / ESC[K |
|
||||
| Page Up / Page Down | ESC[V / ESC[U |
|
||||
| Insert | ESC[@ |
|
||||
| Delete | #127 |
|
||||
| F1-F10 | ESC OP through ESC OY |
|
||||
| ENQ (#5) | ESC[?1;0c (Device Attributes response) |
|
||||
| DSR query (ESC[6n) | ESC[row;colR (cursor position report) |
|
||||
|
||||
|
||||
## Types
|
||||
|
||||
### TParseState
|
||||
|
||||
```
|
||||
psNormal Normal character input
|
||||
psEscape Received ESC, awaiting next byte
|
||||
psCSI In CSI sequence (ESC[...), parsing parameters
|
||||
psCSIQuestion DEC private mode (ESC[?...)
|
||||
psMusic Accumulating ANSI music string (ESC[M...^N)
|
||||
```
|
||||
|
||||
### TTermCell
|
||||
|
||||
```pascal
|
||||
TTermCell = record
|
||||
Ch: Char; { Display character }
|
||||
FG: Byte; { Foreground palette index 0-15 }
|
||||
BG: Byte; { Background palette index 0-15 }
|
||||
Bold: Boolean; { Bold attribute (FG +8 at render) }
|
||||
Blink: Boolean; { Blink attribute (toggles on TickBlink) }
|
||||
end;
|
||||
```
|
||||
|
||||
### TTermLineRec
|
||||
|
||||
```pascal
|
||||
TTermLineRec = record
|
||||
Cells: array[0..255] of TTermCell;
|
||||
end;
|
||||
PTermLine = ^TTermLineRec;
|
||||
```
|
||||
|
||||
### ANSI Color Palette
|
||||
|
||||
Standard 16-color ANSI palette in BGR TColor order:
|
||||
|
||||
```
|
||||
0 Black 8 Dark Gray
|
||||
1 Red 9 Bright Red
|
||||
2 Green 10 Bright Green
|
||||
3 Brown/Yellow 11 Bright Yellow
|
||||
4 Blue 12 Bright Blue
|
||||
5 Magenta 13 Bright Magenta
|
||||
6 Cyan 14 Bright Cyan
|
||||
7 Light Gray 15 Bright White
|
||||
```
|
||||
|
||||
|
||||
## ANSI Escape Sequences
|
||||
|
||||
### CSI Sequences (ESC[ params final-byte)
|
||||
|
||||
| Final | Name | Description |
|
||||
|---|---|---|
|
||||
| A | CUU | Cursor up P1 rows (default 1) |
|
||||
| B | CUD | Cursor down P1 rows (default 1) |
|
||||
| C | CUF | Cursor forward P1 columns (default 1) |
|
||||
| D | CUB | Cursor back P1 columns (default 1) |
|
||||
| H, f | CUP/HVP | Cursor position to row P1, column P2 (1-based) |
|
||||
| J | ED | Erase display: 0=below, 1=above, 2=all |
|
||||
| K | EL | Erase line: 0=to end, 1=from start, 2=entire |
|
||||
| L | IL | Insert P1 blank lines at cursor |
|
||||
| M | DL | Delete P1 lines at cursor |
|
||||
| P | DCH | Delete P1 characters at cursor |
|
||||
| S | SU | Scroll up P1 lines |
|
||||
| T | SD | Scroll down P1 lines |
|
||||
| @ | ICH | Insert P1 blank characters at cursor |
|
||||
| m | SGR | Set graphic rendition (see below) |
|
||||
| s | SCP | Save cursor position |
|
||||
| u | RCP | Restore cursor position |
|
||||
| c | DA | Device attributes (P1=0: respond ESC[?1;0c) |
|
||||
| n | DSR | Device status report (P1=5: OK, P1=6: cursor pos) |
|
||||
|
||||
### SGR Codes (ESC[ code m)
|
||||
|
||||
| Code | Effect |
|
||||
|---|---|
|
||||
| 0 | Reset all attributes |
|
||||
| 1 | Bold (maps FG index +8 for bright colors) |
|
||||
| 5 | Blink (cell visibility toggles every 500 ms) |
|
||||
| 7 | Reverse video (swap FG and BG at render) |
|
||||
| 22 | Normal intensity (cancel bold) |
|
||||
| 25 | Blink off |
|
||||
| 27 | Reverse off |
|
||||
| 30-37 | Foreground color (black, red, green, yellow, blue, magenta, cyan, white) |
|
||||
| 40-47 | Background color (same order) |
|
||||
|
||||
### DEC Private Modes (ESC[? code h/l)
|
||||
|
||||
| Mode | h (set) | l (reset) |
|
||||
|---|---|---|
|
||||
| ?7 | Enable auto-wrap at right margin | Disable auto-wrap |
|
||||
| ?25 | Show cursor | Hide cursor |
|
||||
|
||||
### Control Characters
|
||||
|
||||
| Char | Action |
|
||||
|---|---|
|
||||
| BEL (#7) | MessageBeep |
|
||||
| BS (#8) | Cursor left one column |
|
||||
| TAB (#9) | Advance to next tab stop (multiple of 8) |
|
||||
| LF (#10) | Cursor down; scroll at bottom |
|
||||
| CR (#13) | Cursor to column 0 |
|
||||
| ENQ (#5) | Respond with device attributes |
|
||||
|
||||
|
||||
## ANSI Music
|
||||
|
||||
ANSI music is triggered by `ESC[M` and terminated by `^N` (Ctrl-N, #14).
|
||||
The string between these delimiters contains musical notation:
|
||||
|
||||
| Command | Description |
|
||||
|---|---|
|
||||
| T*n* | Set tempo in BPM (32-255) |
|
||||
| L*n* | Set default note length (1=whole, 2=half, 4=quarter, 8=eighth) |
|
||||
| O*n* | Set octave (0-7) |
|
||||
| < / > | Shift octave down / up |
|
||||
| A-G | Play note (optional # or + for sharp, - for flat) |
|
||||
| P*n* | Pause for duration *n* |
|
||||
|
||||
Notes accept an optional duration digit after the letter (e.g., C4 = quarter
|
||||
note C). A trailing dot adds 50% to the duration.
|
||||
|
||||
Uses the Windows 3.1 sound API: OpenSound, SetVoiceAccent, SetVoiceNote,
|
||||
StartSound, CloseSound.
|
||||
|
||||
|
||||
## Font Selection
|
||||
|
||||
CreatePaintFont selects the best available font for the requested FontSize.
|
||||
The selection order ensures pixel-perfect rendering when possible:
|
||||
|
||||
1. **Terminal raster at the exact size.** If the Terminal font has a raster
|
||||
at the requested cell height, use it. Terminal is pixel-perfect but only
|
||||
available at specific heights (typically 12 and 24 pixels on VGA).
|
||||
|
||||
2. **Perfect DOS VGA 437 at multiples of 8.** If the requested cell height
|
||||
is a multiple of 8 (8, 16, 24, 32, ...) and the font is installed, use
|
||||
it. This TrueType CP437 font renders pixel-perfectly at these sizes.
|
||||
Install `DOSVGA.TTF` via Control Panel > Fonts.
|
||||
|
||||
3. **Terminal raster at the nearest available size.** Search outward from
|
||||
the requested height (alternating up and down) to find the closest
|
||||
Terminal raster variant.
|
||||
|
||||
4. **Stock OEM_FIXED_FONT.** Last resort. Always available.
|
||||
|
||||
### Supported Point Sizes (96 DPI)
|
||||
|
||||
| FontSize | Cell Height | Font Used |
|
||||
|---|---|---|
|
||||
| 6 | 8 | Perfect DOS VGA 437 |
|
||||
| 9 | 12 | Terminal raster |
|
||||
| 12 | 16 | Perfect DOS VGA 437 |
|
||||
| 18 | 24 | Terminal raster |
|
||||
| 24 | 32 | Perfect DOS VGA 437 |
|
||||
| 30 | 40 | Perfect DOS VGA 437 |
|
||||
| 36 | 48 | Perfect DOS VGA 437 |
|
||||
|
||||
Other sizes fall back to the nearest Terminal raster height.
|
||||
|
||||
### Installing the Perfect DOS VGA 437 Font
|
||||
|
||||
1. Open Control Panel and double-click Fonts.
|
||||
2. Click Add.
|
||||
3. Browse to the directory containing `DOSVGA.TTF` and select it.
|
||||
4. Click OK to install.
|
||||
|
||||
The font is free for personal and commercial use. See `fonts/DOSVGA.TXT`
|
||||
for license details.
|
||||
|
||||
|
||||
## Rendering Architecture
|
||||
|
||||
### Split-Phase Design
|
||||
|
||||
Rendering is split into parsing and drawing to avoid interleaving CPU work
|
||||
with GDI kernel transitions:
|
||||
|
||||
1. **Parse phase** (ParseDataBuf): Pure CPU loop that fills the FScreen cell
|
||||
buffer with characters and attributes. No GDI calls. Three inline fast
|
||||
paths eliminate function-call overhead for printable runs, CSI parameter
|
||||
accumulation, and common control characters (ESC, CR, LF).
|
||||
|
||||
2. **Render phase** (FlipToScreen or WriteDeferredBuf): Flushes coalesced
|
||||
scrolls via a single ScrollDC call, then redraws only dirty rows via
|
||||
RenderRow.
|
||||
|
||||
### Dirty Row Tracking
|
||||
|
||||
Each row has a boolean dirty flag (FDirtyRow). FAllDirty forces a full
|
||||
redraw. DirtyBlinkRows marks only the cursor row and rows containing blink
|
||||
cells, reducing blink overhead from ~63 ms to ~3 ms on a 486.
|
||||
|
||||
### RenderRow
|
||||
|
||||
Scans cells for color runs (consecutive cells with the same effective FG and
|
||||
BG). Each run is drawn with a single ExtTextOut call using ETO_OPAQUE
|
||||
(fills background and renders text in one operation). A DxBuf array forces
|
||||
uniform character spacing for TrueType fonts.
|
||||
|
||||
### Scroll Coalescing
|
||||
|
||||
Multiple scroll-up operations during a parse batch are accumulated in
|
||||
FPendingScrolls. The render phase issues one ScrollDC to shift on-screen
|
||||
pixels, then only redraws the newly exposed bottom rows.
|
||||
|
||||
### Blink
|
||||
|
||||
Cursor and text blink are driven by TickBlink, which uses GetTickCount to
|
||||
toggle FBlinkOn every 500 ms. No WM_TIMER is used. The application calls
|
||||
TickBlink from its main loop.
|
||||
|
||||
|
||||
## Scrollback Buffer
|
||||
|
||||
- **FScreen**: TList of PTermLine holding the active terminal display.
|
||||
- **FScrollback**: TList of PTermLine holding history (up to ScrollbackSize).
|
||||
- **FScrollPos**: 0 = live view, >0 = viewing history (lines scrolled back).
|
||||
- Vertical scrollbar (WS_VSCROLL) allows the user to scroll through history.
|
||||
- TrimScrollback batch-frees excess lines in a single pass.
|
||||
|
||||
|
||||
## Usage Example
|
||||
|
||||
```pascal
|
||||
FAnsi := TKPAnsi.Create(Self);
|
||||
FAnsi.FontSize := 12;
|
||||
FAnsi.Parent := Self;
|
||||
FAnsi.Left := 0;
|
||||
FAnsi.Top := 38;
|
||||
FAnsi.OnKeyData := AnsiKeyData;
|
||||
|
||||
{ In the main loop: }
|
||||
Len := FComm.ReadInputBuf(@Buf, BufSize);
|
||||
if Len > 0 then
|
||||
FAnsi.WriteDeferredBuf(@Buf, Len);
|
||||
FAnsi.TickBlink;
|
||||
FAnsi.FlipToScreen;
|
||||
|
||||
{ Size the form to fit: }
|
||||
ClientWidth := FAnsi.Width;
|
||||
ClientHeight := FAnsi.Top + FAnsi.Height;
|
||||
```
|
||||
|
||||
|
||||
## Platform Notes
|
||||
|
||||
- Targets Windows 3.1 / Delphi 1.0.
|
||||
- Uses CS_OWNDC for a persistent device context (font and background mode
|
||||
survive across GetDC/ReleaseDC cycles).
|
||||
- Uses `{ }` comments (Delphi 1.0 does not support `//` comments).
|
||||
- The Perfect DOS VGA 437 TrueType font requires Windows 3.1's TrueType
|
||||
rasterizer. The font's cmap was converted from format 6 to format 0 and
|
||||
its OS/2 table downgraded from version 3 to version 1 for Win 3.1
|
||||
compatibility.
|
||||
- Requested with DEFAULT_CHARSET (not OEM_CHARSET) to prevent Win 3.1's
|
||||
font mapper from rejecting TrueType fonts in favor of raster substitutes.
|
||||
139
delphi/KPCOMM.md
Normal file
139
delphi/KPCOMM.md
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
# TKPComm - Serial Communications Component
|
||||
|
||||
TKPComm is a non-visual Delphi 1.0 component providing serial port access
|
||||
via the Windows 3.1 comm API. It is a TComponent descendant that wraps
|
||||
OpenComm, ReadComm, WriteComm, and related functions into a property-driven
|
||||
interface. The application polls for received data; there is no event-driven
|
||||
notification infrastructure (no hidden window, no WM_COMMNOTIFY, no
|
||||
OnComm event).
|
||||
|
||||
Installs to the **KP** component palette tab.
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
### Published (Design-Time and Runtime)
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| CommPort | Integer | 1 | Port number 1-16 (1=COM1, 2=COM2, etc.). Cannot be changed while port is open. |
|
||||
| Settings | string | '9600,N,8,1' | Baud rate, parity, data bits, stop bits. Format: `baud,parity,databits,stopbits`. Can be changed while port is open to reconfigure on the fly. |
|
||||
| PortOpen | Boolean | False | Opens or closes the serial port. Setting True calls OpenComm, BuildCommDCB, and SetCommState; False calls CloseComm. |
|
||||
| InBufferSize | Integer | 4096 | Receive ring buffer size in bytes (minimum 64). Cannot be changed while port is open. |
|
||||
| OutBufferSize | Integer | 4096 | Transmit ring buffer size in bytes (minimum 64). Cannot be changed while port is open. |
|
||||
| Handshaking | THandshaking | hsNone | Flow control method (see below). Can be changed while port is open. |
|
||||
| InputLen | Integer | 0 | Maximum bytes returned per Input read. 0 means up to 255 bytes. |
|
||||
| InputMode | TInputMode | imText | Data reading mode. imBinary preserves embedded null bytes. |
|
||||
| DTREnable | Boolean | True | Assert Data Terminal Ready when True. Toggled immediately if port is open. |
|
||||
| RTSEnable | Boolean | True | Assert Request To Send when True. Toggled immediately if port is open. |
|
||||
| NullDiscard | Boolean | False | Strip null bytes from received data. |
|
||||
| EOFEnable | Boolean | False | Enable EOF character detection. |
|
||||
| ParityReplace | string | '?' | Character substituted for parity errors. Empty string disables replacement. |
|
||||
|
||||
### Public (Runtime Only)
|
||||
|
||||
| Property | Type | Access | Description |
|
||||
|---|---|---|---|
|
||||
| Input | string | Read | Reads available data from the receive buffer. Limited to 255 bytes per call (Delphi short string). Use ReadInputBuf for larger reads. |
|
||||
| Output | string | Write | Writes data to the transmit buffer. Raises an exception if the port is not open or the write fails. |
|
||||
| InBufferCount | Integer | Read | Bytes currently waiting in the receive buffer. |
|
||||
| OutBufferCount | Integer | Read | Bytes currently waiting in the transmit buffer. |
|
||||
| Break | Boolean | Read/Write | Asserts or clears the break signal via SetCommBreak/ClearCommBreak. |
|
||||
|
||||
|
||||
## Methods
|
||||
|
||||
### Public
|
||||
|
||||
| Method | Description |
|
||||
|---|---|
|
||||
| Create(AOwner) | Constructor. Initializes defaults; FCommId set to -1 (closed). |
|
||||
| Destroy | Destructor. Closes the port if still open. |
|
||||
| ReadInputBuf(Buf, BufSize): Integer | Reads up to BufSize bytes into a caller-supplied PChar buffer. Returns the number of bytes actually read. Bypasses the 255-byte short string limit of the Input property. |
|
||||
|
||||
|
||||
## Types
|
||||
|
||||
### THandshaking
|
||||
|
||||
```
|
||||
hsNone No flow control
|
||||
hsXonXoff Software XON/XOFF (XON=$11, XOFF=$13)
|
||||
hsRtsCts Hardware RTS/CTS
|
||||
hsBoth Combined RTS/CTS and XON/XOFF
|
||||
```
|
||||
|
||||
### TInputMode
|
||||
|
||||
```
|
||||
imText Text mode (default)
|
||||
imBinary Binary mode (preserves embedded nulls)
|
||||
```
|
||||
|
||||
|
||||
## DCB Flag Constants
|
||||
|
||||
Bit masks for the packed TDCB.Flags field:
|
||||
|
||||
| Constant | Value | Description |
|
||||
|---|---|---|
|
||||
| dcbBinary | $0001 | Binary mode (always set) |
|
||||
| dcbRtsDisable | $0002 | RTS disabled |
|
||||
| dcbParity | $0004 | Parity check/replacement |
|
||||
| dcbOutxCtsFlow | $0008 | CTS output flow control |
|
||||
| dcbOutxDsrFlow | $0010 | DSR output flow control |
|
||||
| dcbDtrDisable | $0020 | DTR disabled |
|
||||
| dcbOutX | $0040 | XON/XOFF output flow control |
|
||||
| dcbInX | $0080 | XON/XOFF input flow control |
|
||||
| dcbPeChar | $0100 | Parity error character replacement |
|
||||
| dcbNull | $0200 | Null byte stripping |
|
||||
| dcbChEvt | $0400 | Character event notification |
|
||||
| dcbDtrflow | $0800 | DTR flow control |
|
||||
| dcbRtsflow | $1000 | RTS flow control |
|
||||
|
||||
|
||||
## Port Lifecycle
|
||||
|
||||
1. Create the component and set properties (CommPort, Settings, buffer sizes,
|
||||
Handshaking, etc.).
|
||||
2. Set `PortOpen := True`. This calls OpenComm, builds the DCB from
|
||||
the Settings string, applies flow control, null discard, parity replace,
|
||||
and asserts DTR/RTS.
|
||||
3. Poll `Input` or call `ReadInputBuf` in the application's main loop.
|
||||
Set `Output` to send data.
|
||||
4. Set `PortOpen := False` to close. DTR and RTS are cleared, break is
|
||||
released, and CloseComm is called.
|
||||
5. The destructor closes the port automatically if still open.
|
||||
|
||||
|
||||
## Usage Example
|
||||
|
||||
```pascal
|
||||
FComm := TKPComm.Create(Self);
|
||||
FComm.CommPort := 1;
|
||||
FComm.Settings := '115200,N,8,1';
|
||||
FComm.PortOpen := True;
|
||||
|
||||
{ In the main loop: }
|
||||
Len := FComm.ReadInputBuf(@Buf, SizeOf(Buf));
|
||||
if Len > 0 then
|
||||
ProcessData(@Buf, Len);
|
||||
|
||||
{ To send data: }
|
||||
FComm.Output := 'AT' + #13;
|
||||
```
|
||||
|
||||
|
||||
## Platform Notes
|
||||
|
||||
- Targets Windows 3.1 using the 16-bit comm API (OpenComm, ReadComm,
|
||||
WriteComm, GetCommState, SetCommState, GetCommError,
|
||||
EscapeCommFunction, BuildCommDCB).
|
||||
- No notification infrastructure. The application must poll for data.
|
||||
There is no hidden window, no WM_COMMNOTIFY handling, and
|
||||
EnableCommNotification is not used.
|
||||
- The Input property is limited to 255 bytes by Delphi 1.0's short
|
||||
string type. Use ReadInputBuf for bulk reads (2048+ bytes per call).
|
||||
- EscapeCommFunction returns 0 on success in the 16-bit API (opposite
|
||||
of the Win32 convention).
|
||||
- Uses `{ }` comments (Delphi 1.0 does not support `//` comments).
|
||||
|
|
@ -6,7 +6,7 @@ unit TestMain;
|
|||
{ Layout: toolbar row at top (port, settings, open/close, status), }
|
||||
{ TKPAnsi terminal filling the rest of the form. Received serial data }
|
||||
{ is polled from TKPComm.Input in a PeekMessage main loop; keystrokes }
|
||||
{ from the terminal are sent out via TKPComm.Output. }
|
||||
{ from the terminal are sent to the serial port via TKPComm.Output. }
|
||||
|
||||
interface
|
||||
|
||||
|
|
@ -17,19 +17,15 @@ uses
|
|||
type
|
||||
TMainForm = class(TForm)
|
||||
private
|
||||
{ Components (owned by Self, freed automatically) }
|
||||
FComm: TKPComm; { Serial communications component }
|
||||
FAnsi: TKPAnsi; { ANSI terminal display }
|
||||
|
||||
{ Toolbar controls }
|
||||
FLabelPort: TLabel; { "Port:" label }
|
||||
FLabelPort: TLabel; { "Port:" caption }
|
||||
FEditPort: TEdit; { COM port number entry }
|
||||
FLabelSettings: TLabel; { "Settings:" label }
|
||||
FLabelSettings: TLabel; { "Settings:" caption }
|
||||
FEditSettings: TEdit; { Baud/parity/data/stop entry }
|
||||
FBtnOpen: TButton; { Opens the serial port }
|
||||
FBtnClose: TButton; { Closes the serial port }
|
||||
FLabelStatus: TLabel; { Displays "Open" or "Closed" }
|
||||
|
||||
FDone: Boolean; { True when WM_QUIT received }
|
||||
procedure AnsiKeyData(Sender: TObject; const Data: string);
|
||||
procedure BtnCloseClick(Sender: TObject);
|
||||
|
|
@ -46,6 +42,7 @@ var
|
|||
implementation
|
||||
|
||||
|
||||
{ OnKeyData handler: sends terminal keystrokes to the serial port }
|
||||
procedure TMainForm.AnsiKeyData(Sender: TObject; const Data: string);
|
||||
begin
|
||||
if FComm.PortOpen and (Length(Data) > 0) then
|
||||
|
|
@ -67,6 +64,7 @@ begin
|
|||
end;
|
||||
|
||||
|
||||
{ Reads port number and settings from the toolbar, opens the port }
|
||||
procedure TMainForm.BtnOpenClick(Sender: TObject);
|
||||
begin
|
||||
try
|
||||
|
|
@ -89,10 +87,9 @@ begin
|
|||
Caption := 'KPComm ANSI Terminal';
|
||||
BorderStyle := bsSingle;
|
||||
|
||||
{ Serial component }
|
||||
FComm := TKPComm.Create(Self);
|
||||
|
||||
{ Row 1: Port and Settings }
|
||||
{ Toolbar row }
|
||||
FLabelPort := TLabel.Create(Self);
|
||||
FLabelPort.Parent := Self;
|
||||
FLabelPort.Left := 8;
|
||||
|
|
@ -144,19 +141,22 @@ begin
|
|||
FLabelStatus.Top := 12;
|
||||
FLabelStatus.Caption := 'Closed';
|
||||
|
||||
{ ANSI terminal }
|
||||
{ ANSI terminal -- FontSize controls the OEM terminal font point size. }
|
||||
{ The control auto-sizes Width/Height from FontSize, Cols, and Rows. }
|
||||
FAnsi := TKPAnsi.Create(Self);
|
||||
FAnsi.FontSize := 12;
|
||||
FAnsi.Parent := Self;
|
||||
FAnsi.Left := 0;
|
||||
FAnsi.Top := 38;
|
||||
FAnsi.OnKeyData := AnsiKeyData;
|
||||
|
||||
end;
|
||||
|
||||
|
||||
{ PeekMessage polling loop. Drains Windows messages, polls serial }
|
||||
{ data from TKPComm, and yields CPU when idle. }
|
||||
procedure TMainForm.Run;
|
||||
const
|
||||
BufSize = 2048; { Read buffer -- 8x larger than 255-byte string limit }
|
||||
BufSize = 2048;
|
||||
var
|
||||
Msg: TMsg;
|
||||
Buf: array[0..BufSize - 1] of Char;
|
||||
|
|
@ -173,7 +173,6 @@ begin
|
|||
FDone := False;
|
||||
while not FDone do
|
||||
begin
|
||||
{ Process all pending Windows messages (keyboard, paint, scrollbar) }
|
||||
while PeekMessage(Msg, 0, 0, 0, pm_Remove or pm_NoYield) do
|
||||
begin
|
||||
if Msg.message = wm_Quit then
|
||||
|
|
@ -188,9 +187,7 @@ begin
|
|||
if FDone then
|
||||
Break;
|
||||
|
||||
{ Drain all available serial data. WriteDeferredBuf renders each }
|
||||
{ character run immediately via ExtTextOut -- no deferred pass. }
|
||||
{ Messages are checked between chunks so keyboard stays responsive. }
|
||||
{ Read serial data into the terminal }
|
||||
HasData := False;
|
||||
if FComm.PortOpen then
|
||||
begin
|
||||
|
|
@ -199,7 +196,6 @@ begin
|
|||
begin
|
||||
FAnsi.WriteDeferredBuf(@Buf, Len);
|
||||
HasData := True;
|
||||
{ Check for messages between chunks }
|
||||
while PeekMessage(Msg, 0, 0, 0, pm_Remove or pm_NoYield) do
|
||||
begin
|
||||
if Msg.message = wm_Quit then
|
||||
|
|
@ -217,22 +213,18 @@ begin
|
|||
if FDone then
|
||||
Break;
|
||||
|
||||
{ Blink + dirty-row pass. During normal data flow, WriteDeferredBuf }
|
||||
{ already rendered inline so FlipToScreen is a no-op. Only blink }
|
||||
{ toggle (every 500ms) or scrollbar updates produce dirty rows here. }
|
||||
{ Update cursor blink and repaint any dirty rows }
|
||||
FAnsi.TickBlink;
|
||||
FAnsi.FlipToScreen;
|
||||
|
||||
{ Yield CPU to other apps when no serial data is flowing. }
|
||||
{ PM_NOYIELD keeps message draining fast; Yield here gives other }
|
||||
{ apps a timeslice only when idle. During bulk data flow, HasData }
|
||||
{ stays True and the loop runs at full speed. }
|
||||
{ Yield CPU when no serial data is flowing }
|
||||
if not HasData then
|
||||
Yield;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
{ Syncs button enabled state and status label with port open/closed }
|
||||
procedure TMainForm.UpdateStatus;
|
||||
begin
|
||||
if FComm.PortOpen then
|
||||
|
|
|
|||
BIN
delphi/fonts/DOSVGA.TTF
(Stored with Git LFS)
Normal file
BIN
delphi/fonts/DOSVGA.TTF
(Stored with Git LFS)
Normal file
Binary file not shown.
104
delphi/fonts/DOSVGA.TXT
Normal file
104
delphi/fonts/DOSVGA.TXT
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/
|
||||
/(_____________ ____
|
||||
\ /______)\ | |
|
||||
:\ | / \:| |:::::::::: : .. . : .. . . :. .
|
||||
\_____| / | \| |______
|
||||
___ / ________ \... . . .
|
||||
\______________ \ | | /.. . . . . .
|
||||
\ |__| /
|
||||
--x--x-----x----\______ |-/_____/-x--x-xx--x-- - -x -- - - -- - - -
|
||||
. . . . . . . . . . . .\____|. . . . . .
|
||||
-------------------------------------------------------------------------------
|
||||
>> perfect dos vga 437 - general information >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
"Perfect DOS VGA 437" and "Perfect DOS VGA 437 Win" are truetype fonts
|
||||
designed to emulate the MS-DOS/Text mode standard font, used on VGA monitors,
|
||||
with the 437 Codepage (standard US/International). This is a "bitmap" font,
|
||||
meaning it emulates a bitmap font and can only be used at a given size (8 or
|
||||
multiples of it like 16, 24, 32, etc). It's optimized for Flash too, so it
|
||||
won't produce antialias if used at round positions.
|
||||
|
||||
There are two fonts available. "Perfect DOS VGA 437" uses the original DOS
|
||||
codepage 437. It should be used, for example, if you're opening DOS ASCII
|
||||
files on notepad or another windows-based editor. Since it's faithful to the
|
||||
original DOS codes, it won't accent correctly in windows ("é" would produce
|
||||
something different, not an "e" with an acute).
|
||||
|
||||
There's also "Perfect DOS VGA 437 Win" which is the exactly same font adapted
|
||||
to a windows codepage. This should use accented characters correctly but won't
|
||||
work if you're opening a DOS-based text file.
|
||||
|
||||
UPDATE: this is a new version, updated in august/2008. It has fixed leading
|
||||
metrics for Mac systems.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
>> perfect dos vga 437 - creation process >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
This font was created to be used on a Flash-based ANSi viewer I'm working. To
|
||||
create it, I created a small Quick Basic program to write all characters on
|
||||
screen,
|
||||
|
||||
CLS
|
||||
FOR l = 0 TO 255
|
||||
charWrite 1 + (l MOD 20), 1 + (l \ 20) * 6 + (l MOD 2), LTRIM$(RTRIM$(STR$(l))) + CHR$(l)
|
||||
NEXT
|
||||
SUB charWrite (lin, col, char$)
|
||||
DEF SEG = &HB800
|
||||
FOR i = 1 TO LEN(char$)
|
||||
POKE ((lin - 1) * 160) + ((col - 2 + i) * 2), ASC(MID$(char$, i, 1))
|
||||
IF (i = LEN(char$)) THEN POKE ((lin - 1) * 160) + ((col - 2 + i) * 2) + 1, 113
|
||||
NEXT
|
||||
END SUB
|
||||
|
||||
Then captured the text screen using SCREEN THIEF (a very, very old screen
|
||||
capture TSR program which converts text screens to images accurately). I then
|
||||
recreated the font polygon by polygon on Fontlab, while looking at the image
|
||||
on Photoshop. No conversion took place.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
>> copyright and stuff >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
This is a free font/file, distribute as you wish to who you wish. You are free
|
||||
to use it on a movie, a videogame, a video, a broadcast, without having to ask
|
||||
my permission.
|
||||
|
||||
Please do not send me emails asking me to sign release forms if it require
|
||||
any sort of scanning or digital compositing. It's a big chore. This license
|
||||
file and a simple confirmation email should suffice as proof that you are
|
||||
allowed to use it.
|
||||
|
||||
Of course I don't mind emails letting me know where something has been used.
|
||||
Those are always gratifying!
|
||||
|
||||
Do NOT sell this font. It's not yours and you can't make money of it.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
>> author >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Zeh Fernando
|
||||
zeh@zehfernando.com
|
||||
www.zehfernando.com
|
||||
|
||||
rorshack ^ maiden brazil
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
>> other notes >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
The year is now 2021. I would be remiss not to mention these more modern font
|
||||
packages:
|
||||
|
||||
https://int10h.org/oldschool-pc-fonts/fontlist/
|
||||
|
||||
They include VGA-like fonts and a bunch of other systems, easily supplanting
|
||||
the need for "Perfect DOS VGA" and then some.
|
||||
|
||||
They use a Creative Commons license.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
^zehPULLSdahTRICK^kudosOUTtoWHOkeepsITreal^smashDAHfuckingENTAH!!!^lowres4ever^
|
||||
-------------------------------------------------------------------------------
|
||||
Loading…
Add table
Reference in a new issue