Стартовый пул

This commit is contained in:
2024-04-02 08:46:59 +03:00
parent fd57fffd3a
commit 3bb34d000b
5591 changed files with 3291734 additions and 0 deletions

View File

@@ -0,0 +1,52 @@
# BGRABitmap Assistant: https://chat.openai.com/g/g-FO7aiutRx-bgrabitmap-assistant
# This program extracts method signatures in BGRABitmap to help provide accurate examples
import os
import re
def gather_method_signatures(source_dir):
print("Source directory:", source_dir)
regexMethod = r"(?P<proc_or_fun>procedure|function)\s+(?P<class>\w+)\.(?P<method>\w+)(\((?P<parameters>[^)]*)\))?(:(?P<return_type>[^;]+))?;"
patternMethod = re.compile(regexMethod)
signatures = []
classes = set()
for root, dirs, files in os.walk(source_dir):
for file in files:
if file.endswith('.pas') or file.endswith('.inc'):
print("Source file:", file)
with open(os.path.join(root, file), 'r') as source_file:
content = source_file.read()
matches = patternMethod.finditer(content)
for match in matches:
proc_or_fun = match.group('proc_or_fun')
class_name = match.group('class')
method_name = match.group('method')
parameters = match.group('parameters')
if parameters is None:
parameters = ""
else:
parameters = re.sub(r'\s', '', parameters).replace(':', ': ').replace(';', '; ')
return_type = match.group('return_type')
if return_type is None:
return_type = ""
else:
return_type = return_type.strip()
method_signature = f'{proc_or_fun} {class_name}.{method_name}({parameters})'
if proc_or_fun == "function":
method_signature += f': {return_type}'
signatures.append(f"{method_signature}; {{in {file}}}")
classes.add(class_name)
return signatures, classes
app_directory = os.path.dirname(__file__)
source_directory = os.path.join(app_directory, '../../bgrabitmap')
signatures, classes = gather_method_signatures(source_directory)
target_file = os.path.join(app_directory, 'all.pas')
with open(target_file, "w") as file:
for line in signatures:
file.write(line + "\n")
target_file = os.path.join(app_directory, 'all_classes.txt')
with open(target_file, "w") as file:
file.write(", ".join(classes))

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="11"/>
<General>
<Flags>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasTitleStatement Value="False"/>
<MainUnitHasScaledStatement Value="False"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<MainUnit Value="0"/>
<Title Value="generatecolorspaces"/>
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
</General>
<BuildModes Count="1">
<Item1 Name="Default" Default="True"/>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
</PublishOptions>
<RunParams>
<FormatVersion Value="2"/>
<Modes Count="1">
<Mode0 Name="default"/>
</Modes>
</RunParams>
<Units Count="2">
<Unit0>
<Filename Value="generatecolorspaces.lpr"/>
<IsPartOfProject Value="True"/>
</Unit0>
<Unit1>
<Filename Value="unitmakerunit.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="UnitMakerUnit"/>
</Unit1>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<Target>
<Filename Value="generatecolorspaces"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Other>
<CustomOptions Value="-dColorHelpersMaker"/>
</Other>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">
<Item1>
<Name Value="EAbort"/>
</Item1>
<Item2>
<Name Value="ECodetoolError"/>
</Item2>
<Item3>
<Name Value="EFOpenError"/>
</Item3>
</Exceptions>
</Debugging>
</CONFIG>

View File

@@ -0,0 +1,8 @@
program generatecolorspaces;
uses UnitMakerUnit;
begin
GenerateCode;
end.

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="12"/>
<PathDelim Value="\"/>
<General>
<Flags>
<CompatibilityMode Value="True"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<Title Value="pmakedoc"/>
<ResourceType Value="res"/>
<UseXPManifest Value="True"/>
<Icon Value="0"/>
</General>
<i18n>
<EnableI18N LFM="False"/>
</i18n>
<BuildModes Count="1">
<Item1 Name="Default" Default="True"/>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
</PublishOptions>
<RunParams>
<FormatVersion Value="2"/>
<Modes Count="1">
<Mode0 Name="default"/>
</Modes>
</RunParams>
<RequiredPackages Count="1">
<Item1>
<PackageName Value="LCL"/>
</Item1>
</RequiredPackages>
<Units Count="2">
<Unit0>
<Filename Value="pmakedoc.lpr"/>
<IsPartOfProject Value="True"/>
</Unit0>
<Unit1>
<Filename Value="unit1.pas"/>
<IsPartOfProject Value="True"/>
<ComponentName Value="Form1"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="Unit1"/>
</Unit1>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<PathDelim Value="\"/>
<Target>
<Filename Value="pmakedoc"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Linking>
<Debugging>
<DebugInfoType Value="dsDwarf3"/>
</Debugging>
<Options>
<Win32>
<GraphicApplication Value="True"/>
</Win32>
</Options>
</Linking>
<Other>
<ConfigFile>
<WriteConfigFilePath Value="$(ProjOutDir)\fpclaz.cfg"/>
</ConfigFile>
</Other>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">
<Item1>
<Name Value="EAbort"/>
</Item1>
<Item2>
<Name Value="ECodetoolError"/>
</Item2>
<Item3>
<Name Value="EFOpenError"/>
</Item3>
</Exceptions>
</Debugging>
</CONFIG>

View File

@@ -0,0 +1,21 @@
program pmakedoc;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset
Forms, Unit1
{ you can add units after this };
{$R *.res}
begin
RequireDerivedFormResource := True;
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.

View File

@@ -0,0 +1,14 @@
Some pages on the wiki are generated by this program:
http://wiki.freepascal.org/BGRABitmap#BGRABitmapTypes_unit_reference
The idea is to add comments with a specific format:
- on the line preceding a declaration
- using { } brackets
- starting with * for a main entry. Example:
{* Specifies the shape of a predefined blur }
- starting with ** for a sub-entry. Example, one enumeration:
{** Gaussian-like, pixel importance decreases progressively }
The sections titles are prefixed and suffixed by ===. Example:
{=== Miscellaneous types ===}

View File

@@ -0,0 +1,59 @@
object Form1: TForm1
Left = 379
Height = 331
Top = 219
Width = 536
Caption = 'Form1'
ClientHeight = 331
ClientWidth = 536
LCLVersion = '3.99.0.0'
OnCreate = FormCreate
object EPath: TEdit
Left = 8
Height = 22
Top = 30
Width = 520
Anchors = [akTop, akLeft, akRight]
TabOrder = 0
Text = '../../bgrabitmap'
end
object Label1: TLabel
Left = 9
Height = 16
Top = 9
Width = 89
Caption = 'Path of library:'
ParentColor = False
end
object Memo1: TMemo
Left = 8
Height = 235
Top = 88
Width = 518
Anchors = [akTop, akLeft, akRight, akBottom]
ScrollBars = ssBoth
TabOrder = 1
end
object Button1: TButton
Left = 8
Height = 25
Top = 56
Width = 96
Caption = 'Browse...'
TabOrder = 2
OnClick = Button1Click
end
object Button2: TButton
Left = 112
Height = 25
Top = 56
Width = 96
Caption = 'Make doc'
TabOrder = 3
OnClick = Button2Click
end
object SelectDirectoryDialog1: TSelectDirectoryDialog
Left = 300
Top = 9
end
end

View File

@@ -0,0 +1,408 @@
// SPDX-License-Identifier: LGPL-3.0-linking-exception
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
fgl;
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
EPath: TEdit;
Label1: TLabel;
Memo1: TMemo;
SelectDirectoryDialog1: TSelectDirectoryDialog;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ private declarations }
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
uses LazUTF8, FileUtil, LazFileUtils, RegExpr, StrUtils;
const
BoldKeywords : array[1..58] of string = ('var','procedure','function','and',
'or','xor','not','if','then','case','begin','end','of',
'exit','new','class','is','const','div','do','downto','to','else','for',
'in','mod','nil','object','record','repeat','self','shl','shr','string',
'unit','until','uses','while','array','interface', 'out', 'constructor',
'property','read','write','default', 'packed', 'operator', 'inline',
'overload', 'virtual', 'abstract', 'helper', 'ifdef', 'endif', 'set',
'specialize', 'generic');
type
TDocumentationPages = specialize TFPGMap<string, string>;
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
begin
if SelectDirectoryDialog1.Execute then
EPath.Text := SelectDirectoryDialog1.FileName;
end;
procedure HighlightKeywords(var s: string);
const keywordChars = ['a'..'z','A'..'Z'];
moreKeywordChars = ['a'..'z','A'..'Z','0'..'9','_'];
var
i,start: Integer;
w,wlower: string;
j: Integer;
found, first: boolean;
begin
i := 1;
s := StringReplace(s, '''', '&apos;', [rfReplaceAll]);
s := StringReplace(s, '{%H-}', '', [rfReplaceAll]);
first := true;
while i <= length(s) do
begin
if s[i] in keywordChars then
begin
start := i;
inc(i);
while i <= length(s) do
begin
if not (s[i] in moreKeywordChars) then break;
inc(i);
end;
w := copy(s,start,i-start);
wlower := lowercase(w);
found := false;
for j := low(BoldKeywords) to high(BoldKeywords) do
if BoldKeywords[j] = wlower then
begin
delete(s, start, length(w));
dec(i, length(w));
w := ''''''''+wlower+'''''''';
insert(w, s, start);
inc(i, length(w));
found := true;
break;
end;
if not found and first then
begin
delete(s, start, length(w));
dec(i, length(w));
first := copy(s, start, 1) = ',';
w := ''''''+w+'''''';
insert(w, s, start);
inc(i, length(w));
continue;
end;
first := false;
end else
inc(i);
end;
end;
procedure AdaptMarkdown(var s: string);
var r: TRegExpr;
begin
r := TRegExpr.Create('([^A-Z0-9]|^)_([A-Z0-9]+([_.][A-Z0-9]+)*)_([^A-Z0-9]|$)'); r.ModifierI:= true;
s := r.Replace(s, '$1''''$2''''$4', true);
r.Free;
r := TRegExpr.Create('\*\*([A-Z0-9]+([_.][A-Z0-9]+)*)\*\*'); r.ModifierI:= true;
s := r.Replace(s, '''''''$1''''''', true);
r.Free;
r := TRegExpr.Create('([^\\]|^)\[([^\]]+)\]\(https://wiki.freepascal.org/(\w+)\)'); r.ModifierI:= true;
s := r.Replace(s, '$1[[$3|$2]]', true);
r.Free;
r := TRegExpr.Create('([^\\]|^)\[([^\]]+)\]\(([-\w:/.]+)\)'); r.ModifierI:= true;
s := r.Replace(s, '$1[$3 $2]', true);
r.Free;
r := TRegExpr.Create('```pascal([^`]+)```');
s := r.Replace(s, '<syntaxhighlight>$1</syntaxhighlight>', true);
r.Free;
r := TRegExpr.Create('\^([0-9]+)');
s := r.Replace(s, '<sup>$1</sup>', true);
r.Free;
s := StringReplace(s, '\[', '[', [rfReplaceAll]);
s := StringReplace(s, ' --> ', ' &rarr; ', [rfReplaceAll]);
s := StringReplace(s, ' <-- ', ' &larr; ', [rfReplaceAll]);
end;
procedure MakeDocFor(AFilename: string; APages: TDocumentationPages);
var
t: textfile;
fileoutput,s,bgcolor: String;
description, element: String;
comStart,comEnd, idxColor: integer;
oddRow,indented : boolean;
docName, colorStr: string;
tableOpened, inCode, bulletPoint, prevBulletPoint: boolean;
procedure openTable;
begin
if not tableOpened then
begin
fileoutput += '<table style="border-collapse: collapse;">'+lineending;
oddRow := true;
tableOpened:= true;
end;
end;
procedure closeTable;
begin
if tableOpened then
begin
fileoutput += '</table>'+LineEnding;
tableOpened:= false;
end;
end;
procedure flushOutput;
var
docIndex: Integer;
begin
if fileoutput <> '' then
begin
closeTable;
if not APages.Find(docName, docIndex) then
begin
docIndex := APages.Add(docName, '=== ' + docName + ' ===' + LineEnding);
end;
APages.Data[docIndex] := APages.Data[docIndex] + fileoutput;
fileoutput:= '';
end;
end;
begin
docName := ExtractFileName(AFilename);
fileoutput := '';
tableOpened:= false;
assignfile(t, UTF8ToSys(AFilename));
reset(t);
while not eof(t) do
begin
readln(t,s);
comStart:= pos('{====',s);
if comStart <> 0 then
begin
comEnd:= pos('====}',s);
if comEnd <> 0 then
begin
closeTable;
fileOutput += trim(copy(s,comStart+1,comEnd+3 -(comStart+1)+1)) + LineEnding;
continue;
end;
end;
comStart:= pos('{===',s);
if comStart <> 0 then
begin
comEnd:= pos('===}',s);
if comEnd <> 0 then
begin
flushOutput;
docName:= trim(copy(s,comStart+4,comEnd-1 -(comStart+4)+1));
continue;
end;
end;
comStart:= pos('{* ',s+' ');
indented:= false;
inCode := false;
if comStart <> 0 then
comStart += 2
else
begin
comStart := pos('{** ',s+' ');
if comStart <> 0 then
comStart += 3;
indented := true;
end;
if comStart<>0 then
begin
delete(s,1,comStart-1);
comStart := 1;
description := '';
comEnd := pos('}',s);
if comEnd = 0 then
begin
prevBulletPoint := false;
description += trim(copy(s,comStart,length(s)-comStart+1));
while not eof(t) do
begin
readln(t,s);
bulletPoint := false;
s := trim(s);
if s.StartsWith('```') then inCode := not inCode;
if not inCode then
begin
s := StringReplace(s, '<=', '&le;', [rfReplaceAll]);
s := StringReplace(s, '>=', '&ge;', [rfReplaceAll]);
end;
if s = '' then description += '<p>'
else
begin
comEnd := pos('}',s);
if comEnd > 0 then s := trim(copy(s,1,comEnd-1));
if not inCode then
begin
if s.StartsWith('- ') then
begin
description += IfThen(prevBulletPoint,'',LineEnding)+'* '+s.Substring(2)+LineEnding;
bulletPoint := true;
end
else
description += ' '+s;
end;
if comEnd > 0 then break
else if inCode then description += LineEnding;
end;
prevBulletPoint:= bulletPoint;
end;
end
else
description += trim(copy(s,comStart,comEnd-comStart));
AdaptMarkdown(description);
while pos('[#',description) <> 0 do
begin
idxColor := pos('[#',description);
colorStr := copy(description, idxColor, 9);
if (length(colorStr) = 9) and colorStr.EndsWith(']') then
begin
delete(description, idxColor, length(colorStr));
insert('<span style="width:8px; height: 8px; display: inline-block; border: 1px solid black; background: '+copy(colorStr,2,length(colorStr)-2)+';"></span>', description, idxColor);
end;
end;
if not eof(t) then
readln(t,element) else element := '?';
HighlightKeywords(element);
element := trim(element);
openTable;
if oddRow then bgcolor := 'white' else bgcolor := '#f0f0ff';
if indented then
begin
fileoutput += '<tr><td width="10%"></td><td colspan="2" style="background: '+bgcolor+';">'+element+'</td></tr>'+LineEnding;
fileoutput += '<tr><td width="10%"></td><td width="10%" style="background: '+bgcolor+';"></td>'+
'<td style="border: 1px solid #e0e0a0; background: #ffffe4;">'+description+'</td></tr>'+LineEnding;
end else
begin
fileoutput += '<tr style="background: '+bgcolor+';"><td colspan="3">'+element+'</td></tr>'+LineEnding;
fileoutput += '<tr style="background: '+bgcolor+';"><td width="10%"></td>'+
'<td style="border: 1px solid #e0e0a0; background: #ffffe4;" colspan="2">'+description+'</td></tr>'+LineEnding;
end;
fileoutput += '<tr style="height: 8px;"><td colspan="3"></td></tr>'+LineEnding;
oddRow := not oddRow;
end;
end;
closefile(t);
flushOutput;
end;
function ExportPages(APages: TDocumentationPages; APath: string): string;
var
i: Integer;
u: textfile;
outname, fullname, currentContent, fileoutput: String;
begin
result := '';
if APages.Count = 0 then exit;
CreateDirUTF8(APath+DirectorySeparator+'doc');
for i := 0 to APages.Count-1 do
begin
outname := 'doc'+DirectorySeparator+APages.Keys[i]+'.txt';
fullname := APath+outname;
fileoutput := APages.Data[i];
if FileExistsUTF8(fullname) then
begin
currentContent := ReadFileToString(fullname);
if currentContent <> fileoutput then
begin
assignfile(u, UTF8ToSys(fullname));
rewrite(u);
write(u, fileoutput);
closefile(u);
result += outname + ' (updated)' + LineEnding;
end else
begin
result += outname + ' (unchanged)' + LineEnding;
end;
end else
begin
result += outname + ' (created)' + LineEnding;
assignfile(u, UTF8ToSys(fullname));
rewrite(u);
write(u, fileoutput);
closefile(u);
end;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
var sr: TSearchRec;
output,ext: string;
basePath,path: string;
pages: TDocumentationPages;
begin
memo1.Text := 'Analyzing...';
memo1.Update;
basePath := ExtractFilePath(ParamStr(0));
{$IFDEF DARWIN}
if basePath.EndsWith('/MacOS/') then
basePath := ExpandFileNameUTF8(basePath+'../../../');
{$ENDIF}
path := ExpandFileNameUTF8(AppendPathDelim(EPath.Text), basePath);
if FindFirstUTF8(path+'*.*', faAnyFile, sr) = 0 then
begin
pages := TDocumentationPages.Create;
pages.Sorted:= true;
repeat
if sr.Attr and (faDirectory or faVolumeId or faSymLink) <> 0 then continue;
ext := AnsiLowerCase(ExtractFileExt(sr.Name));
if (ext = '.pas') or (ext = '.inc') then
MakeDocFor(path+sr.Name, pages);
until FindNextUTF8(sr) <> 0;
FindCloseUTF8(sr);
output := ExportPages(pages, path);
if output = '' then
Memo1.Text := 'No output'
else
Memo1.text := output;
pages.Free;
end
else
Memo1.Text := 'Nothing to do';
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
EPath.Text := StringReplace(EPath.Text, '/', PathDelim, [rfReplaceAll]);
end;
end.

View File

@@ -0,0 +1,933 @@
# ArabicShaping-13.0.0.txt
# Date: 2020-01-31, 23:55:00 GMT [KW, RP]
# © 2020 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
# This file is a normative contributory data file in the
# Unicode Character Database.
#
# This file defines the Joining_Type and Joining_Group property
# values for Arabic, Syriac, N'Ko, Mandaic, and Manichaean positional
# shaping, repeating in machine readable form the information
# exemplified in Tables 9-3, 9-8, 9-9, 9-10, 9-14, 9-15, 9-16, 9-19,
# 9-20, 10-4, 10-5, 10-6, 10-7, and 19-5 of The Unicode Standard core
# specification. This file also defines Joining_Type values for
# Mongolian, Phags-pa, Psalter Pahlavi, Sogdian, Chorasmian, and Adlam positional shaping,
# and Joining_Type and Joining_Group values for Hanifi Rohingya positional shaping,
# which are not listed in tables in the standard.
#
# See Sections 9.2, 9.3, 9.5, 10.5, 10.6, 13.4, 14.3, 14.10, 16.14, 19.4, and 19.9
# of The Unicode Standard core specification for more information.
#
# Each line contains four fields, separated by a semicolon.
#
# Field 0: the code point, in 4-digit hexadecimal
# form, of a character.
#
# Field 1: gives a short schematic name for that character.
# The schematic name is descriptive of the shape, based as
# consistently as possible on a name for the skeleton and
# then the diacritic marks applied to the skeleton, if any.
# Note that this schematic name is considered a comment,
# and does not constitute a formal property value.
#
# Field 2: defines the joining type (property name: Joining_Type)
# R Right_Joining
# L Left_Joining
# D Dual_Joining
# C Join_Causing
# U Non_Joining
# T Transparent
#
# See Section 9.2, Arabic for more information on these joining types.
# Note that for cursive joining scripts which are typically rendered
# top-to-bottom, rather than right-to-left, Joining_Type=L conventionally
# refers to bottom joining, and Joining_Type=R conventionally refers
# to top joining. See Section 14.3, Phags-pa for more information on the
# interpretation of joining types in vertical layout.
#
# Field 3: defines the joining group (property name: Joining_Group)
#
# The values of the joining group are based schematically on character
# names. Where a schematic character name consists of two or more parts
# separated by spaces, the formal Joining_Group property value, as specified in
# PropertyValueAliases.txt, consists of the same name parts joined by
# underscores. Hence, the entry:
#
# 0629; TEH MARBUTA; R; TEH MARBUTA
#
# corresponds to [Joining_Group = Teh_Marbuta].
#
# Note: The property value now designated [Joining_Group = Teh_Marbuta_Goal]
# used to apply to both of the following characters
# in earlier versions of the standard:
#
# U+06C2 ARABIC LETTER HEH GOAL WITH HAMZA ABOVE
# U+06C3 ARABIC LETTER TEH MARBUTA GOAL
#
# However, it currently applies only to U+06C3, and *not* to U+06C2.
# To avoid destabilizing existing Joining_Group property aliases, the
# prior Joining_Group value for U+06C3 (Hamza_On_Heh_Goal) has been
# retained as a property value alias, despite the fact that it
# no longer applies to its namesake character, U+06C2.
# See PropertyValueAliases.txt.
#
# When other cursive scripts are added to the Unicode Standard in the
# future, the joining group value of all its letters will default to
# jg=No_Joining_Group in this data file. Other, more specific
# joining group values will be defined only if an explicit proposal
# to define those values exactly has been approved by the UTC. This
# is the convention exemplified by the N'Ko, Mandaic, Mongolian,
# Phags-pa, Psalter Pahlavi, Sogdian, Chorasmian, and Adlam scripts.
# Only the Arabic, Manichaean, and Syriac scripts currently have
# explicit joining group values defined for all characters, including
# those which have only a single character in a particular Joining_Group
# class. Hanifi Rohingya has explicit Joining_Group values assigned only for
# the few characters which share a particular Joining_Group class, but
# assigns jg=No_Joining_Group to all the singletons.
#
# Note: Code points that are not explicitly listed in this file are
# either of joining type T or U:
#
# - Those that are not explicitly listed and that are of General Category Mn, Me, or Cf
# have joining type T.
# - All others not explicitly listed have joining type U.
#
# For an explicit listing of all characters of joining type T, see
# the derived property file DerivedJoiningType.txt.
#
# #############################################################
# Unicode; Schematic Name; Joining Type; Joining Group
# Arabic Characters
0600; ARABIC NUMBER SIGN; U; No_Joining_Group
0601; ARABIC SIGN SANAH; U; No_Joining_Group
0602; ARABIC FOOTNOTE MARKER; U; No_Joining_Group
0603; ARABIC SIGN SAFHA; U; No_Joining_Group
0604; ARABIC SIGN SAMVAT; U; No_Joining_Group
0605; ARABIC NUMBER MARK ABOVE; U; No_Joining_Group
0608; ARABIC RAY; U; No_Joining_Group
060B; AFGHANI SIGN; U; No_Joining_Group
0620; DOTLESS YEH WITH SEPARATE RING BELOW; D; YEH
0621; HAMZA; U; No_Joining_Group
0622; ALEF WITH MADDA ABOVE; R; ALEF
0623; ALEF WITH HAMZA ABOVE; R; ALEF
0624; WAW WITH HAMZA ABOVE; R; WAW
0625; ALEF WITH HAMZA BELOW; R; ALEF
0626; DOTLESS YEH WITH HAMZA ABOVE; D; YEH
0627; ALEF; R; ALEF
0628; BEH; D; BEH
0629; TEH MARBUTA; R; TEH MARBUTA
062A; DOTLESS BEH WITH 2 DOTS ABOVE; D; BEH
062B; DOTLESS BEH WITH 3 DOTS ABOVE; D; BEH
062C; HAH WITH DOT BELOW; D; HAH
062D; HAH; D; HAH
062E; HAH WITH DOT ABOVE; D; HAH
062F; DAL; R; DAL
0630; DAL WITH DOT ABOVE; R; DAL
0631; REH; R; REH
0632; REH WITH DOT ABOVE; R; REH
0633; SEEN; D; SEEN
0634; SEEN WITH 3 DOTS ABOVE; D; SEEN
0635; SAD; D; SAD
0636; SAD WITH DOT ABOVE; D; SAD
0637; TAH; D; TAH
0638; TAH WITH DOT ABOVE; D; TAH
0639; AIN; D; AIN
063A; AIN WITH DOT ABOVE; D; AIN
063B; KEHEH WITH 2 DOTS ABOVE; D; GAF
063C; KEHEH WITH 3 DOTS BELOW; D; GAF
063D; FARSI YEH WITH INVERTED V ABOVE; D; FARSI YEH
063E; FARSI YEH WITH 2 DOTS ABOVE; D; FARSI YEH
063F; FARSI YEH WITH 3 DOTS ABOVE; D; FARSI YEH
0640; TATWEEL; C; No_Joining_Group
0641; FEH; D; FEH
0642; QAF; D; QAF
0643; KAF; D; KAF
0644; LAM; D; LAM
0645; MEEM; D; MEEM
0646; NOON; D; NOON
0647; HEH; D; HEH
0648; WAW; R; WAW
0649; DOTLESS YEH; D; YEH
064A; YEH; D; YEH
066E; DOTLESS BEH; D; BEH
066F; DOTLESS QAF; D; QAF
0671; ALEF WITH WASLA ABOVE; R; ALEF
0672; ALEF WITH WAVY HAMZA ABOVE; R; ALEF
0673; ALEF WITH WAVY HAMZA BELOW; R; ALEF
0674; HIGH HAMZA; U; No_Joining_Group
0675; HIGH HAMZA ALEF; R; ALEF
0676; HIGH HAMZA WAW; R; WAW
0677; HIGH HAMZA WAW WITH DAMMA ABOVE; R; WAW
0678; HIGH HAMZA DOTLESS YEH; D; YEH
0679; DOTLESS BEH WITH TAH ABOVE; D; BEH
067A; DOTLESS BEH WITH VERTICAL 2 DOTS ABOVE; D; BEH
067B; DOTLESS BEH WITH VERTICAL 2 DOTS BELOW; D; BEH
067C; DOTLESS BEH WITH ATTACHED RING BELOW AND 2 DOTS ABOVE; D; BEH
067D; DOTLESS BEH WITH INVERTED 3 DOTS ABOVE; D; BEH
067E; DOTLESS BEH WITH 3 DOTS BELOW; D; BEH
067F; DOTLESS BEH WITH 4 DOTS ABOVE; D; BEH
0680; DOTLESS BEH WITH 4 DOTS BELOW; D; BEH
0681; HAH WITH HAMZA ABOVE; D; HAH
0682; HAH WITH VERTICAL 2 DOTS ABOVE; D; HAH
0683; HAH WITH 2 DOTS BELOW; D; HAH
0684; HAH WITH VERTICAL 2 DOTS BELOW; D; HAH
0685; HAH WITH 3 DOTS ABOVE; D; HAH
0686; HAH WITH 3 DOTS BELOW; D; HAH
0687; HAH WITH 4 DOTS BELOW; D; HAH
0688; DAL WITH TAH ABOVE; R; DAL
0689; DAL WITH ATTACHED RING BELOW; R; DAL
068A; DAL WITH DOT BELOW; R; DAL
068B; DAL WITH DOT BELOW AND TAH ABOVE; R; DAL
068C; DAL WITH 2 DOTS ABOVE; R; DAL
068D; DAL WITH 2 DOTS BELOW; R; DAL
068E; DAL WITH 3 DOTS ABOVE; R; DAL
068F; DAL WITH INVERTED 3 DOTS ABOVE; R; DAL
0690; DAL WITH 4 DOTS ABOVE; R; DAL
0691; REH WITH TAH ABOVE; R; REH
0692; REH WITH V ABOVE; R; REH
0693; REH WITH ATTACHED RING BELOW; R; REH
0694; REH WITH DOT BELOW; R; REH
0695; REH WITH V BELOW; R; REH
0696; REH WITH DOT BELOW AND DOT WITHIN; R; REH
0697; REH WITH 2 DOTS ABOVE; R; REH
0698; REH WITH 3 DOTS ABOVE; R; REH
0699; REH WITH 4 DOTS ABOVE; R; REH
069A; SEEN WITH DOT BELOW AND DOT ABOVE; D; SEEN
069B; SEEN WITH 3 DOTS BELOW; D; SEEN
069C; SEEN WITH 3 DOTS BELOW AND 3 DOTS ABOVE; D; SEEN
069D; SAD WITH 2 DOTS BELOW; D; SAD
069E; SAD WITH 3 DOTS ABOVE; D; SAD
069F; TAH WITH 3 DOTS ABOVE; D; TAH
06A0; AIN WITH 3 DOTS ABOVE; D; AIN
06A1; DOTLESS FEH; D; FEH
06A2; DOTLESS FEH WITH DOT BELOW; D; FEH
06A3; FEH WITH DOT BELOW; D; FEH
06A4; DOTLESS FEH WITH 3 DOTS ABOVE; D; FEH
06A5; DOTLESS FEH WITH 3 DOTS BELOW; D; FEH
06A6; DOTLESS FEH WITH 4 DOTS ABOVE; D; FEH
06A7; DOTLESS QAF WITH DOT ABOVE; D; QAF
06A8; DOTLESS QAF WITH 3 DOTS ABOVE; D; QAF
06A9; KEHEH; D; GAF
06AA; SWASH KAF; D; SWASH KAF
06AB; KEHEH WITH ATTACHED RING BELOW; D; GAF
06AC; KAF WITH DOT ABOVE; D; KAF
06AD; KAF WITH 3 DOTS ABOVE; D; KAF
06AE; KAF WITH 3 DOTS BELOW; D; KAF
06AF; GAF; D; GAF
06B0; GAF WITH ATTACHED RING BELOW; D; GAF
06B1; GAF WITH 2 DOTS ABOVE; D; GAF
06B2; GAF WITH 2 DOTS BELOW; D; GAF
06B3; GAF WITH VERTICAL 2 DOTS BELOW; D; GAF
06B4; GAF WITH 3 DOTS ABOVE; D; GAF
06B5; LAM WITH V ABOVE; D; LAM
06B6; LAM WITH DOT ABOVE; D; LAM
06B7; LAM WITH 3 DOTS ABOVE; D; LAM
06B8; LAM WITH 3 DOTS BELOW; D; LAM
06B9; NOON WITH DOT BELOW; D; NOON
06BA; DOTLESS NOON; D; NOON
06BB; DOTLESS NOON WITH TAH ABOVE; D; NOON
06BC; NOON WITH ATTACHED RING BELOW; D; NOON
06BD; NYA; D; NYA
06BE; KNOTTED HEH; D; KNOTTED HEH
06BF; HAH WITH 3 DOTS BELOW AND DOT ABOVE; D; HAH
06C0; DOTLESS TEH MARBUTA WITH HAMZA ABOVE; R; TEH MARBUTA
06C1; HEH GOAL; D; HEH GOAL
06C2; HEH GOAL WITH HAMZA ABOVE; D; HEH GOAL
06C3; TEH MARBUTA GOAL; R; TEH MARBUTA GOAL
06C4; WAW WITH ATTACHED RING WITHIN; R; WAW
06C5; WAW WITH BAR; R; WAW
06C6; WAW WITH V ABOVE; R; WAW
06C7; WAW WITH DAMMA ABOVE; R; WAW
06C8; WAW WITH ALEF ABOVE; R; WAW
06C9; WAW WITH INVERTED V ABOVE; R; WAW
06CA; WAW WITH 2 DOTS ABOVE; R; WAW
06CB; WAW WITH 3 DOTS ABOVE; R; WAW
06CC; FARSI YEH; D; FARSI YEH
06CD; YEH WITH TAIL; R; YEH WITH TAIL
06CE; FARSI YEH WITH V ABOVE; D; FARSI YEH
06CF; WAW WITH DOT ABOVE; R; WAW
06D0; DOTLESS YEH WITH VERTICAL 2 DOTS BELOW; D; YEH
06D1; DOTLESS YEH WITH 3 DOTS BELOW; D; YEH
06D2; YEH BARREE; R; YEH BARREE
06D3; YEH BARREE WITH HAMZA ABOVE; R; YEH BARREE
06D5; DOTLESS TEH MARBUTA; R; TEH MARBUTA
06DD; ARABIC END OF AYAH; U; No_Joining_Group
06EE; DAL WITH INVERTED V ABOVE; R; DAL
06EF; REH WITH INVERTED V ABOVE; R; REH
06FA; SEEN WITH DOT BELOW AND 3 DOTS ABOVE; D; SEEN
06FB; SAD WITH DOT BELOW AND DOT ABOVE; D; SAD
06FC; AIN WITH DOT BELOW AND DOT ABOVE; D; AIN
06FF; KNOTTED HEH WITH INVERTED V ABOVE; D; KNOTTED HEH
# Syriac Characters
070F; SYRIAC ABBREVIATION MARK; T; No_Joining_Group
0710; ALAPH; R; ALAPH
0712; BETH; D; BETH
0713; GAMAL; D; GAMAL
0714; GAMAL GARSHUNI; D; GAMAL
0715; DALATH; R; DALATH RISH
0716; DOTLESS DALATH RISH; R; DALATH RISH
0717; HE; R; HE
0718; WAW; R; SYRIAC WAW
0719; ZAIN; R; ZAIN
071A; HETH; D; HETH
071B; TETH; D; TETH
071C; TETH GARSHUNI; D; TETH
071D; YUDH; D; YUDH
071E; YUDH HE; R; YUDH HE
071F; KAPH; D; KAPH
0720; LAMADH; D; LAMADH
0721; MIM; D; MIM
0722; NUN; D; NUN
0723; SEMKATH; D; SEMKATH
0724; FINAL SEMKATH; D; FINAL SEMKATH
0725; E; D; E
0726; PE; D; PE
0727; REVERSED PE; D; REVERSED PE
0728; SADHE; R; SADHE
0729; QAPH; D; QAPH
072A; RISH; R; DALATH RISH
072B; SHIN; D; SHIN
072C; TAW; R; TAW
072D; PERSIAN BHETH; D; BETH
072E; PERSIAN GHAMAL; D; GAMAL
072F; PERSIAN DHALATH; R; DALATH RISH
074D; SOGDIAN ZHAIN; R; ZHAIN
074E; SOGDIAN KHAPH; D; KHAPH
074F; SOGDIAN FE; D; FE
# Arabic Supplement Characters
0750; DOTLESS BEH WITH HORIZONTAL 3 DOTS BELOW; D; BEH
0751; BEH WITH 3 DOTS ABOVE; D; BEH
0752; DOTLESS BEH WITH INVERTED 3 DOTS BELOW; D; BEH
0753; DOTLESS BEH WITH INVERTED 3 DOTS BELOW AND 2 DOTS ABOVE; D; BEH
0754; DOTLESS BEH WITH 2 DOTS BELOW AND DOT ABOVE; D; BEH
0755; DOTLESS BEH WITH INVERTED V BELOW; D; BEH
0756; DOTLESS BEH WITH V ABOVE; D; BEH
0757; HAH WITH 2 DOTS ABOVE; D; HAH
0758; HAH WITH INVERTED 3 DOTS BELOW; D; HAH
0759; DAL WITH VERTICAL 2 DOTS BELOW AND TAH ABOVE; R; DAL
075A; DAL WITH INVERTED V BELOW; R; DAL
075B; REH WITH BAR; R; REH
075C; SEEN WITH 4 DOTS ABOVE; D; SEEN
075D; AIN WITH 2 DOTS ABOVE; D; AIN
075E; AIN WITH INVERTED 3 DOTS ABOVE; D; AIN
075F; AIN WITH VERTICAL 2 DOTS ABOVE; D; AIN
0760; DOTLESS FEH WITH 2 DOTS BELOW; D; FEH
0761; DOTLESS FEH WITH INVERTED 3 DOTS BELOW; D; FEH
0762; KEHEH WITH DOT ABOVE; D; GAF
0763; KEHEH WITH 3 DOTS ABOVE; D; GAF
0764; KEHEH WITH INVERTED 3 DOTS BELOW; D; GAF
0765; MEEM WITH DOT ABOVE; D; MEEM
0766; MEEM WITH DOT BELOW; D; MEEM
0767; NOON WITH 2 DOTS BELOW; D; NOON
0768; NOON WITH TAH ABOVE; D; NOON
0769; NOON WITH V ABOVE; D; NOON
076A; LAM WITH BAR; D; LAM
076B; REH WITH VERTICAL 2 DOTS ABOVE; R; REH
076C; REH WITH HAMZA ABOVE; R; REH
076D; SEEN WITH VERTICAL 2 DOTS ABOVE; D; SEEN
076E; HAH WITH TAH BELOW; D; HAH
076F; HAH WITH TAH AND 2 DOTS BELOW; D; HAH
0770; SEEN WITH 2 DOTS AND TAH ABOVE; D; SEEN
0771; REH WITH 2 DOTS AND TAH ABOVE; R; REH
0772; HAH WITH TAH ABOVE; D; HAH
0773; ALEF WITH DIGIT TWO ABOVE; R; ALEF
0774; ALEF WITH DIGIT THREE ABOVE; R; ALEF
0775; FARSI YEH WITH DIGIT TWO ABOVE; D; FARSI YEH
0776; FARSI YEH WITH DIGIT THREE ABOVE; D; FARSI YEH
0777; DOTLESS YEH WITH DIGIT FOUR BELOW; D; YEH
0778; WAW WITH DIGIT TWO ABOVE; R; WAW
0779; WAW WITH DIGIT THREE ABOVE; R; WAW
077A; BURUSHASKI YEH BARREE WITH DIGIT TWO ABOVE; D; BURUSHASKI YEH BARREE
077B; BURUSHASKI YEH BARREE WITH DIGIT THREE ABOVE; D; BURUSHASKI YEH BARREE
077C; HAH WITH DIGIT FOUR BELOW; D; HAH
077D; SEEN WITH DIGIT FOUR ABOVE; D; SEEN
077E; SEEN WITH INVERTED V ABOVE; D; SEEN
077F; KAF WITH 2 DOTS ABOVE; D; KAF
# N'Ko Characters
07CA; NKO A; D; No_Joining_Group
07CB; NKO EE; D; No_Joining_Group
07CC; NKO I; D; No_Joining_Group
07CD; NKO E; D; No_Joining_Group
07CE; NKO U; D; No_Joining_Group
07CF; NKO OO; D; No_Joining_Group
07D0; NKO O; D; No_Joining_Group
07D1; NKO DAGBASINNA; D; No_Joining_Group
07D2; NKO N; D; No_Joining_Group
07D3; NKO BA; D; No_Joining_Group
07D4; NKO PA; D; No_Joining_Group
07D5; NKO TA; D; No_Joining_Group
07D6; NKO JA; D; No_Joining_Group
07D7; NKO CHA; D; No_Joining_Group
07D8; NKO DA; D; No_Joining_Group
07D9; NKO RA; D; No_Joining_Group
07DA; NKO RRA; D; No_Joining_Group
07DB; NKO SA; D; No_Joining_Group
07DC; NKO GBA; D; No_Joining_Group
07DD; NKO FA; D; No_Joining_Group
07DE; NKO KA; D; No_Joining_Group
07DF; NKO LA; D; No_Joining_Group
07E0; NKO NA WOLOSO; D; No_Joining_Group
07E1; NKO MA; D; No_Joining_Group
07E2; NKO NYA; D; No_Joining_Group
07E3; NKO NA; D; No_Joining_Group
07E4; NKO HA; D; No_Joining_Group
07E5; NKO WA; D; No_Joining_Group
07E6; NKO YA; D; No_Joining_Group
07E7; NKO NYA WOLOSO; D; No_Joining_Group
07E8; NKO JONA JA; D; No_Joining_Group
07E9; NKO JONA CHA; D; No_Joining_Group
07EA; NKO JONA RA; D; No_Joining_Group
07FA; NKO LAJANYALAN; C; No_Joining_Group
# Mandaic Characters
0840; MANDAIC HALQA; R; No_Joining_Group
0841; MANDAIC AB; D; No_Joining_Group
0842; MANDAIC AG; D; No_Joining_Group
0843; MANDAIC AD; D; No_Joining_Group
0844; MANDAIC AH; D; No_Joining_Group
0845; MANDAIC USHENNA; D; No_Joining_Group
0846; MANDAIC AZ; R; No_Joining_Group
0847; MANDAIC IT; R; No_Joining_Group
0848; MANDAIC ATT; D; No_Joining_Group
0849; MANDAIC AKSA; R; No_Joining_Group
084A; MANDAIC AK; D; No_Joining_Group
084B; MANDAIC AL; D; No_Joining_Group
084C; MANDAIC AM; D; No_Joining_Group
084D; MANDAIC AN; D; No_Joining_Group
084E; MANDAIC AS; D; No_Joining_Group
084F; MANDAIC IN; D; No_Joining_Group
0850; MANDAIC AP; D; No_Joining_Group
0851; MANDAIC ASZ; D; No_Joining_Group
0852; MANDAIC AQ; D; No_Joining_Group
0853; MANDAIC AR; D; No_Joining_Group
0854; MANDAIC ASH; R; No_Joining_Group
0855; MANDAIC AT; D; No_Joining_Group
0856; MANDAIC DUSHENNA; R; No_Joining_Group
0857; MANDAIC KAD; R; No_Joining_Group
0858; MANDAIC AIN; R; No_Joining_Group
# Syriac Supplement Characters
0860; MALAYALAM NGA; D; MALAYALAM NGA
0861; MALAYALAM JA; U; MALAYALAM JA
0862; MALAYALAM NYA; D; MALAYALAM NYA
0863; MALAYALAM TTA; D; MALAYALAM TTA
0864; MALAYALAM NNA; D; MALAYALAM NNA
0865; MALAYALAM NNNA; D; MALAYALAM NNNA
0866; MALAYALAM BHA; U; MALAYALAM BHA
0867; MALAYALAM RA; R; MALAYALAM RA
0868; MALAYALAM LLA; D; MALAYALAM LLA
0869; MALAYALAM LLLA; R; MALAYALAM LLLA
086A; MALAYALAM SSA; R; MALAYALAM SSA
# Arabic Extended-A Characters
08A0; DOTLESS BEH WITH V BELOW; D; BEH
08A1; BEH WITH HAMZA ABOVE; D; BEH
08A2; HAH WITH DOT BELOW AND 2 DOTS ABOVE; D; HAH
08A3; TAH WITH 2 DOTS ABOVE; D; TAH
08A4; DOTLESS FEH WITH DOT BELOW AND 3 DOTS ABOVE; D; FEH
08A5; QAF WITH DOT BELOW; D; QAF
08A6; LAM WITH DOUBLE BAR; D; LAM
08A7; MEEM WITH 3 DOTS ABOVE; D; MEEM
08A8; YEH WITH HAMZA ABOVE; D; YEH
08A9; YEH WITH DOT ABOVE; D; YEH
08AA; REH WITH LOOP; R; REH
08AB; WAW WITH DOT WITHIN; R; WAW
08AC; ROHINGYA YEH; R; ROHINGYA YEH
08AD; LOW ALEF; U; No_Joining_Group
08AE; DAL WITH 3 DOTS BELOW; R; DAL
08AF; SAD WITH 3 DOTS BELOW; D; SAD
08B0; KEHEH WITH STROKE BELOW; D; GAF
08B1; STRAIGHT WAW; R; STRAIGHT WAW
08B2; REH WITH DOT AND INVERTED V ABOVE; R; REH
08B3; AIN WITH 3 DOTS BELOW; D; AIN
08B4; KAF WITH DOT BELOW; D; KAF
08B6; BEH WITH MEEM ABOVE; D; BEH
08B7; DOTLESS BEH WITH 3 DOTS BELOW AND MEEM ABOVE; D; BEH
08B8; DOTLESS BEH WITH TEH ABOVE; D; BEH
08B9; REH WITH NOON ABOVE; R; REH
08BA; YEH WITH NOON ABOVE; D; YEH
08BB; AFRICAN FEH; D; AFRICAN FEH
08BC; AFRICAN QAF; D; AFRICAN QAF
08BD; AFRICAN NOON; D; AFRICAN NOON
08BE; DOTLESS BEH WITH 3 DOTS BELOW AND V ABOVE; D; BEH
08BF; DOTLESS BEH WITH 2 DOTS AND V ABOVE; D; BEH
08C0; DOTLESS BEH WITH TAH AND V ABOVE; D; BEH
08C1; HAH WITH 3 DOTS BELOW AND V ABOVE; D; HAH
08C2; KEHEH WITH V ABOVE; D; GAF
08C3; AIN WITH DIAMOND 4 DOTS ABOVE; D; AIN
08C4; AFRICAN QAF WITH 3 DOTS ABOVE; D; AFRICAN QAF
08C5; HAH WITH DOT BELOW AND 3 DOTS ABOVE; D; HAH
08C6; HAH WITH DIAMOND 4 DOTS BELOW; D; HAH
08C7; LAM WITH TAH ABOVE; D; LAM
08E2; ARABIC DISPUTED END OF AYAH; U; No_Joining_Group
# Mongolian Characters
1806; MONGOLIAN TODO SOFT HYPHEN; U; No_Joining_Group
1807; MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER; D; No_Joining_Group
180A; MONGOLIAN NIRUGU; C; No_Joining_Group
180E; MONGOLIAN VOWEL SEPARATOR; U; No_Joining_Group
1820; MONGOLIAN A; D; No_Joining_Group
1821; MONGOLIAN E; D; No_Joining_Group
1822; MONGOLIAN I; D; No_Joining_Group
1823; MONGOLIAN O; D; No_Joining_Group
1824; MONGOLIAN U; D; No_Joining_Group
1825; MONGOLIAN OE; D; No_Joining_Group
1826; MONGOLIAN UE; D; No_Joining_Group
1827; MONGOLIAN EE; D; No_Joining_Group
1828; MONGOLIAN NA; D; No_Joining_Group
1829; MONGOLIAN ANG; D; No_Joining_Group
182A; MONGOLIAN BA; D; No_Joining_Group
182B; MONGOLIAN PA; D; No_Joining_Group
182C; MONGOLIAN QA; D; No_Joining_Group
182D; MONGOLIAN GA; D; No_Joining_Group
182E; MONGOLIAN MA; D; No_Joining_Group
182F; MONGOLIAN LA; D; No_Joining_Group
1830; MONGOLIAN SA; D; No_Joining_Group
1831; MONGOLIAN SHA; D; No_Joining_Group
1832; MONGOLIAN TA; D; No_Joining_Group
1833; MONGOLIAN DA; D; No_Joining_Group
1834; MONGOLIAN CHA; D; No_Joining_Group
1835; MONGOLIAN JA; D; No_Joining_Group
1836; MONGOLIAN YA; D; No_Joining_Group
1837; MONGOLIAN RA; D; No_Joining_Group
1838; MONGOLIAN WA; D; No_Joining_Group
1839; MONGOLIAN FA; D; No_Joining_Group
183A; MONGOLIAN KA; D; No_Joining_Group
183B; MONGOLIAN KHA; D; No_Joining_Group
183C; MONGOLIAN TSA; D; No_Joining_Group
183D; MONGOLIAN ZA; D; No_Joining_Group
183E; MONGOLIAN HAA; D; No_Joining_Group
183F; MONGOLIAN ZRA; D; No_Joining_Group
1840; MONGOLIAN LHA; D; No_Joining_Group
1841; MONGOLIAN ZHI; D; No_Joining_Group
1842; MONGOLIAN CHI; D; No_Joining_Group
1843; MONGOLIAN TODO LONG VOWEL SIGN; D; No_Joining_Group
1844; MONGOLIAN TODO E; D; No_Joining_Group
1845; MONGOLIAN TODO I; D; No_Joining_Group
1846; MONGOLIAN TODO O; D; No_Joining_Group
1847; MONGOLIAN TODO U; D; No_Joining_Group
1848; MONGOLIAN TODO OE; D; No_Joining_Group
1849; MONGOLIAN TODO UE; D; No_Joining_Group
184A; MONGOLIAN TODO ANG; D; No_Joining_Group
184B; MONGOLIAN TODO BA; D; No_Joining_Group
184C; MONGOLIAN TODO PA; D; No_Joining_Group
184D; MONGOLIAN TODO QA; D; No_Joining_Group
184E; MONGOLIAN TODO GA; D; No_Joining_Group
184F; MONGOLIAN TODO MA; D; No_Joining_Group
1850; MONGOLIAN TODO TA; D; No_Joining_Group
1851; MONGOLIAN TODO DA; D; No_Joining_Group
1852; MONGOLIAN TODO CHA; D; No_Joining_Group
1853; MONGOLIAN TODO JA; D; No_Joining_Group
1854; MONGOLIAN TODO TSA; D; No_Joining_Group
1855; MONGOLIAN TODO YA; D; No_Joining_Group
1856; MONGOLIAN TODO WA; D; No_Joining_Group
1857; MONGOLIAN TODO KA; D; No_Joining_Group
1858; MONGOLIAN TODO GAA; D; No_Joining_Group
1859; MONGOLIAN TODO HAA; D; No_Joining_Group
185A; MONGOLIAN TODO JIA; D; No_Joining_Group
185B; MONGOLIAN TODO NIA; D; No_Joining_Group
185C; MONGOLIAN TODO DZA; D; No_Joining_Group
185D; MONGOLIAN SIBE E; D; No_Joining_Group
185E; MONGOLIAN SIBE I; D; No_Joining_Group
185F; MONGOLIAN SIBE IY; D; No_Joining_Group
1860; MONGOLIAN SIBE UE; D; No_Joining_Group
1861; MONGOLIAN SIBE U; D; No_Joining_Group
1862; MONGOLIAN SIBE ANG; D; No_Joining_Group
1863; MONGOLIAN SIBE KA; D; No_Joining_Group
1864; MONGOLIAN SIBE GA; D; No_Joining_Group
1865; MONGOLIAN SIBE HA; D; No_Joining_Group
1866; MONGOLIAN SIBE PA; D; No_Joining_Group
1867; MONGOLIAN SIBE SHA; D; No_Joining_Group
1868; MONGOLIAN SIBE TA; D; No_Joining_Group
1869; MONGOLIAN SIBE DA; D; No_Joining_Group
186A; MONGOLIAN SIBE JA; D; No_Joining_Group
186B; MONGOLIAN SIBE FA; D; No_Joining_Group
186C; MONGOLIAN SIBE GAA; D; No_Joining_Group
186D; MONGOLIAN SIBE HAA; D; No_Joining_Group
186E; MONGOLIAN SIBE TSA; D; No_Joining_Group
186F; MONGOLIAN SIBE ZA; D; No_Joining_Group
1870; MONGOLIAN SIBE RAA; D; No_Joining_Group
1871; MONGOLIAN SIBE CHA; D; No_Joining_Group
1872; MONGOLIAN SIBE ZHA; D; No_Joining_Group
1873; MONGOLIAN MANCHU I; D; No_Joining_Group
1874; MONGOLIAN MANCHU KA; D; No_Joining_Group
1875; MONGOLIAN MANCHU RA; D; No_Joining_Group
1876; MONGOLIAN MANCHU FA; D; No_Joining_Group
1877; MONGOLIAN MANCHU ZHA; D; No_Joining_Group
1878; MONGOLIAN MANCHU CHA WITH 2 DOTS; D; No_Joining_Group
1880; MONGOLIAN ALI GALI ANUSVARA ONE; U; No_Joining_Group
1881; MONGOLIAN ALI GALI VISARGA ONE; U; No_Joining_Group
1882; MONGOLIAN ALI GALI DAMARU; U; No_Joining_Group
1883; MONGOLIAN ALI GALI UBADAMA; U; No_Joining_Group
1884; MONGOLIAN ALI GALI INVERTED UBADAMA; U; No_Joining_Group
1885; MONGOLIAN ALI GALI BALUDA; T; No_Joining_Group
1886; MONGOLIAN ALI GALI THREE BALUDA; T; No_Joining_Group
1887; MONGOLIAN ALI GALI A; D; No_Joining_Group
1888; MONGOLIAN ALI GALI I; D; No_Joining_Group
1889; MONGOLIAN ALI GALI KA; D; No_Joining_Group
188A; MONGOLIAN ALI GALI NGA; D; No_Joining_Group
188B; MONGOLIAN ALI GALI CA; D; No_Joining_Group
188C; MONGOLIAN ALI GALI TTA; D; No_Joining_Group
188D; MONGOLIAN ALI GALI TTHA; D; No_Joining_Group
188E; MONGOLIAN ALI GALI DDA; D; No_Joining_Group
188F; MONGOLIAN ALI GALI NNA; D; No_Joining_Group
1890; MONGOLIAN ALI GALI TA; D; No_Joining_Group
1891; MONGOLIAN ALI GALI DA; D; No_Joining_Group
1892; MONGOLIAN ALI GALI PA; D; No_Joining_Group
1893; MONGOLIAN ALI GALI PHA; D; No_Joining_Group
1894; MONGOLIAN ALI GALI SSA; D; No_Joining_Group
1895; MONGOLIAN ALI GALI ZHA; D; No_Joining_Group
1896; MONGOLIAN ALI GALI ZA; D; No_Joining_Group
1897; MONGOLIAN ALI GALI AH; D; No_Joining_Group
1898; MONGOLIAN TODO ALI GALI TA; D; No_Joining_Group
1899; MONGOLIAN TODO ALI GALI ZHA; D; No_Joining_Group
189A; MONGOLIAN MANCHU ALI GALI GHA; D; No_Joining_Group
189B; MONGOLIAN MANCHU ALI GALI NGA; D; No_Joining_Group
189C; MONGOLIAN MANCHU ALI GALI CA; D; No_Joining_Group
189D; MONGOLIAN MANCHU ALI GALI JHA; D; No_Joining_Group
189E; MONGOLIAN MANCHU ALI GALI TTA; D; No_Joining_Group
189F; MONGOLIAN MANCHU ALI GALI DDHA; D; No_Joining_Group
18A0; MONGOLIAN MANCHU ALI GALI TA; D; No_Joining_Group
18A1; MONGOLIAN MANCHU ALI GALI DHA; D; No_Joining_Group
18A2; MONGOLIAN MANCHU ALI GALI SSA; D; No_Joining_Group
18A3; MONGOLIAN MANCHU ALI GALI CYA; D; No_Joining_Group
18A4; MONGOLIAN MANCHU ALI GALI ZHA; D; No_Joining_Group
18A5; MONGOLIAN MANCHU ALI GALI ZA; D; No_Joining_Group
18A6; MONGOLIAN ALI GALI HALF U; D; No_Joining_Group
18A7; MONGOLIAN ALI GALI HALF YA; D; No_Joining_Group
18A8; MONGOLIAN MANCHU ALI GALI BHA; D; No_Joining_Group
18AA; MONGOLIAN MANCHU ALI GALI LHA; D; No_Joining_Group
# Other
200C; ZERO WIDTH NON-JOINER; U; No_Joining_Group
200D; ZERO WIDTH JOINER; C; No_Joining_Group
202F; NARROW NO-BREAK SPACE; U; No_Joining_Group
2066; LEFT-TO-RIGHT ISOLATE; U; No_Joining_Group
2067; RIGHT-TO-LEFT ISOLATE; U; No_Joining_Group
2068; FIRST STRONG ISOLATE; U; No_Joining_Group
2069; POP DIRECTIONAL ISOLATE; U; No_Joining_Group
# Phags-Pa Characters
A840; PHAGS-PA KA; D; No_Joining_Group
A841; PHAGS-PA KHA; D; No_Joining_Group
A842; PHAGS-PA GA; D; No_Joining_Group
A843; PHAGS-PA NGA; D; No_Joining_Group
A844; PHAGS-PA CA; D; No_Joining_Group
A845; PHAGS-PA CHA; D; No_Joining_Group
A846; PHAGS-PA JA; D; No_Joining_Group
A847; PHAGS-PA NYA; D; No_Joining_Group
A848; PHAGS-PA TA; D; No_Joining_Group
A849; PHAGS-PA THA; D; No_Joining_Group
A84A; PHAGS-PA DA; D; No_Joining_Group
A84B; PHAGS-PA NA; D; No_Joining_Group
A84C; PHAGS-PA PA; D; No_Joining_Group
A84D; PHAGS-PA PHA; D; No_Joining_Group
A84E; PHAGS-PA BA; D; No_Joining_Group
A84F; PHAGS-PA MA; D; No_Joining_Group
A850; PHAGS-PA TSA; D; No_Joining_Group
A851; PHAGS-PA TSHA; D; No_Joining_Group
A852; PHAGS-PA DZA; D; No_Joining_Group
A853; PHAGS-PA WA; D; No_Joining_Group
A854; PHAGS-PA ZHA; D; No_Joining_Group
A855; PHAGS-PA ZA; D; No_Joining_Group
A856; PHAGS-PA SMALL A; D; No_Joining_Group
A857; PHAGS-PA YA; D; No_Joining_Group
A858; PHAGS-PA RA; D; No_Joining_Group
A859; PHAGS-PA LA; D; No_Joining_Group
A85A; PHAGS-PA SHA; D; No_Joining_Group
A85B; PHAGS-PA SA; D; No_Joining_Group
A85C; PHAGS-PA HA; D; No_Joining_Group
A85D; PHAGS-PA A; D; No_Joining_Group
A85E; PHAGS-PA I; D; No_Joining_Group
A85F; PHAGS-PA U; D; No_Joining_Group
A860; PHAGS-PA E; D; No_Joining_Group
A861; PHAGS-PA O; D; No_Joining_Group
A862; PHAGS-PA QA; D; No_Joining_Group
A863; PHAGS-PA XA; D; No_Joining_Group
A864; PHAGS-PA FA; D; No_Joining_Group
A865; PHAGS-PA GGA; D; No_Joining_Group
A866; PHAGS-PA EE; D; No_Joining_Group
A867; PHAGS-PA SUBJOINED WA; D; No_Joining_Group
A868; PHAGS-PA SUBJOINED YA; D; No_Joining_Group
A869; PHAGS-PA TTA; D; No_Joining_Group
A86A; PHAGS-PA TTHA; D; No_Joining_Group
A86B; PHAGS-PA DDA; D; No_Joining_Group
A86C; PHAGS-PA NNA; D; No_Joining_Group
A86D; PHAGS-PA ALTERNATE YA; D; No_Joining_Group
A86E; PHAGS-PA VOICELESS SHA; D; No_Joining_Group
A86F; PHAGS-PA VOICED HA; D; No_Joining_Group
A870; PHAGS-PA ASPIRATED FA; D; No_Joining_Group
A871; PHAGS-PA SUBJOINED RA; D; No_Joining_Group
A872; PHAGS-PA SUPERFIXED RA; L; No_Joining_Group
A873; PHAGS-PA CANDRABINDU; U; No_Joining_Group
# Manichaean Characters
10AC0; MANICHAEAN ALEPH; D; MANICHAEAN ALEPH
10AC1; MANICHAEAN BETH; D; MANICHAEAN BETH
10AC2; MANICHAEAN BETH WITH 2 DOTS ABOVE; D; MANICHAEAN BETH
10AC3; MANICHAEAN GIMEL; D; MANICHAEAN GIMEL
10AC4; MANICHAEAN GIMEL WITH ATTACHED RING BELOW; D; MANICHAEAN GIMEL
10AC5; MANICHAEAN DALETH; R; MANICHAEAN DALETH
10AC6; MANICHAEAN HE; U; No_Joining_Group
10AC7; MANICHAEAN WAW; R; MANICHAEAN WAW
10AC8; MANICHAEAN UD; U; No_Joining_Group
10AC9; MANICHAEAN ZAYIN; R; MANICHAEAN ZAYIN
10ACA; MANICHAEAN ZAYIN WITH 2 DOTS ABOVE; R; MANICHAEAN ZAYIN
10ACB; MANICHAEAN JAYIN; U; No_Joining_Group
10ACC; MANICHAEAN JAYIN WITH 2 DOTS ABOVE; U; No_Joining_Group
10ACD; MANICHAEAN HETH; L; MANICHAEAN HETH
10ACE; MANICHAEAN TETH; R; MANICHAEAN TETH
10ACF; MANICHAEAN YODH; R; MANICHAEAN YODH
10AD0; MANICHAEAN KAPH; R; MANICHAEAN KAPH
10AD1; MANICHAEAN KAPH WITH DOT ABOVE; R; MANICHAEAN KAPH
10AD2; MANICHAEAN KAPH WITH 2 DOTS ABOVE; R; MANICHAEAN KAPH
10AD3; MANICHAEAN LAMEDH; D; MANICHAEAN LAMEDH
10AD4; MANICHAEAN DHAMEDH; D; MANICHAEAN DHAMEDH
10AD5; MANICHAEAN THAMEDH; D; MANICHAEAN THAMEDH
10AD6; MANICHAEAN MEM; D; MANICHAEAN MEM
10AD7; MANICHAEAN NUN; L; MANICHAEAN NUN
10AD8; MANICHAEAN SAMEKH; D; MANICHAEAN SAMEKH
10AD9; MANICHAEAN AYIN; D; MANICHAEAN AYIN
10ADA; MANICHAEAN AYIN WITH 2 DOTS ABOVE; D; MANICHAEAN AYIN
10ADB; MANICHAEAN PE; D; MANICHAEAN PE
10ADC; MANICHAEAN PE WITH DOT ABOVE; D; MANICHAEAN PE
10ADD; MANICHAEAN SADHE; R; MANICHAEAN SADHE
10ADE; MANICHAEAN QOPH; D; MANICHAEAN QOPH
10ADF; MANICHAEAN QOPH WITH DOT ABOVE; D; MANICHAEAN QOPH
10AE0; MANICHAEAN QOPH WITH 2 DOTS ABOVE; D; MANICHAEAN QOPH
10AE1; MANICHAEAN RESH; R; MANICHAEAN RESH
10AE2; MANICHAEAN SHIN; U; No_Joining_Group
10AE3; MANICHAEAN SHIN WITH 2 DOTS ABOVE; U; No_Joining_Group
10AE4; MANICHAEAN TAW; R; MANICHAEAN TAW
10AEB; MANICHAEAN ONE; D; MANICHAEAN ONE
10AEC; MANICHAEAN FIVE; D; MANICHAEAN FIVE
10AED; MANICHAEAN TEN; D; MANICHAEAN TEN
10AEE; MANICHAEAN TWENTY; D; MANICHAEAN TWENTY
10AEF; MANICHAEAN HUNDRED; R; MANICHAEAN HUNDRED
# Psalter Pahlavi Characters
10B80; PSALTER PAHLAVI ALEPH; D; No_Joining_Group
10B81; PSALTER PAHLAVI BETH; R; No_Joining_Group
10B82; PSALTER PAHLAVI GIMEL; D; No_Joining_Group
10B83; PSALTER PAHLAVI DALETH; R; No_Joining_Group
10B84; PSALTER PAHLAVI HE; R; No_Joining_Group
10B85; PSALTER PAHLAVI WAW-AYIN-RESH; R; No_Joining_Group
10B86; PSALTER PAHLAVI ZAYIN; D; No_Joining_Group
10B87; PSALTER PAHLAVI HETH; D; No_Joining_Group
10B88; PSALTER PAHLAVI YODH; D; No_Joining_Group
10B89; PSALTER PAHLAVI KAPH; R; No_Joining_Group
10B8A; PSALTER PAHLAVI LAMEDH; D; No_Joining_Group
10B8B; PSALTER PAHLAVI MEM-QOPH; D; No_Joining_Group
10B8C; PSALTER PAHLAVI NUN; R; No_Joining_Group
10B8D; PSALTER PAHLAVI SAMEKH; D; No_Joining_Group
10B8E; PSALTER PAHLAVI PE; R; No_Joining_Group
10B8F; PSALTER PAHLAVI SADHE; R; No_Joining_Group
10B90; PSALTER PAHLAVI SHIN; D; No_Joining_Group
10B91; PSALTER PAHLAVI TAW; R; No_Joining_Group
10BA9; PSALTER PAHLAVI ONE; R; No_Joining_Group
10BAA; PSALTER PAHLAVI TWO; R; No_Joining_Group
10BAB; PSALTER PAHLAVI THREE; R; No_Joining_Group
10BAC; PSALTER PAHLAVI FOUR; R; No_Joining_Group
10BAD; PSALTER PAHLAVI TEN; D; No_Joining_Group
10BAE; PSALTER PAHLAVI TWENTY; D; No_Joining_Group
10BAF; PSALTER PAHLAVI HUNDRED; U; No_Joining_Group
# Hanifi Rohingya Characters
10D00; HANIFI ROHINGYA A; L; No_Joining_Group
10D01; HANIFI ROHINGYA BA; D; No_Joining_Group
10D02; HANIFI ROHINGYA PA; D; HANIFI ROHINGYA PA
10D03; HANIFI ROHINGYA TA; D; No_Joining_Group
10D04; HANIFI ROHINGYA TTA; D; No_Joining_Group
10D05; HANIFI ROHINGYA JA; D; No_Joining_Group
10D06; HANIFI ROHINGYA CA; D; No_Joining_Group
10D07; HANIFI ROHINGYA HA; D; No_Joining_Group
10D08; HANIFI ROHINGYA KHA; D; No_Joining_Group
10D09; HANIFI ROHINGYA PA WITH DOT ABOVE; D; HANIFI ROHINGYA PA
10D0A; HANIFI ROHINGYA DA; D; No_Joining_Group
10D0B; HANIFI ROHINGYA DDA; D; No_Joining_Group
10D0C; HANIFI ROHINGYA RA; D; No_Joining_Group
10D0D; HANIFI ROHINGYA RRA; D; No_Joining_Group
10D0E; HANIFI ROHINGYA ZA; D; No_Joining_Group
10D0F; HANIFI ROHINGYA SA; D; No_Joining_Group
10D10; HANIFI ROHINGYA SHA; D; No_Joining_Group
10D11; HANIFI ROHINGYA KA; D; No_Joining_Group
10D12; HANIFI ROHINGYA GA; D; No_Joining_Group
10D13; HANIFI ROHINGYA LA; D; No_Joining_Group
10D14; HANIFI ROHINGYA MA; D; No_Joining_Group
10D15; HANIFI ROHINGYA NA; D; No_Joining_Group
10D16; HANIFI ROHINGYA WA; D; No_Joining_Group
10D17; HANIFI ROHINGYA KINNA WA; D; No_Joining_Group
10D18; HANIFI ROHINGYA YA; D; No_Joining_Group
10D19; HANIFI ROHINGYA KINNA YA; D; HANIFI ROHINGYA KINNA YA
10D1A; HANIFI ROHINGYA NGA; D; No_Joining_Group
10D1B; HANIFI ROHINGYA NYA; D; No_Joining_Group
10D1C; HANIFI ROHINGYA PA WITH 3 DOTS ABOVE; D; HANIFI ROHINGYA PA
10D1D; HANIFI ROHINGYA VOWEL A; D; No_Joining_Group
10D1E; HANIFI ROHINGYA DOTLESS KINNA YA WITH LEFT-FACING HOOK BELOW; D; HANIFI ROHINGYA KINNA YA
10D1F; HANIFI ROHINGYA VOWEL U; D; No_Joining_Group
10D20; HANIFI ROHINGYA DOTLESS KINNA YA WITH RIGHT-FACING HOOK BELOW; D; HANIFI ROHINGYA KINNA YA
10D21; HANIFI ROHINGYA VOWEL O; D; No_Joining_Group
10D22; HANIFI ROHINGYA SAKIN; R; No_Joining_Group
10D23; HANIFI ROHINGYA DOTLESS KINNA YA WITH DOT ABOVE; D; HANIFI ROHINGYA KINNA YA
# Sogdian Characters
10F30; SOGDIAN ALEPH; D; No_Joining_Group
10F31; SOGDIAN BETH; D; No_Joining_Group
10F32; SOGDIAN GIMEL; D; No_Joining_Group
10F33; SOGDIAN HE; R; No_Joining_Group
10F34; SOGDIAN WAW; D; No_Joining_Group
10F35; SOGDIAN ZAYIN; D; No_Joining_Group
10F36; SOGDIAN HETH; D; No_Joining_Group
10F37; SOGDIAN YODH; D; No_Joining_Group
10F38; SOGDIAN KAPH; D; No_Joining_Group
10F39; SOGDIAN LAMEDH; D; No_Joining_Group
10F3A; SOGDIAN MEM; D; No_Joining_Group
10F3B; SOGDIAN NUN; D; No_Joining_Group
10F3C; SOGDIAN SAMEKH; D; No_Joining_Group
10F3D; SOGDIAN AYIN; D; No_Joining_Group
10F3E; SOGDIAN PE; D; No_Joining_Group
10F3F; SOGDIAN SADHE; D; No_Joining_Group
10F40; SOGDIAN RESH-AYIN; D; No_Joining_Group
10F41; SOGDIAN SHIN; D; No_Joining_Group
10F42; SOGDIAN TAW; D; No_Joining_Group
10F43; SOGDIAN FETH; D; No_Joining_Group
10F44; SOGDIAN LESH; D; No_Joining_Group
10F45; SOGDIAN INDEPENDENT SHIN; U; No_Joining_Group
10F51; SOGDIAN ONE; D; No_Joining_Group
10F52; SOGDIAN TEN; D; No_Joining_Group
10F53; SOGDIAN TWENTY; D; No_Joining_Group
10F54; SOGDIAN ONE HUNDRED; R; No_Joining_Group
# Chorasmian Characters
10FB0; CHORASMIAN ALEPH; D; No_Joining_Group
10FB1; CHORASMIAN SMALL ALEPH; U; No_Joining_Group
10FB2; CHORASMIAN BETH; D; No_Joining_Group
10FB3; CHORASMIAN GIMEL; D; No_Joining_Group
10FB4; CHORASMIAN DALETH; R; No_Joining_Group
10FB5; CHORASMIAN HE; R; No_Joining_Group
10FB6; CHORASMIAN WAW; R; No_Joining_Group
10FB7; CHORASMIAN CURLED WAW; U; No_Joining_Group
10FB8; CHORASMIAN ZAYIN; D; No_Joining_Group
10FB9; CHORASMIAN HETH; R; No_Joining_Group
10FBA; CHORASMIAN YODH; R; No_Joining_Group
10FBB; CHORASMIAN KAPH; D; No_Joining_Group
10FBC; CHORASMIAN LAMEDH; D; No_Joining_Group
10FBD; CHORASMIAN MEM; R; No_Joining_Group
10FBE; CHORASMIAN NUN; D; No_Joining_Group
10FBF; CHORASMIAN SAMEKH; D; No_Joining_Group
10FC0; CHORASMIAN AYIN; U; No_Joining_Group
10FC1; CHORASMIAN PE; D; No_Joining_Group
10FC2; CHORASMIAN RESH; R; No_Joining_Group
10FC3; CHORASMIAN SHIN; R; No_Joining_Group
10FC4; CHORASMIAN TAW; D; No_Joining_Group
10FC5; CHORASMIAN ONE; U; No_Joining_Group
10FC6; CHORASMIAN TWO; U; No_Joining_Group
10FC7; CHORASMIAN THREE; U; No_Joining_Group
10FC8; CHORASMIAN FOUR; U; No_Joining_Group
10FC9; CHORASMIAN TEN; R; No_Joining_Group
10FCA; CHORASMIAN TWENTY; D; No_Joining_Group
10FCB; CHORASMIAN ONE HUNDRED; L; No_Joining_Group
# Kaithi Number Signs
# These are prepended concatenation marks, comparable
# to the number signs in the Arabic script.
# Listed here for consistency in property values.
110BD; KAITHI NUMBER SIGN; U; No_Joining_Group
110CD; KAITHI NUMBER SIGN ABOVE; U; No_Joining_Group
# Adlam Characters
1E900;ADLAM CAPITAL ALIF; D; No_Joining_Group
1E901;ADLAM CAPITAL DAALI; D; No_Joining_Group
1E902;ADLAM CAPITAL LAAM; D; No_Joining_Group
1E903;ADLAM CAPITAL MIIM; D; No_Joining_Group
1E904;ADLAM CAPITAL BA; D; No_Joining_Group
1E905;ADLAM CAPITAL SINNYIIYHE; D; No_Joining_Group
1E906;ADLAM CAPITAL PE; D; No_Joining_Group
1E907;ADLAM CAPITAL BHE; D; No_Joining_Group
1E908;ADLAM CAPITAL RA; D; No_Joining_Group
1E909;ADLAM CAPITAL E; D; No_Joining_Group
1E90A;ADLAM CAPITAL FA; D; No_Joining_Group
1E90B;ADLAM CAPITAL I; D; No_Joining_Group
1E90C;ADLAM CAPITAL O; D; No_Joining_Group
1E90D;ADLAM CAPITAL DHA; D; No_Joining_Group
1E90E;ADLAM CAPITAL YHE; D; No_Joining_Group
1E90F;ADLAM CAPITAL WAW; D; No_Joining_Group
1E910;ADLAM CAPITAL NUN; D; No_Joining_Group
1E911;ADLAM CAPITAL KAF; D; No_Joining_Group
1E912;ADLAM CAPITAL YA; D; No_Joining_Group
1E913;ADLAM CAPITAL U; D; No_Joining_Group
1E914;ADLAM CAPITAL JIIM; D; No_Joining_Group
1E915;ADLAM CAPITAL CHI; D; No_Joining_Group
1E916;ADLAM CAPITAL HA; D; No_Joining_Group
1E917;ADLAM CAPITAL QAAF; D; No_Joining_Group
1E918;ADLAM CAPITAL GA; D; No_Joining_Group
1E919;ADLAM CAPITAL NYA; D; No_Joining_Group
1E91A;ADLAM CAPITAL TU; D; No_Joining_Group
1E91B;ADLAM CAPITAL NHA; D; No_Joining_Group
1E91C;ADLAM CAPITAL VA; D; No_Joining_Group
1E91D;ADLAM CAPITAL KHA; D; No_Joining_Group
1E91E;ADLAM CAPITAL GBE; D; No_Joining_Group
1E91F;ADLAM CAPITAL ZAL; D; No_Joining_Group
1E920;ADLAM CAPITAL KPO; D; No_Joining_Group
1E921;ADLAM CAPITAL SHA; D; No_Joining_Group
1E922;ADLAM SMALL ALIF; D; No_Joining_Group
1E923;ADLAM SMALL DAALI; D; No_Joining_Group
1E924;ADLAM SMALL LAAM; D; No_Joining_Group
1E925;ADLAM SMALL MIIM; D; No_Joining_Group
1E926;ADLAM SMALL BA; D; No_Joining_Group
1E927;ADLAM SMALL SINNYIIYHE; D; No_Joining_Group
1E928;ADLAM SMALL PE; D; No_Joining_Group
1E929;ADLAM SMALL BHE; D; No_Joining_Group
1E92A;ADLAM SMALL RA; D; No_Joining_Group
1E92B;ADLAM SMALL E; D; No_Joining_Group
1E92C;ADLAM SMALL FA; D; No_Joining_Group
1E92D;ADLAM SMALL I; D; No_Joining_Group
1E92E;ADLAM SMALL O; D; No_Joining_Group
1E92F;ADLAM SMALL DHA; D; No_Joining_Group
1E930;ADLAM SMALL YHE; D; No_Joining_Group
1E931;ADLAM SMALL WAW; D; No_Joining_Group
1E932;ADLAM SMALL NUN; D; No_Joining_Group
1E933;ADLAM SMALL KAF; D; No_Joining_Group
1E934;ADLAM SMALL YA; D; No_Joining_Group
1E935;ADLAM SMALL U; D; No_Joining_Group
1E936;ADLAM SMALL JIIM; D; No_Joining_Group
1E937;ADLAM SMALL CHI; D; No_Joining_Group
1E938;ADLAM SMALL HA; D; No_Joining_Group
1E939;ADLAM SMALL QAAF; D; No_Joining_Group
1E93A;ADLAM SMALL GA; D; No_Joining_Group
1E93B;ADLAM SMALL NYA; D; No_Joining_Group
1E93C;ADLAM SMALL TU; D; No_Joining_Group
1E93D;ADLAM SMALL NHA; D; No_Joining_Group
1E93E;ADLAM SMALL VA; D; No_Joining_Group
1E93F;ADLAM SMALL KHA; D; No_Joining_Group
1E940;ADLAM SMALL GBE; D; No_Joining_Group
1E941;ADLAM SMALL ZAL; D; No_Joining_Group
1E942;ADLAM SMALL KPO; D; No_Joining_Group
1E943;ADLAM SMALL SHA; D; No_Joining_Group
1E94B;ADLAM NASALIZATION MARK; T; No_Joining_Group
# EOF

View File

@@ -0,0 +1,185 @@
# BidiBrackets-13.0.0.txt
# Date: 2019-09-09, 19:31:00 GMT [AG, LI, KW]
# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
# Unicode Character Database
# For documentation, see http://www.unicode.org/reports/tr44/
#
# Bidi_Paired_Bracket and Bidi_Paired_Bracket_Type Properties
#
# This file is a normative contributory data file in the Unicode
# Character Database.
#
# Bidi_Paired_Bracket is a normative property of type Miscellaneous,
# which establishes a mapping between characters that are treated as
# bracket pairs by the Unicode Bidirectional Algorithm.
#
# Bidi_Paired_Bracket_Type is a normative property of type Enumeration,
# which classifies characters into opening and closing paired brackets
# for the purposes of the Unicode Bidirectional Algorithm.
#
# This file lists the set of code points with Bidi_Paired_Bracket_Type
# property values Open and Close. The set is derived from the character
# properties General_Category (gc), Bidi_Class (bc), Bidi_Mirrored (Bidi_M),
# and Bidi_Mirroring_Glyph (bmg), as follows: two characters, A and B,
# form a bracket pair if A has gc=Ps and B has gc=Pe, both have bc=ON and
# Bidi_M=Y, and bmg of A is B. Bidi_Paired_Bracket (bpb) maps A to B and
# vice versa, and their Bidi_Paired_Bracket_Type (bpt) property values are
# Open (o) and Close (c), respectively.
#
# The brackets with ticks U+298D LEFT SQUARE BRACKET WITH TICK IN TOP CORNER
# through U+2990 RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER are paired the
# same way their glyphs form mirror pairs, according to their bmg property
# values. They are not paired on the basis of a diagonal or antidiagonal
# matching of the corner ticks inferred from code point order.
#
# For legacy reasons, the characters U+FD3E ORNATE LEFT PARENTHESIS and
# U+FD3F ORNATE RIGHT PARENTHESIS do not mirror in bidirectional display
# and therefore do not form a bracket pair.
#
# The Unicode property value stability policy guarantees that characters
# which have bpt=o or bpt=c also have bc=ON and Bidi_M=Y. As a result, an
# implementation can optimize the lookup of the Bidi_Paired_Bracket_Type
# property values Open and Close by restricting the processing to characters
# with bc=ON.
#
# The format of the file is three fields separated by a semicolon.
# Field 0: Unicode code point value, represented as a hexadecimal value
# Field 1: Bidi_Paired_Bracket property value, a code point value or <none>
# Field 2: Bidi_Paired_Bracket_Type property value, one of the following:
# o Open
# c Close
# n None
# The names of the characters in field 0 are given in comments at the end
# of each line.
#
# For information on bidirectional paired brackets, see UAX #9: Unicode
# Bidirectional Algorithm, at http://www.unicode.org/unicode/reports/tr9/
#
# This file was originally created by Andrew Glass and Laurentiu Iancu
# for Unicode 6.3.
0028; 0029; o # LEFT PARENTHESIS
0029; 0028; c # RIGHT PARENTHESIS
005B; 005D; o # LEFT SQUARE BRACKET
005D; 005B; c # RIGHT SQUARE BRACKET
007B; 007D; o # LEFT CURLY BRACKET
007D; 007B; c # RIGHT CURLY BRACKET
0F3A; 0F3B; o # TIBETAN MARK GUG RTAGS GYON
0F3B; 0F3A; c # TIBETAN MARK GUG RTAGS GYAS
0F3C; 0F3D; o # TIBETAN MARK ANG KHANG GYON
0F3D; 0F3C; c # TIBETAN MARK ANG KHANG GYAS
169B; 169C; o # OGHAM FEATHER MARK
169C; 169B; c # OGHAM REVERSED FEATHER MARK
2045; 2046; o # LEFT SQUARE BRACKET WITH QUILL
2046; 2045; c # RIGHT SQUARE BRACKET WITH QUILL
207D; 207E; o # SUPERSCRIPT LEFT PARENTHESIS
207E; 207D; c # SUPERSCRIPT RIGHT PARENTHESIS
208D; 208E; o # SUBSCRIPT LEFT PARENTHESIS
208E; 208D; c # SUBSCRIPT RIGHT PARENTHESIS
2308; 2309; o # LEFT CEILING
2309; 2308; c # RIGHT CEILING
230A; 230B; o # LEFT FLOOR
230B; 230A; c # RIGHT FLOOR
2329; 232A; o # LEFT-POINTING ANGLE BRACKET
232A; 2329; c # RIGHT-POINTING ANGLE BRACKET
2768; 2769; o # MEDIUM LEFT PARENTHESIS ORNAMENT
2769; 2768; c # MEDIUM RIGHT PARENTHESIS ORNAMENT
276A; 276B; o # MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT
276B; 276A; c # MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT
276C; 276D; o # MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT
276D; 276C; c # MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT
276E; 276F; o # HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT
276F; 276E; c # HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT
2770; 2771; o # HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT
2771; 2770; c # HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT
2772; 2773; o # LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT
2773; 2772; c # LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT
2774; 2775; o # MEDIUM LEFT CURLY BRACKET ORNAMENT
2775; 2774; c # MEDIUM RIGHT CURLY BRACKET ORNAMENT
27C5; 27C6; o # LEFT S-SHAPED BAG DELIMITER
27C6; 27C5; c # RIGHT S-SHAPED BAG DELIMITER
27E6; 27E7; o # MATHEMATICAL LEFT WHITE SQUARE BRACKET
27E7; 27E6; c # MATHEMATICAL RIGHT WHITE SQUARE BRACKET
27E8; 27E9; o # MATHEMATICAL LEFT ANGLE BRACKET
27E9; 27E8; c # MATHEMATICAL RIGHT ANGLE BRACKET
27EA; 27EB; o # MATHEMATICAL LEFT DOUBLE ANGLE BRACKET
27EB; 27EA; c # MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET
27EC; 27ED; o # MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET
27ED; 27EC; c # MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET
27EE; 27EF; o # MATHEMATICAL LEFT FLATTENED PARENTHESIS
27EF; 27EE; c # MATHEMATICAL RIGHT FLATTENED PARENTHESIS
2983; 2984; o # LEFT WHITE CURLY BRACKET
2984; 2983; c # RIGHT WHITE CURLY BRACKET
2985; 2986; o # LEFT WHITE PARENTHESIS
2986; 2985; c # RIGHT WHITE PARENTHESIS
2987; 2988; o # Z NOTATION LEFT IMAGE BRACKET
2988; 2987; c # Z NOTATION RIGHT IMAGE BRACKET
2989; 298A; o # Z NOTATION LEFT BINDING BRACKET
298A; 2989; c # Z NOTATION RIGHT BINDING BRACKET
298B; 298C; o # LEFT SQUARE BRACKET WITH UNDERBAR
298C; 298B; c # RIGHT SQUARE BRACKET WITH UNDERBAR
298D; 2990; o # LEFT SQUARE BRACKET WITH TICK IN TOP CORNER
298E; 298F; c # RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
298F; 298E; o # LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
2990; 298D; c # RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER
2991; 2992; o # LEFT ANGLE BRACKET WITH DOT
2992; 2991; c # RIGHT ANGLE BRACKET WITH DOT
2993; 2994; o # LEFT ARC LESS-THAN BRACKET
2994; 2993; c # RIGHT ARC GREATER-THAN BRACKET
2995; 2996; o # DOUBLE LEFT ARC GREATER-THAN BRACKET
2996; 2995; c # DOUBLE RIGHT ARC LESS-THAN BRACKET
2997; 2998; o # LEFT BLACK TORTOISE SHELL BRACKET
2998; 2997; c # RIGHT BLACK TORTOISE SHELL BRACKET
29D8; 29D9; o # LEFT WIGGLY FENCE
29D9; 29D8; c # RIGHT WIGGLY FENCE
29DA; 29DB; o # LEFT DOUBLE WIGGLY FENCE
29DB; 29DA; c # RIGHT DOUBLE WIGGLY FENCE
29FC; 29FD; o # LEFT-POINTING CURVED ANGLE BRACKET
29FD; 29FC; c # RIGHT-POINTING CURVED ANGLE BRACKET
2E22; 2E23; o # TOP LEFT HALF BRACKET
2E23; 2E22; c # TOP RIGHT HALF BRACKET
2E24; 2E25; o # BOTTOM LEFT HALF BRACKET
2E25; 2E24; c # BOTTOM RIGHT HALF BRACKET
2E26; 2E27; o # LEFT SIDEWAYS U BRACKET
2E27; 2E26; c # RIGHT SIDEWAYS U BRACKET
2E28; 2E29; o # LEFT DOUBLE PARENTHESIS
2E29; 2E28; c # RIGHT DOUBLE PARENTHESIS
3008; 3009; o # LEFT ANGLE BRACKET
3009; 3008; c # RIGHT ANGLE BRACKET
300A; 300B; o # LEFT DOUBLE ANGLE BRACKET
300B; 300A; c # RIGHT DOUBLE ANGLE BRACKET
300C; 300D; o # LEFT CORNER BRACKET
300D; 300C; c # RIGHT CORNER BRACKET
300E; 300F; o # LEFT WHITE CORNER BRACKET
300F; 300E; c # RIGHT WHITE CORNER BRACKET
3010; 3011; o # LEFT BLACK LENTICULAR BRACKET
3011; 3010; c # RIGHT BLACK LENTICULAR BRACKET
3014; 3015; o # LEFT TORTOISE SHELL BRACKET
3015; 3014; c # RIGHT TORTOISE SHELL BRACKET
3016; 3017; o # LEFT WHITE LENTICULAR BRACKET
3017; 3016; c # RIGHT WHITE LENTICULAR BRACKET
3018; 3019; o # LEFT WHITE TORTOISE SHELL BRACKET
3019; 3018; c # RIGHT WHITE TORTOISE SHELL BRACKET
301A; 301B; o # LEFT WHITE SQUARE BRACKET
301B; 301A; c # RIGHT WHITE SQUARE BRACKET
FE59; FE5A; o # SMALL LEFT PARENTHESIS
FE5A; FE59; c # SMALL RIGHT PARENTHESIS
FE5B; FE5C; o # SMALL LEFT CURLY BRACKET
FE5C; FE5B; c # SMALL RIGHT CURLY BRACKET
FE5D; FE5E; o # SMALL LEFT TORTOISE SHELL BRACKET
FE5E; FE5D; c # SMALL RIGHT TORTOISE SHELL BRACKET
FF08; FF09; o # FULLWIDTH LEFT PARENTHESIS
FF09; FF08; c # FULLWIDTH RIGHT PARENTHESIS
FF3B; FF3D; o # FULLWIDTH LEFT SQUARE BRACKET
FF3D; FF3B; c # FULLWIDTH RIGHT SQUARE BRACKET
FF5B; FF5D; o # FULLWIDTH LEFT CURLY BRACKET
FF5D; FF5B; c # FULLWIDTH RIGHT CURLY BRACKET
FF5F; FF60; o # FULLWIDTH LEFT WHITE PARENTHESIS
FF60; FF5F; c # FULLWIDTH RIGHT WHITE PARENTHESIS
FF62; FF63; o # HALFWIDTH LEFT CORNER BRACKET
FF63; FF62; c # HALFWIDTH RIGHT CORNER BRACKET
# EOF

View File

@@ -0,0 +1,2 @@
0 fail
91699 success

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,625 @@
# BidiMirroring-13.0.0.txt
# Date: 2019-09-09, 19:34:00 GMT [KW, LI, RP]
# © 2019 Unicode®, Inc.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
# Unicode Character Database
# For documentation, see http://www.unicode.org/reports/tr44/
#
# Bidi_Mirroring_Glyph Property
#
# This file is an informative contributory data file in the
# Unicode Character Database.
#
# This data file lists characters that have the Bidi_Mirrored=Yes property
# value, for which there is another Unicode character that typically has a glyph
# that is the mirror image of the original character's glyph.
#
# The repertoire covered by the file is Unicode 13.0.0.
#
# The file contains a list of lines with mappings from one code point
# to another one for character-based mirroring.
# Note that for "real" mirroring, a rendering engine needs to select
# appropriate alternative glyphs, and that many Unicode characters do not
# have a mirror-image Unicode character.
#
# Each mapping line contains two fields, separated by a semicolon (';').
# Each of the two fields contains a code point represented as a
# variable-length hexadecimal value with 4 to 6 digits.
# A comment indicates where the characters are "BEST FIT" mirroring.
#
# Code points for which Bidi_Mirrored=Yes, but for which no appropriate
# characters exist with mirrored glyphs, are
# listed as comments at the end of the file.
#
# Formally, the default value of the Bidi_Mirroring_Glyph property
# for each code point is <none>, unless a mapping to
# some other character is specified in this data file. When a code
# point has the default value for the Bidi_Mirroring_Glyph property,
# that means that no other character exists whose glyph is suitable
# for character-based mirroring.
#
# For information on bidi mirroring, see UAX #9: Unicode Bidirectional Algorithm,
# at http://www.unicode.org/unicode/reports/tr9/
#
# This file was originally created by Markus Scherer.
# Extended for Unicode 3.2, 4.0, 4.1, 5.0, 5.1, 5.2, and 6.0 by Ken Whistler,
# and for subsequent versions by Ken Whistler, Laurentiu Iancu, and Roozbeh Pournader.
#
# Historical and Compatibility Information:
#
# The OpenType Mirroring Pairs List (OMPL) is frozen to match the
# Unicode 5.1 version of the Bidi_Mirroring_Glyph property (2008).
# See https://www.microsoft.com/typography/otspec/ompl.txt
#
# The Unicode 6.1 version of the Bidi_Mirroring_Glyph property (2011)
# added one mirroring pair: 27CB <--> 27CD.
#
# The Unicode 11.0 version of the Bidi_Mirroring_Glyph property (2018)
# underwent a substantial revision, to formally recognize all of the
# exact mirroring pairs and "BEST FIT" mirroring pairs that had been
# added after the freezing of the OMPL list. As a result, starting
# with Unicode 11.0, the bmg mapping values more accurately reflect
# the current status of glyphs for Bidi_Mirrored characters in
# the Unicode Standard, but this listing now extends significantly
# beyond the frozen OMPL list. Implementers should be aware of this
# intentional distinction.
#
# ############################################################
#
# Property: Bidi_Mirroring_Glyph
#
# @missing: 0000..10FFFF; <none>
0028; 0029 # LEFT PARENTHESIS
0029; 0028 # RIGHT PARENTHESIS
003C; 003E # LESS-THAN SIGN
003E; 003C # GREATER-THAN SIGN
005B; 005D # LEFT SQUARE BRACKET
005D; 005B # RIGHT SQUARE BRACKET
007B; 007D # LEFT CURLY BRACKET
007D; 007B # RIGHT CURLY BRACKET
00AB; 00BB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
00BB; 00AB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
0F3A; 0F3B # TIBETAN MARK GUG RTAGS GYON
0F3B; 0F3A # TIBETAN MARK GUG RTAGS GYAS
0F3C; 0F3D # TIBETAN MARK ANG KHANG GYON
0F3D; 0F3C # TIBETAN MARK ANG KHANG GYAS
169B; 169C # OGHAM FEATHER MARK
169C; 169B # OGHAM REVERSED FEATHER MARK
2039; 203A # SINGLE LEFT-POINTING ANGLE QUOTATION MARK
203A; 2039 # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
2045; 2046 # LEFT SQUARE BRACKET WITH QUILL
2046; 2045 # RIGHT SQUARE BRACKET WITH QUILL
207D; 207E # SUPERSCRIPT LEFT PARENTHESIS
207E; 207D # SUPERSCRIPT RIGHT PARENTHESIS
208D; 208E # SUBSCRIPT LEFT PARENTHESIS
208E; 208D # SUBSCRIPT RIGHT PARENTHESIS
2208; 220B # ELEMENT OF
2209; 220C # NOT AN ELEMENT OF
220A; 220D # SMALL ELEMENT OF
220B; 2208 # CONTAINS AS MEMBER
220C; 2209 # DOES NOT CONTAIN AS MEMBER
220D; 220A # SMALL CONTAINS AS MEMBER
2215; 29F5 # DIVISION SLASH
221F; 2BFE # RIGHT ANGLE
2220; 29A3 # ANGLE
2221; 299B # MEASURED ANGLE
2222; 29A0 # SPHERICAL ANGLE
2224; 2AEE # DOES NOT DIVIDE
223C; 223D # TILDE OPERATOR
223D; 223C # REVERSED TILDE
2243; 22CD # ASYMPTOTICALLY EQUAL TO
2245; 224C # APPROXIMATELY EQUAL TO
224C; 2245 # ALL EQUAL TO
2252; 2253 # APPROXIMATELY EQUAL TO OR THE IMAGE OF
2253; 2252 # IMAGE OF OR APPROXIMATELY EQUAL TO
2254; 2255 # COLON EQUALS
2255; 2254 # EQUALS COLON
2264; 2265 # LESS-THAN OR EQUAL TO
2265; 2264 # GREATER-THAN OR EQUAL TO
2266; 2267 # LESS-THAN OVER EQUAL TO
2267; 2266 # GREATER-THAN OVER EQUAL TO
2268; 2269 # [BEST FIT] LESS-THAN BUT NOT EQUAL TO
2269; 2268 # [BEST FIT] GREATER-THAN BUT NOT EQUAL TO
226A; 226B # MUCH LESS-THAN
226B; 226A # MUCH GREATER-THAN
226E; 226F # [BEST FIT] NOT LESS-THAN
226F; 226E # [BEST FIT] NOT GREATER-THAN
2270; 2271 # [BEST FIT] NEITHER LESS-THAN NOR EQUAL TO
2271; 2270 # [BEST FIT] NEITHER GREATER-THAN NOR EQUAL TO
2272; 2273 # [BEST FIT] LESS-THAN OR EQUIVALENT TO
2273; 2272 # [BEST FIT] GREATER-THAN OR EQUIVALENT TO
2274; 2275 # [BEST FIT] NEITHER LESS-THAN NOR EQUIVALENT TO
2275; 2274 # [BEST FIT] NEITHER GREATER-THAN NOR EQUIVALENT TO
2276; 2277 # LESS-THAN OR GREATER-THAN
2277; 2276 # GREATER-THAN OR LESS-THAN
2278; 2279 # [BEST FIT] NEITHER LESS-THAN NOR GREATER-THAN
2279; 2278 # [BEST FIT] NEITHER GREATER-THAN NOR LESS-THAN
227A; 227B # PRECEDES
227B; 227A # SUCCEEDS
227C; 227D # PRECEDES OR EQUAL TO
227D; 227C # SUCCEEDS OR EQUAL TO
227E; 227F # [BEST FIT] PRECEDES OR EQUIVALENT TO
227F; 227E # [BEST FIT] SUCCEEDS OR EQUIVALENT TO
2280; 2281 # [BEST FIT] DOES NOT PRECEDE
2281; 2280 # [BEST FIT] DOES NOT SUCCEED
2282; 2283 # SUBSET OF
2283; 2282 # SUPERSET OF
2284; 2285 # [BEST FIT] NOT A SUBSET OF
2285; 2284 # [BEST FIT] NOT A SUPERSET OF
2286; 2287 # SUBSET OF OR EQUAL TO
2287; 2286 # SUPERSET OF OR EQUAL TO
2288; 2289 # [BEST FIT] NEITHER A SUBSET OF NOR EQUAL TO
2289; 2288 # [BEST FIT] NEITHER A SUPERSET OF NOR EQUAL TO
228A; 228B # [BEST FIT] SUBSET OF WITH NOT EQUAL TO
228B; 228A # [BEST FIT] SUPERSET OF WITH NOT EQUAL TO
228F; 2290 # SQUARE IMAGE OF
2290; 228F # SQUARE ORIGINAL OF
2291; 2292 # SQUARE IMAGE OF OR EQUAL TO
2292; 2291 # SQUARE ORIGINAL OF OR EQUAL TO
2298; 29B8 # CIRCLED DIVISION SLASH
22A2; 22A3 # RIGHT TACK
22A3; 22A2 # LEFT TACK
22A6; 2ADE # ASSERTION
22A8; 2AE4 # TRUE
22A9; 2AE3 # FORCES
22AB; 2AE5 # DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
22B0; 22B1 # PRECEDES UNDER RELATION
22B1; 22B0 # SUCCEEDS UNDER RELATION
22B2; 22B3 # NORMAL SUBGROUP OF
22B3; 22B2 # CONTAINS AS NORMAL SUBGROUP
22B4; 22B5 # NORMAL SUBGROUP OF OR EQUAL TO
22B5; 22B4 # CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
22B6; 22B7 # ORIGINAL OF
22B7; 22B6 # IMAGE OF
22B8; 27DC # MULTIMAP
22C9; 22CA # LEFT NORMAL FACTOR SEMIDIRECT PRODUCT
22CA; 22C9 # RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT
22CB; 22CC # LEFT SEMIDIRECT PRODUCT
22CC; 22CB # RIGHT SEMIDIRECT PRODUCT
22CD; 2243 # REVERSED TILDE EQUALS
22D0; 22D1 # DOUBLE SUBSET
22D1; 22D0 # DOUBLE SUPERSET
22D6; 22D7 # LESS-THAN WITH DOT
22D7; 22D6 # GREATER-THAN WITH DOT
22D8; 22D9 # VERY MUCH LESS-THAN
22D9; 22D8 # VERY MUCH GREATER-THAN
22DA; 22DB # LESS-THAN EQUAL TO OR GREATER-THAN
22DB; 22DA # GREATER-THAN EQUAL TO OR LESS-THAN
22DC; 22DD # EQUAL TO OR LESS-THAN
22DD; 22DC # EQUAL TO OR GREATER-THAN
22DE; 22DF # EQUAL TO OR PRECEDES
22DF; 22DE # EQUAL TO OR SUCCEEDS
22E0; 22E1 # [BEST FIT] DOES NOT PRECEDE OR EQUAL
22E1; 22E0 # [BEST FIT] DOES NOT SUCCEED OR EQUAL
22E2; 22E3 # [BEST FIT] NOT SQUARE IMAGE OF OR EQUAL TO
22E3; 22E2 # [BEST FIT] NOT SQUARE ORIGINAL OF OR EQUAL TO
22E4; 22E5 # [BEST FIT] SQUARE IMAGE OF OR NOT EQUAL TO
22E5; 22E4 # [BEST FIT] SQUARE ORIGINAL OF OR NOT EQUAL TO
22E6; 22E7 # [BEST FIT] LESS-THAN BUT NOT EQUIVALENT TO
22E7; 22E6 # [BEST FIT] GREATER-THAN BUT NOT EQUIVALENT TO
22E8; 22E9 # [BEST FIT] PRECEDES BUT NOT EQUIVALENT TO
22E9; 22E8 # [BEST FIT] SUCCEEDS BUT NOT EQUIVALENT TO
22EA; 22EB # [BEST FIT] NOT NORMAL SUBGROUP OF
22EB; 22EA # [BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP
22EC; 22ED # [BEST FIT] NOT NORMAL SUBGROUP OF OR EQUAL TO
22ED; 22EC # [BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
22F0; 22F1 # UP RIGHT DIAGONAL ELLIPSIS
22F1; 22F0 # DOWN RIGHT DIAGONAL ELLIPSIS
22F2; 22FA # ELEMENT OF WITH LONG HORIZONTAL STROKE
22F3; 22FB # ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
22F4; 22FC # SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
22F6; 22FD # ELEMENT OF WITH OVERBAR
22F7; 22FE # SMALL ELEMENT OF WITH OVERBAR
22FA; 22F2 # CONTAINS WITH LONG HORIZONTAL STROKE
22FB; 22F3 # CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
22FC; 22F4 # SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
22FD; 22F6 # CONTAINS WITH OVERBAR
22FE; 22F7 # SMALL CONTAINS WITH OVERBAR
2308; 2309 # LEFT CEILING
2309; 2308 # RIGHT CEILING
230A; 230B # LEFT FLOOR
230B; 230A # RIGHT FLOOR
2329; 232A # LEFT-POINTING ANGLE BRACKET
232A; 2329 # RIGHT-POINTING ANGLE BRACKET
2768; 2769 # MEDIUM LEFT PARENTHESIS ORNAMENT
2769; 2768 # MEDIUM RIGHT PARENTHESIS ORNAMENT
276A; 276B # MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT
276B; 276A # MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT
276C; 276D # MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT
276D; 276C # MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT
276E; 276F # HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT
276F; 276E # HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT
2770; 2771 # HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT
2771; 2770 # HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT
2772; 2773 # LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT
2773; 2772 # LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT
2774; 2775 # MEDIUM LEFT CURLY BRACKET ORNAMENT
2775; 2774 # MEDIUM RIGHT CURLY BRACKET ORNAMENT
27C3; 27C4 # OPEN SUBSET
27C4; 27C3 # OPEN SUPERSET
27C5; 27C6 # LEFT S-SHAPED BAG DELIMITER
27C6; 27C5 # RIGHT S-SHAPED BAG DELIMITER
27C8; 27C9 # REVERSE SOLIDUS PRECEDING SUBSET
27C9; 27C8 # SUPERSET PRECEDING SOLIDUS
27CB; 27CD # MATHEMATICAL RISING DIAGONAL
27CD; 27CB # MATHEMATICAL FALLING DIAGONAL
27D5; 27D6 # LEFT OUTER JOIN
27D6; 27D5 # RIGHT OUTER JOIN
27DC; 22B8 # LEFT MULTIMAP
27DD; 27DE # LONG RIGHT TACK
27DE; 27DD # LONG LEFT TACK
27E2; 27E3 # WHITE CONCAVE-SIDED DIAMOND WITH LEFTWARDS TICK
27E3; 27E2 # WHITE CONCAVE-SIDED DIAMOND WITH RIGHTWARDS TICK
27E4; 27E5 # WHITE SQUARE WITH LEFTWARDS TICK
27E5; 27E4 # WHITE SQUARE WITH RIGHTWARDS TICK
27E6; 27E7 # MATHEMATICAL LEFT WHITE SQUARE BRACKET
27E7; 27E6 # MATHEMATICAL RIGHT WHITE SQUARE BRACKET
27E8; 27E9 # MATHEMATICAL LEFT ANGLE BRACKET
27E9; 27E8 # MATHEMATICAL RIGHT ANGLE BRACKET
27EA; 27EB # MATHEMATICAL LEFT DOUBLE ANGLE BRACKET
27EB; 27EA # MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET
27EC; 27ED # MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET
27ED; 27EC # MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET
27EE; 27EF # MATHEMATICAL LEFT FLATTENED PARENTHESIS
27EF; 27EE # MATHEMATICAL RIGHT FLATTENED PARENTHESIS
2983; 2984 # LEFT WHITE CURLY BRACKET
2984; 2983 # RIGHT WHITE CURLY BRACKET
2985; 2986 # LEFT WHITE PARENTHESIS
2986; 2985 # RIGHT WHITE PARENTHESIS
2987; 2988 # Z NOTATION LEFT IMAGE BRACKET
2988; 2987 # Z NOTATION RIGHT IMAGE BRACKET
2989; 298A # Z NOTATION LEFT BINDING BRACKET
298A; 2989 # Z NOTATION RIGHT BINDING BRACKET
298B; 298C # LEFT SQUARE BRACKET WITH UNDERBAR
298C; 298B # RIGHT SQUARE BRACKET WITH UNDERBAR
298D; 2990 # LEFT SQUARE BRACKET WITH TICK IN TOP CORNER
298E; 298F # RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
298F; 298E # LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
2990; 298D # RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER
2991; 2992 # LEFT ANGLE BRACKET WITH DOT
2992; 2991 # RIGHT ANGLE BRACKET WITH DOT
2993; 2994 # LEFT ARC LESS-THAN BRACKET
2994; 2993 # RIGHT ARC GREATER-THAN BRACKET
2995; 2996 # DOUBLE LEFT ARC GREATER-THAN BRACKET
2996; 2995 # DOUBLE RIGHT ARC LESS-THAN BRACKET
2997; 2998 # LEFT BLACK TORTOISE SHELL BRACKET
2998; 2997 # RIGHT BLACK TORTOISE SHELL BRACKET
299B; 2221 # MEASURED ANGLE OPENING LEFT
29A0; 2222 # SPHERICAL ANGLE OPENING LEFT
29A3; 2220 # REVERSED ANGLE
29A4; 29A5 # ANGLE WITH UNDERBAR
29A5; 29A4 # REVERSED ANGLE WITH UNDERBAR
29A8; 29A9 # MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT
29A9; 29A8 # MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT
29AA; 29AB # MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT
29AB; 29AA # MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT
29AC; 29AD # MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP
29AD; 29AC # MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP
29AE; 29AF # MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN
29AF; 29AE # MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN
29B8; 2298 # CIRCLED REVERSE SOLIDUS
29C0; 29C1 # CIRCLED LESS-THAN
29C1; 29C0 # CIRCLED GREATER-THAN
29C4; 29C5 # SQUARED RISING DIAGONAL SLASH
29C5; 29C4 # SQUARED FALLING DIAGONAL SLASH
29CF; 29D0 # LEFT TRIANGLE BESIDE VERTICAL BAR
29D0; 29CF # VERTICAL BAR BESIDE RIGHT TRIANGLE
29D1; 29D2 # BOWTIE WITH LEFT HALF BLACK
29D2; 29D1 # BOWTIE WITH RIGHT HALF BLACK
29D4; 29D5 # TIMES WITH LEFT HALF BLACK
29D5; 29D4 # TIMES WITH RIGHT HALF BLACK
29D8; 29D9 # LEFT WIGGLY FENCE
29D9; 29D8 # RIGHT WIGGLY FENCE
29DA; 29DB # LEFT DOUBLE WIGGLY FENCE
29DB; 29DA # RIGHT DOUBLE WIGGLY FENCE
29E8; 29E9 # DOWN-POINTING TRIANGLE WITH LEFT HALF BLACK
29E9; 29E8 # DOWN-POINTING TRIANGLE WITH RIGHT HALF BLACK
29F5; 2215 # REVERSE SOLIDUS OPERATOR
29F8; 29F9 # BIG SOLIDUS
29F9; 29F8 # BIG REVERSE SOLIDUS
29FC; 29FD # LEFT-POINTING CURVED ANGLE BRACKET
29FD; 29FC # RIGHT-POINTING CURVED ANGLE BRACKET
2A2B; 2A2C # MINUS SIGN WITH FALLING DOTS
2A2C; 2A2B # MINUS SIGN WITH RISING DOTS
2A2D; 2A2E # PLUS SIGN IN LEFT HALF CIRCLE
2A2E; 2A2D # PLUS SIGN IN RIGHT HALF CIRCLE
2A34; 2A35 # MULTIPLICATION SIGN IN LEFT HALF CIRCLE
2A35; 2A34 # MULTIPLICATION SIGN IN RIGHT HALF CIRCLE
2A3C; 2A3D # INTERIOR PRODUCT
2A3D; 2A3C # RIGHTHAND INTERIOR PRODUCT
2A64; 2A65 # Z NOTATION DOMAIN ANTIRESTRICTION
2A65; 2A64 # Z NOTATION RANGE ANTIRESTRICTION
2A79; 2A7A # LESS-THAN WITH CIRCLE INSIDE
2A7A; 2A79 # GREATER-THAN WITH CIRCLE INSIDE
2A7B; 2A7C # [BEST FIT] LESS-THAN WITH QUESTION MARK ABOVE
2A7C; 2A7B # [BEST FIT] GREATER-THAN WITH QUESTION MARK ABOVE
2A7D; 2A7E # LESS-THAN OR SLANTED EQUAL TO
2A7E; 2A7D # GREATER-THAN OR SLANTED EQUAL TO
2A7F; 2A80 # LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
2A80; 2A7F # GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
2A81; 2A82 # LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
2A82; 2A81 # GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
2A83; 2A84 # LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT
2A84; 2A83 # GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT
2A85; 2A86 # [BEST FIT] LESS-THAN OR APPROXIMATE
2A86; 2A85 # [BEST FIT] GREATER-THAN OR APPROXIMATE
2A87; 2A88 # [BEST FIT] LESS-THAN AND SINGLE-LINE NOT EQUAL TO
2A88; 2A87 # [BEST FIT] GREATER-THAN AND SINGLE-LINE NOT EQUAL TO
2A89; 2A8A # [BEST FIT] LESS-THAN AND NOT APPROXIMATE
2A8A; 2A89 # [BEST FIT] GREATER-THAN AND NOT APPROXIMATE
2A8B; 2A8C # LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN
2A8C; 2A8B # GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN
2A8D; 2A8E # [BEST FIT] LESS-THAN ABOVE SIMILAR OR EQUAL
2A8E; 2A8D # [BEST FIT] GREATER-THAN ABOVE SIMILAR OR EQUAL
2A8F; 2A90 # [BEST FIT] LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN
2A90; 2A8F # [BEST FIT] GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN
2A91; 2A92 # LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL
2A92; 2A91 # GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL
2A93; 2A94 # LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL
2A94; 2A93 # GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL
2A95; 2A96 # SLANTED EQUAL TO OR LESS-THAN
2A96; 2A95 # SLANTED EQUAL TO OR GREATER-THAN
2A97; 2A98 # SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE
2A98; 2A97 # SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE
2A99; 2A9A # DOUBLE-LINE EQUAL TO OR LESS-THAN
2A9A; 2A99 # DOUBLE-LINE EQUAL TO OR GREATER-THAN
2A9B; 2A9C # DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN
2A9C; 2A9B # DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN
2A9D; 2A9E # [BEST FIT] SIMILAR OR LESS-THAN
2A9E; 2A9D # [BEST FIT] SIMILAR OR GREATER-THAN
2A9F; 2AA0 # [BEST FIT] SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN
2AA0; 2A9F # [BEST FIT] SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN
2AA1; 2AA2 # DOUBLE NESTED LESS-THAN
2AA2; 2AA1 # DOUBLE NESTED GREATER-THAN
2AA6; 2AA7 # LESS-THAN CLOSED BY CURVE
2AA7; 2AA6 # GREATER-THAN CLOSED BY CURVE
2AA8; 2AA9 # LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
2AA9; 2AA8 # GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
2AAA; 2AAB # SMALLER THAN
2AAB; 2AAA # LARGER THAN
2AAC; 2AAD # SMALLER THAN OR EQUAL TO
2AAD; 2AAC # LARGER THAN OR EQUAL TO
2AAF; 2AB0 # PRECEDES ABOVE SINGLE-LINE EQUALS SIGN
2AB0; 2AAF # SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN
2AB1; 2AB2 # [BEST FIT] PRECEDES ABOVE SINGLE-LINE NOT EQUAL TO
2AB2; 2AB1 # [BEST FIT] SUCCEEDS ABOVE SINGLE-LINE NOT EQUAL TO
2AB3; 2AB4 # PRECEDES ABOVE EQUALS SIGN
2AB4; 2AB3 # SUCCEEDS ABOVE EQUALS SIGN
2AB5; 2AB6 # [BEST FIT] PRECEDES ABOVE NOT EQUAL TO
2AB6; 2AB5 # [BEST FIT] SUCCEEDS ABOVE NOT EQUAL TO
2AB7; 2AB8 # [BEST FIT] PRECEDES ABOVE ALMOST EQUAL TO
2AB8; 2AB7 # [BEST FIT] SUCCEEDS ABOVE ALMOST EQUAL TO
2AB9; 2ABA # [BEST FIT] PRECEDES ABOVE NOT ALMOST EQUAL TO
2ABA; 2AB9 # [BEST FIT] SUCCEEDS ABOVE NOT ALMOST EQUAL TO
2ABB; 2ABC # DOUBLE PRECEDES
2ABC; 2ABB # DOUBLE SUCCEEDS
2ABD; 2ABE # SUBSET WITH DOT
2ABE; 2ABD # SUPERSET WITH DOT
2ABF; 2AC0 # SUBSET WITH PLUS SIGN BELOW
2AC0; 2ABF # SUPERSET WITH PLUS SIGN BELOW
2AC1; 2AC2 # SUBSET WITH MULTIPLICATION SIGN BELOW
2AC2; 2AC1 # SUPERSET WITH MULTIPLICATION SIGN BELOW
2AC3; 2AC4 # SUBSET OF OR EQUAL TO WITH DOT ABOVE
2AC4; 2AC3 # SUPERSET OF OR EQUAL TO WITH DOT ABOVE
2AC5; 2AC6 # SUBSET OF ABOVE EQUALS SIGN
2AC6; 2AC5 # SUPERSET OF ABOVE EQUALS SIGN
2AC7; 2AC8 # [BEST FIT] SUBSET OF ABOVE TILDE OPERATOR
2AC8; 2AC7 # [BEST FIT] SUPERSET OF ABOVE TILDE OPERATOR
2AC9; 2ACA # [BEST FIT] SUBSET OF ABOVE ALMOST EQUAL TO
2ACA; 2AC9 # [BEST FIT] SUPERSET OF ABOVE ALMOST EQUAL TO
2ACB; 2ACC # [BEST FIT] SUBSET OF ABOVE NOT EQUAL TO
2ACC; 2ACB # [BEST FIT] SUPERSET OF ABOVE NOT EQUAL TO
2ACD; 2ACE # SQUARE LEFT OPEN BOX OPERATOR
2ACE; 2ACD # SQUARE RIGHT OPEN BOX OPERATOR
2ACF; 2AD0 # CLOSED SUBSET
2AD0; 2ACF # CLOSED SUPERSET
2AD1; 2AD2 # CLOSED SUBSET OR EQUAL TO
2AD2; 2AD1 # CLOSED SUPERSET OR EQUAL TO
2AD3; 2AD4 # SUBSET ABOVE SUPERSET
2AD4; 2AD3 # SUPERSET ABOVE SUBSET
2AD5; 2AD6 # SUBSET ABOVE SUBSET
2AD6; 2AD5 # SUPERSET ABOVE SUPERSET
2ADE; 22A6 # SHORT LEFT TACK
2AE3; 22A9 # DOUBLE VERTICAL BAR LEFT TURNSTILE
2AE4; 22A8 # VERTICAL BAR DOUBLE LEFT TURNSTILE
2AE5; 22AB # DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE
2AEC; 2AED # DOUBLE STROKE NOT SIGN
2AED; 2AEC # REVERSED DOUBLE STROKE NOT SIGN
2AEE; 2224 # DOES NOT DIVIDE WITH REVERSED NEGATION SLASH
2AF7; 2AF8 # TRIPLE NESTED LESS-THAN
2AF8; 2AF7 # TRIPLE NESTED GREATER-THAN
2AF9; 2AFA # DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO
2AFA; 2AF9 # DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO
2BFE; 221F # REVERSED RIGHT ANGLE
2E02; 2E03 # LEFT SUBSTITUTION BRACKET
2E03; 2E02 # RIGHT SUBSTITUTION BRACKET
2E04; 2E05 # LEFT DOTTED SUBSTITUTION BRACKET
2E05; 2E04 # RIGHT DOTTED SUBSTITUTION BRACKET
2E09; 2E0A # LEFT TRANSPOSITION BRACKET
2E0A; 2E09 # RIGHT TRANSPOSITION BRACKET
2E0C; 2E0D # LEFT RAISED OMISSION BRACKET
2E0D; 2E0C # RIGHT RAISED OMISSION BRACKET
2E1C; 2E1D # LEFT LOW PARAPHRASE BRACKET
2E1D; 2E1C # RIGHT LOW PARAPHRASE BRACKET
2E20; 2E21 # LEFT VERTICAL BAR WITH QUILL
2E21; 2E20 # RIGHT VERTICAL BAR WITH QUILL
2E22; 2E23 # TOP LEFT HALF BRACKET
2E23; 2E22 # TOP RIGHT HALF BRACKET
2E24; 2E25 # BOTTOM LEFT HALF BRACKET
2E25; 2E24 # BOTTOM RIGHT HALF BRACKET
2E26; 2E27 # LEFT SIDEWAYS U BRACKET
2E27; 2E26 # RIGHT SIDEWAYS U BRACKET
2E28; 2E29 # LEFT DOUBLE PARENTHESIS
2E29; 2E28 # RIGHT DOUBLE PARENTHESIS
3008; 3009 # LEFT ANGLE BRACKET
3009; 3008 # RIGHT ANGLE BRACKET
300A; 300B # LEFT DOUBLE ANGLE BRACKET
300B; 300A # RIGHT DOUBLE ANGLE BRACKET
300C; 300D # [BEST FIT] LEFT CORNER BRACKET
300D; 300C # [BEST FIT] RIGHT CORNER BRACKET
300E; 300F # [BEST FIT] LEFT WHITE CORNER BRACKET
300F; 300E # [BEST FIT] RIGHT WHITE CORNER BRACKET
3010; 3011 # LEFT BLACK LENTICULAR BRACKET
3011; 3010 # RIGHT BLACK LENTICULAR BRACKET
3014; 3015 # LEFT TORTOISE SHELL BRACKET
3015; 3014 # RIGHT TORTOISE SHELL BRACKET
3016; 3017 # LEFT WHITE LENTICULAR BRACKET
3017; 3016 # RIGHT WHITE LENTICULAR BRACKET
3018; 3019 # LEFT WHITE TORTOISE SHELL BRACKET
3019; 3018 # RIGHT WHITE TORTOISE SHELL BRACKET
301A; 301B # LEFT WHITE SQUARE BRACKET
301B; 301A # RIGHT WHITE SQUARE BRACKET
FE59; FE5A # SMALL LEFT PARENTHESIS
FE5A; FE59 # SMALL RIGHT PARENTHESIS
FE5B; FE5C # SMALL LEFT CURLY BRACKET
FE5C; FE5B # SMALL RIGHT CURLY BRACKET
FE5D; FE5E # SMALL LEFT TORTOISE SHELL BRACKET
FE5E; FE5D # SMALL RIGHT TORTOISE SHELL BRACKET
FE64; FE65 # SMALL LESS-THAN SIGN
FE65; FE64 # SMALL GREATER-THAN SIGN
FF08; FF09 # FULLWIDTH LEFT PARENTHESIS
FF09; FF08 # FULLWIDTH RIGHT PARENTHESIS
FF1C; FF1E # FULLWIDTH LESS-THAN SIGN
FF1E; FF1C # FULLWIDTH GREATER-THAN SIGN
FF3B; FF3D # FULLWIDTH LEFT SQUARE BRACKET
FF3D; FF3B # FULLWIDTH RIGHT SQUARE BRACKET
FF5B; FF5D # FULLWIDTH LEFT CURLY BRACKET
FF5D; FF5B # FULLWIDTH RIGHT CURLY BRACKET
FF5F; FF60 # FULLWIDTH LEFT WHITE PARENTHESIS
FF60; FF5F # FULLWIDTH RIGHT WHITE PARENTHESIS
FF62; FF63 # [BEST FIT] HALFWIDTH LEFT CORNER BRACKET
FF63; FF62 # [BEST FIT] HALFWIDTH RIGHT CORNER BRACKET
# The following characters have no appropriate mirroring character.
# For these characters it is up to the rendering system
# to provide mirrored glyphs.
# 2140; DOUBLE-STRUCK N-ARY SUMMATION
# 2201; COMPLEMENT
# 2202; PARTIAL DIFFERENTIAL
# 2203; THERE EXISTS
# 2204; THERE DOES NOT EXIST
# 2211; N-ARY SUMMATION
# 2216; SET MINUS
# 221A; SQUARE ROOT
# 221B; CUBE ROOT
# 221C; FOURTH ROOT
# 221D; PROPORTIONAL TO
# 2226; NOT PARALLEL TO
# 222B; INTEGRAL
# 222C; DOUBLE INTEGRAL
# 222D; TRIPLE INTEGRAL
# 222E; CONTOUR INTEGRAL
# 222F; SURFACE INTEGRAL
# 2230; VOLUME INTEGRAL
# 2231; CLOCKWISE INTEGRAL
# 2232; CLOCKWISE CONTOUR INTEGRAL
# 2233; ANTICLOCKWISE CONTOUR INTEGRAL
# 2239; EXCESS
# 223B; HOMOTHETIC
# 223E; INVERTED LAZY S
# 223F; SINE WAVE
# 2240; WREATH PRODUCT
# 2241; NOT TILDE
# 2242; MINUS TILDE
# 2244; NOT ASYMPTOTICALLY EQUAL TO
# 2246; APPROXIMATELY BUT NOT ACTUALLY EQUAL TO
# 2247; NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
# 2248; ALMOST EQUAL TO
# 2249; NOT ALMOST EQUAL TO
# 224A; ALMOST EQUAL OR EQUAL TO
# 224B; TRIPLE TILDE
# 225F; QUESTIONED EQUAL TO
# 2260; NOT EQUAL TO
# 2262; NOT IDENTICAL TO
# 228C; MULTISET
# 22A7; MODELS
# 22AA; TRIPLE VERTICAL BAR RIGHT TURNSTILE
# 22AC; DOES NOT PROVE
# 22AD; NOT TRUE
# 22AE; DOES NOT FORCE
# 22AF; NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
# 22BE; RIGHT ANGLE WITH ARC
# 22BF; RIGHT TRIANGLE
# 22F5; ELEMENT OF WITH DOT ABOVE
# 22F8; ELEMENT OF WITH UNDERBAR
# 22F9; ELEMENT OF WITH TWO HORIZONTAL STROKES
# 22FF; Z NOTATION BAG MEMBERSHIP
# 2320; TOP HALF INTEGRAL
# 2321; BOTTOM HALF INTEGRAL
# 27C0; THREE DIMENSIONAL ANGLE
# 27CC; LONG DIVISION
# 27D3; LOWER RIGHT CORNER WITH DOT
# 27D4; UPPER LEFT CORNER WITH DOT
# 299C; RIGHT ANGLE VARIANT WITH SQUARE
# 299D; MEASURED RIGHT ANGLE WITH DOT
# 299E; ANGLE WITH S INSIDE
# 299F; ACUTE ANGLE
# 29A2; TURNED ANGLE
# 29A6; OBLIQUE ANGLE OPENING UP
# 29A7; OBLIQUE ANGLE OPENING DOWN
# 29C2; CIRCLE WITH SMALL CIRCLE TO THE RIGHT
# 29C3; CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT
# 29C9; TWO JOINED SQUARES
# 29CE; RIGHT TRIANGLE ABOVE LEFT TRIANGLE
# 29DC; INCOMPLETE INFINITY
# 29E1; INCREASES AS
# 29E3; EQUALS SIGN AND SLANTED PARALLEL
# 29E4; EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE
# 29E5; IDENTICAL TO AND SLANTED PARALLEL
# 29F4; RULE-DELAYED
# 29F6; SOLIDUS WITH OVERBAR
# 29F7; REVERSE SOLIDUS WITH HORIZONTAL STROKE
# 2A0A; MODULO TWO SUM
# 2A0B; SUMMATION WITH INTEGRAL
# 2A0C; QUADRUPLE INTEGRAL OPERATOR
# 2A0D; FINITE PART INTEGRAL
# 2A0E; INTEGRAL WITH DOUBLE STROKE
# 2A0F; INTEGRAL AVERAGE WITH SLASH
# 2A10; CIRCULATION FUNCTION
# 2A11; ANTICLOCKWISE INTEGRATION
# 2A12; LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE
# 2A13; LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE
# 2A14; LINE INTEGRATION NOT INCLUDING THE POLE
# 2A15; INTEGRAL AROUND A POINT OPERATOR
# 2A16; QUATERNION INTEGRAL OPERATOR
# 2A17; INTEGRAL WITH LEFTWARDS ARROW WITH HOOK
# 2A18; INTEGRAL WITH TIMES SIGN
# 2A19; INTEGRAL WITH INTERSECTION
# 2A1A; INTEGRAL WITH UNION
# 2A1B; INTEGRAL WITH OVERBAR
# 2A1C; INTEGRAL WITH UNDERBAR
# 2A1E; LARGE LEFT TRIANGLE OPERATOR
# 2A1F; Z NOTATION SCHEMA COMPOSITION
# 2A20; Z NOTATION SCHEMA PIPING
# 2A21; Z NOTATION SCHEMA PROJECTION
# 2A24; PLUS SIGN WITH TILDE ABOVE
# 2A26; PLUS SIGN WITH TILDE BELOW
# 2A29; MINUS SIGN WITH COMMA ABOVE
# 2A3E; Z NOTATION RELATIONAL COMPOSITION
# 2A57; SLOPING LARGE OR
# 2A58; SLOPING LARGE AND
# 2A6A; TILDE OPERATOR WITH DOT ABOVE
# 2A6B; TILDE OPERATOR WITH RISING DOTS
# 2A6C; SIMILAR MINUS SIMILAR
# 2A6D; CONGRUENT WITH DOT ABOVE
# 2A6F; ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT
# 2A70; APPROXIMATELY EQUAL OR EQUAL TO
# 2A73; EQUALS SIGN ABOVE TILDE OPERATOR
# 2A74; DOUBLE COLON EQUAL
# 2AA3; DOUBLE NESTED LESS-THAN WITH UNDERBAR
# 2ADC; FORKING
# 2AE2; VERTICAL BAR TRIPLE RIGHT TURNSTILE
# 2AE6; LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL
# 2AF3; PARALLEL WITH TILDE OPERATOR
# 2AFB; TRIPLE SOLIDUS BINARY RELATION
# 2AFD; DOUBLE SOLIDUS OPERATOR
# 1D6DB; MATHEMATICAL BOLD PARTIAL DIFFERENTIAL
# 1D715; MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL
# 1D74F; MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL
# 1D789; MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL
# 1D7C3; MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL
# EOF

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="11"/>
<General>
<Flags>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasTitleStatement Value="False"/>
<MainUnitHasScaledStatement Value="False"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<MainUnit Value="0"/>
<Title Value="parseunicodeclasses"/>
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
</General>
<BuildModes Count="1">
<Item1 Name="Default" Default="True"/>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
</PublishOptions>
<RunParams>
<FormatVersion Value="2"/>
<Modes Count="1">
<Mode0 Name="default"/>
</Modes>
</RunParams>
<RequiredPackages Count="1">
<Item1>
<PackageName Value="LazUtils"/>
</Item1>
</RequiredPackages>
<Units Count="1">
<Unit0>
<Filename Value="parseunicodeclasses.lpr"/>
<IsPartOfProject Value="True"/>
</Unit0>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<Target>
<Filename Value="parseunicodeclasses"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<CodeGeneration>
<Optimizations>
<OptimizationLevel Value="0"/>
</Optimizations>
</CodeGeneration>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">
<Item1>
<Name Value="EAbort"/>
</Item1>
<Item2>
<Name Value="ECodetoolError"/>
</Item2>
<Item3>
<Name Value="EFOpenError"/>
</Item3>
</Exceptions>
</Debugging>
</CONFIG>

View File

@@ -0,0 +1,771 @@
// SPDX-License-Identifier: LGPL-3.0-only (modified to allow linking)
program parseunicodeclasses;
uses Classes, sysutils, fgl, LazUTF8;
type
TIntegerList = specialize TFPGList<Integer>;
var
UnicodeData: array of record
Code: LongInt;
Name, Category: string;
CombiningClass: byte;
BidiClass, Decomposition: string;
Mirrored: boolean;
OldName: string;
end;
UnicodeCount: integer;
procedure LoadUnicodeData;
var
lines, cells: TStringList;
i: Integer;
begin
lines := TStringList.Create;
lines.LoadFromFile('UnicodeData.txt');
setlength(UnicodeData, lines.Count);
UnicodeCount := 0;
cells := TStringList.Create;
cells.Delimiter := ';';
cells.QuoteChar := '"';
cells.StrictDelimiter := true;
for i := 0 to lines.Count-1 do
begin
cells.DelimitedText := lines[i];
if cells.Count >= 11 then
with UnicodeData[UnicodeCount] do
begin
Code := StrToInt('$'+cells[0]);
Name := cells[1];
Category := cells[2];
CombiningClass:= StrToInt(cells[3]);
BidiClass := cells[4];
Decomposition:= cells[5];
Mirrored := (cells[9] = 'Y');
OldName := cells[10];
inc(UnicodeCount);
end;
end;
SetLength(UnicodeData, unicodeCount);
lines.Free;
end;
function IndexOfUnicode(u: LongInt): integer;
var
low, high, mid: Integer;
begin
low := 0;
high := UnicodeCount-1;
while low < high do
begin
mid := (low+high) div 2;
if u > UnicodeData[mid].Code then
low := mid+1
else
high := mid;
end;
if UnicodeData[low].Code = u then
result := low
else
result := -1;
end;
function GetUnicodeBidiClass(u: LongInt): string;
var
idx: Integer;
begin
idx := IndexOfUnicode(u);
if idx = -1 then
result := ''
else result := UnicodeData[idx].BidiClass;
end;
function GetUnicodeCombiningClass(u: LongInt): byte;
var
idx: Integer;
begin
idx := IndexOfUnicode(u);
if idx = -1 then
result := 0
else result := UnicodeData[idx].CombiningClass;
end;
function UnicodeCharToUTF8(u: LongInt): string;
begin
if u >= 0 then
result := UnicodeToUTF8(cardinal(u))
else
result := '';
end;
function ArrayOfCodeToCase(ACodes: array of integer; AIndent: string): string;
var
codeCount, i: Integer;
bufLines: TStringList;
buf: String;
begin
codeCount := length(ACodes);
if codeCount = 0 then exit('');
bufLines := TStringList.Create;
i := 0;
buf := AIndent+' ';
while i < codeCount do
begin
if i > 0 then buf += ', ';
if length(buf) > 95 then
begin
bufLines.Add(buf);
buf := AIndent+' ';
end;
if (i+2 < codeCount) and (ACodes[i]+1 = ACodes[i+1]) and (ACodes[i+1]+1 = ACodes[i+2]) then
begin
buf += '$'+IntToHex(ACodes[i],2) + '..';
while (i+1 < codeCount) and (ACodes[i]+1 = ACodes[i+1]) do inc(i);
buf += '$'+IntToHex(ACodes[i],2);
end else
buf += '$'+IntToHex(ACodes[i],2);
inc(i);
end;
if trim(buf) <> '' then bufLines.Add(buf);
result := '';
for i := 0 to bufLines.Count-1 do
begin
if i > 0 then result += LineEnding;
result += bufLines[i];
end;
bufLines.Free;
result += ': ';
end;
function ArrayOfCodeToCase(ACodes: TIntegerList; AIndent: string): string;
var a: array of integer;
i: Integer;
begin
setlengtH(a, ACodes.Count);
for i := 0 to high(a) do
a[i] := ACodes[i];
result := ArrayOfCodeToCase(a, AIndent);
end;
procedure GenerateUnicodeFunctions;
const Indent = ' ';
var
tIn, tOut: TextFile;
procedure ParseBidiClasses;
type
TUnicodeBidiClass = (ubcBoundaryNeutral, ubcSegmentSeparator, ubcParagraphSeparator, ubcWhiteSpace, ubcOtherNeutrals,
ubcCommonSeparator, ubcNonSpacingMark,
ubcLeftToRight, ubcEuropeanNumber, ubcEuropeanNumberSeparator, ubcEuropeanNumberTerminator,
ubcRightToLeft, ubcArabicLetter, ubcArabicNumber,
ubcUnknown,
ubcCombiningLeftToRight, //ubcLeftToRight in Mc category
ubcMirroredNeutral); //ubcOtherNeutrals with Mirrored property
procedure IncludeClasses(AClasses: TStrings; AMinCode, AMaxCode: integer);
const
MaxGapsPerClass = 20;
var
codes: array[TUnicodeBidiClass] of TIntegerList;
gaps: array[TUnicodeBidiClass] of integer;
procedure FlushCase(curBidi: TUnicodeBidiClass);
var
caseStr: string;
begin
if codes[curBidi].Count = 0 then exit;
caseStr := ArrayOfCodeToCase(codes[curBidi], Indent);
case curBidi of
ubcCommonSeparator: WriteLn(tOut,caseStr+'result := ubcCommonSeparator;');
ubcLeftToRight: WriteLn(tOut,caseStr+'result := ubcLeftToRight;');
ubcCombiningLeftToRight: WriteLn(tOut,caseStr+'result := ubcCombiningLeftToRight;');
ubcEuropeanNumber: WriteLn(tOut,caseStr+'result := ubcEuropeanNumber;');
ubcEuropeanNumberSeparator: WriteLn(tOut,caseStr+'result := ubcEuropeanNumberSeparator;');
ubcEuropeanNumberTerminator: WriteLn(tOut,caseStr+'result := ubcEuropeanNumberTerminator;');
ubcRightToLeft: WriteLn(tOut,caseStr+'result := ubcRightToLeft;');
ubcArabicLetter: WriteLn(tOut,caseStr+'result := ubcArabicLetter;');
ubcArabicNumber: WriteLn(tOut,caseStr+'result := ubcArabicNumber;');
ubcNonSpacingMark: WriteLn(tOut,caseStr+'result := ubcNonSpacingMark;');
ubcBoundaryNeutral: WriteLn(tOut,caseStr+'result := ubcBoundaryNeutral;');
ubcParagraphSeparator: WriteLn(tOut,caseStr+'result := ubcParagraphSeparator;');
ubcSegmentSeparator: WriteLn(tOut,caseStr+'result := ubcSegmentSeparator;');
ubcWhiteSpace: WriteLn(tOut,caseStr+'result := ubcWhiteSpace;');
ubcMirroredNeutral: WriteLn(tOut,caseStr+'result := ubcMirroredNeutral;');
ubcOtherNeutrals: WriteLn(tOut,caseStr+'result := ubcOtherNeutrals;');
else raise exception.Create('Unknown bidi class');
end;
codes[curBidi].Clear;
gaps[curBidi] := 0;
end;
var
newBidi: TUnicodeBidiClass;
curCode: LongInt;
i: integer;
begin
write(' ', AClasses.DelimitedText);
for newBidi := low(TUnicodeBidiClass) to high(TUnicodeBidiClass) do
begin
codes[newBidi] := TIntegerList.Create;
gaps[newBidi] := 0;
end;
for i := 0 to UnicodeCount-1 do
begin
case UnicodeData[i].BidiClass of
'CS': newBidi := ubcCommonSeparator;
'L': newBidi := ubcLeftToRight;
'EN': newBidi := ubcEuropeanNumber;
'ES': newBidi := ubcEuropeanNumberSeparator;
'ET': newBidi := ubcEuropeanNumberTerminator;
'R': newBidi := ubcRightToLeft;
'AL': newBidi := ubcArabicLetter;
'AN': newBidi := ubcArabicNumber;
'NSM': newBidi := ubcNonSpacingMark;
'BN': newBidi := ubcBoundaryNeutral;
'B': newBidi := ubcParagraphSeparator;
'S': newBidi := ubcSegmentSeparator;
'WS': newBidi := ubcWhiteSpace;
'ON': newBidi := ubcOtherNeutrals;
else continue;
end;
if (newBidi = ubcLeftToRight) and (UnicodeData[i].Category = 'Mc') then newBidi := ubcCombiningLeftToRight
else if (newBidi = ubcOtherNeutrals) and UnicodeData[i].Mirrored then newBidi := ubcMirroredNeutral;
if AClasses.IndexOf(UnicodeData[i].BidiClass)<>-1 then
begin
curCode := UnicodeData[i].Code;
if (curCode >= AMinCode) and (curCode <= AMaxCode) then
begin
if (codes[newBidi].Count > 0) and (codes[newBidi].Last+1 <> curCode) then
inc(gaps[newBidi]);
codes[newBidi].Add(curCode);
if gaps[newBidi] > MaxGapsPerClass then
FlushCase(newBidi);
end;
end;
end;
for newBidi := low(TUnicodeBidiClass) to high(TUnicodeBidiClass) do
begin
FlushCase(newBidi);
codes[newBidi].Free;
end;
end;
var c: TStringList;
procedure Include(AMinCode,AMaxCode: integer);
begin
write('Classes from ',IntToHex(AMinCode,2),' to ',IntToHex(AMaxCode,2),':');
Writeln(tOut,Indent+'case u of');
c.CommaText := 'BN';
IncludeClasses(c, AMinCode,AMaxCode);
c.CommaText := 'S';
IncludeClasses(c, AMinCode,AMaxCode);
c.CommaText := 'B';
IncludeClasses(c, AMinCode,AMaxCode);
c.CommaText := 'WS';
IncludeClasses(c, AMinCode,AMaxCode);
c.CommaText := 'L,CL,R,AL';
IncludeClasses(c, AMinCode,AMaxCode);
c.CommaText := 'EN';
IncludeClasses(c, AMinCode,AMaxCode);
c.CommaText := 'ES';
IncludeClasses(c, AMinCode,AMaxCode);
c.CommaText := 'ET';
IncludeClasses(c, AMinCode,AMaxCode);
c.CommaText := 'AN';
IncludeClasses(c, AMinCode,AMaxCode);
c.CommaText := 'CS,NSM';
IncludeClasses(c, AMinCode,AMaxCode);
c.CommaText := 'ON,MN';
IncludeClasses(c, AMinCode,AMaxCode);
writeln(tout,Indent+'else result := ubcUnknown;');
writeln(tout,Indent+'end;');
writeln;
end;
begin
Writeln(tOut,'function GetUnicodeBidiClassEx(u: LongWord): TUnicodeBidiClass;');
Writeln(tOut,'begin');
c := TStringList.Create;
writeln(tOut,' case u of');
writeln(tOut,' $00000..$07FFF:');
writeln(tOut,' case u of');
writeln(tOut,' $00000..$003FF:');
Include($00000, $003FF);
writeln(tOut,' $00400..$007FF:');
Include($00400, $007FF);
writeln(tOut,' $00800..$00BFF:');
Include($00800, $00BFF);
writeln(tOut,' $00C00..$00FFF:');
Include($00C00, $00FFF);
writeln(tOut,' $01000..$017FF:');
Include($01000, $017FF);
writeln(tOut,' $01800..$01FFF:');
Include($01800, $01FFF);
writeln(tOut,' $02000..$02FFF:');
Include($02000, $02FFF);
writeln(tOut,' else');
Include($03000, $07FFF);
writeln(tOut,' end;');
writeln(tOut,' $08000..$0BFFF:');
Include($08000, $0BFFF);
writeln(tOut,' $0C000..$0FFFF:');
Include($0C000, $0FFFF);
writeln(tOut,' else');
writeln(tOut,' case u of');
writeln(tOut,' $10000..$107FF:');
Include($10000, $107FF);
writeln(tOut,' $10800..$10FFF:');
Include($10800, $10FFF);
writeln(tOut,' $11000..$117FF:');
Include($11000, $117FF);
writeln(tOut,' $11800..$17FFF:');
Include($11800, $17FFF);
writeln(tOut,' $18000..$1DFFF:');
Include($18000, $1DFFF);
writeln(tOut,' $1E000..$FFFFF:');
Include($1E000, $FFFFF);
writeln(tOut,' else result := ubcUnknown;');
writeln(tOut,' end');
writeln(tOut,' end');
c.Free;
writeln(tout,'end;');
writeln(tout);
end;
procedure ParseBidiBrackets;
var elem: TStringList;
line: string;
tIn: TextFile;
begin
Writeln(tOut,'function GetUnicodeBracketInfo(u: LongWord): TUnicodeBracketInfo;');
Writeln(tOut,' procedure Bracket(AOpening,AClosing: LongWord);');
Writeln(tOut,' begin');
Writeln(tOut,' result.IsBracket := true;');
Writeln(tOut,' result.OpeningBracket := AOpening;');
Writeln(tOut,' result.ClosingBracket := AClosing;');
Writeln(tOut,' end;');
Writeln(tOut,'begin');
Writeln(tOut,' case u of');
writeln('Parsing bracket data...');
assignfile(tIn, 'BidiBrackets.txt');
reset(tin);
elem := TStringList.Create;
elem.Delimiter := ';';
elem.StrictDelimiter:= true;
while not eof(tin) do
begin
readln(tin, line);
elem.DelimitedText:= line;
if elem.Count >= 3 then
begin
if copy(trim(elem[2]),1,1) = 'o' then
writeln(tOut,' $'+trim(elem[0])+', $'+trim(elem[1])+': Bracket($'+trim(elem[0])+', $'+trim(elem[1])+');');
end;
end;
elem.Free;
closefile(tin);
writeln(tout,' else');
writeln(tout,' begin');
writeln(tout,' result.IsBracket := false;');
writeln(tout,' result.OpeningBracket := 0;');
writeln(tout,' result.ClosingBracket := 0;');
writeln(tout,' end;');
Writeln(tOut,' end;');
Writeln(tOut,'end;');
Writeln(tOut);
end;
procedure ParseArabicLigature;
var
line: string;
cells: TStringList;
chars: TStringList;
u: LongInt;
j: Integer;
procedure AddJoiningType(joinType: string; joinTypeEnum: string; AIndent: string; AMinIndex,AMaxIndex: integer);
var
i,nb: Integer;
charsList: array of integer;
begin
nb := 0;
for i := AMinIndex to AMaxIndex do
if chars.ValueFromIndex[i]=joinType then inc(nb);
if nb = 0 then exit;
setlength(charsList, nb);
nb := 0;
for i := AMinIndex to AMaxIndex do
if chars.ValueFromIndex[i]=joinType then
begin
charsList[nb] := StrToInt('$'+chars.Names[i]);
inc(nb);
end;
writeln(tOut,ArrayOfCodeToCase(charsList, AIndent)+'result := '+joinTypeEnum+';');
end;
procedure AddJoiningTypeRange(AMinIndex,AMaxIndex: integer; AIndent: string; AForceCase: boolean = false);
const MaxGaps = 45;
var
mid, i, gaps, halfGaps: Integer;
begin
gaps := 0;
for i := AMinIndex+1 to AMaxIndex do
if (StrToInt('$'+chars.Names[i])-StrToInt('$'+chars.Names[i-1]) > 1) or
(chars.ValueFromIndex[i] <> chars.ValueFromIndex[i-1]) then inc(gaps);
if (gaps > MaxGaps) and not AForceCase then
begin
halfGaps := 0;
mid := (AMinIndex+AMaxIndex) div 2;
for i := AMinIndex+1 to AMaxIndex do
if (StrToInt('$'+chars.Names[i])-StrToInt('$'+chars.Names[i-1]) > 1) or
(chars.ValueFromIndex[i] <> chars.ValueFromIndex[i-1]) then
begin
inc(halfGaps);
if halfGaps >= gaps shr 1 then
begin
mid := i;
break;
end;
end;
if gaps <= MaxGaps*2.5 then
begin
writeln(tOut,AIndent, 'if u <= $', chars.Names[mid],' then');
AddJoiningTypeRange(AMinIndex, mid, AIndent+' ', true);
writeln(tOut,AIndent, 'else');
AddJoiningTypeRange(mid+1, AMaxIndex, AIndent+' ', true);
end else
begin
writeln(tOut,AIndent, 'if u <= $', chars.Names[mid],' then begin');
AddJoiningTypeRange(AMinIndex, mid, AIndent+' ');
writeln(tOut,AIndent, 'end else begin');
AddJoiningTypeRange(mid+1, AMaxIndex, AIndent+' ');
writeln(tOut,AIndent, 'end');
end;
end else
begin
writeln(tOut,AIndent, 'case u of');
AddJoiningType('T', 'ujtTransparent', AIndent, AMinIndex, AMaxIndex);
AddJoiningType('R', 'ujtRightJoining', AIndent, AMinIndex, AMaxIndex);
AddJoiningType('L', 'ujtLeftJoining', AIndent, AMinIndex, AMaxIndex);
AddJoiningType('D', 'ujtDualJoining', AIndent, AMinIndex, AMaxIndex);
AddJoiningType('C', 'ujtJoinCausing', AIndent, AMinIndex, AMaxIndex);
writeln(tOut,AIndent, 'end');
end;
end;
begin
writeln('Parsing arabic ligature data...');
chars := TStringList.Create;
for j := 0 to UnicodeCount-1 do
begin
if (UnicodeData[j].Category = 'Mn') or (UnicodeData[j].Category = 'Me')
or (UnicodeData[j].Category = 'Cf') then
chars.Values[IntToHex(UnicodeData[j].Code,6)] := 'T';
end;
assignfile(tIn, 'ArabicShaping.txt');
reset(tIn);
cells := TStringList.Create;
cells.Delimiter := ';';
cells.QuoteChar := '"';
cells.StrictDelimiter := true;
while not eof(tIn) do
begin
readln(tIn, line);
if (line = '') or (line[1]='#') then continue;
cells.DelimitedText:= line;
if cells.Count >= 4 then
begin
u := StrToInt('$'+cells[0]);
if trim(cells[2]) = 'U' then
begin
j := chars.IndexOfName(IntToHex(u,6));
if j <> -1 then
chars.Delete(j);
end
else
chars.Values[IntToHex(u,6)] := trim(cells[2]);
end;
end;
closefile(tIn);
cells.Free;
chars.Sort;
Writeln(tOut,'function GetUnicodeJoiningType(u: LongWord): TUnicodeJoiningType;');
Writeln(tOut,'begin');
writeln(tOut,' result := ujtNonJoining;');
AddJoiningTypeRange(0, chars.Count-1, ' ');
chars.Free;
Writeln(tOut,'end;');
Writeln(tOut);
end;
procedure ParseCombiningClasses;
const
CombineLeftOnly = '093F,094E,' + {DEVANAGARI}
'09BF,09C7,09C8,' + {BENGALI}
'0A3F,' + {GURMUKHI}
'0ABF,' + {GUJARATI}
'0B47,0B48,0B4B,0B4C,' + {ORIYA}
'0BC6,0BC7,0BC8,' + {TAMIL}
'0D46,0D47,0D48,' + {MALAYALAM}
'0DD9,0DDA,0DDB,0DDC,0DDD,0DDE,' + {SINHALA}
'1031,103C,1084,' + {MYANMAR}
'17BE,17C1,17C2,17C3,' + {KHMER}
'1A19,' + {BUGINESE}
'1B3E,1B3F,' + {BALINESE}
'302E,302F,' + {HANGUL}
'A9BA,A9BB,A9BF,' + {JAVANESE}
'AA2F,AA30,AA34,'; {CHAM}
CombineLeftAndRight = '09CB,09CC,' + {BENGALI}
'0BCA,0BCB,0BCC,' + {TAMIL}
'0D4A,0D4B,0D4C,' + {MALAYALAM}
'17BF,17C0,17C4,17C5,' + {KHMER}
'1B3D,1B40,1B41,'; {BALINESE}
var
i: Integer;
infos: TStringList;
u: LongInt;
c: byte;
s: String;
procedure FlushLine;
begin
writeln(tOut, s);
s := ' ';
end;
begin
infos := TStringList.Create;
for i := 0 to UnicodeCount-1 do
begin
u := UnicodeData[i].Code;
if (UnicodeData[i].BidiClass = 'NSM') or
(UnicodeData[i].Category = 'Mc') then
begin
c := UnicodeData[i].CombiningClass;
if (c = 0) and (UnicodeData[i].Category = 'Mc') then
begin
if pos(IntToHex(u,4)+',', CombineLeftOnly) <> 0 then c := 208
else if pos(IntToHex(u,4)+',', CombineLeftAndRight) <> 0 then c := 0
else c := 210;
end;
infos.Add('(u:$'+IntToHex(u,2)+'; c:'+IntToStr(c)+')');
end;
end;
writeln(tOut,'type');
writeln(tOut,' TUnicodeCombiningInfo = record');
writeln(tOut,' u: LongWord;');
writeln(tOut,' c: Byte;');
writeln(tOut,' end;');
writeln(tOut,'const');
writeln(tOut,' UnicodeCombiningInfos: array[0..',infos.count-1,'] of TUnicodeCombiningInfo =');
s := ' (';
for i := 0 to infos.Count-1 do
begin
if length(s) + length(infos[i]) + 2 > 80 then FlushLine;
AppendStr(s, ' ' + infos[i]);
if i < infos.Count-1 then AppendStr(s, ',');
end;
if s <> ' ' then FlushLine;
writeln(tOut,' );');
writeln(tOut);
infos.Free;
end;
begin
AssignFile(tOut, 'generatedunicode.inc');
Rewrite(tOut);
writeln(tOut,'{ This file is generated by dev/parseunicode/parseunicodeclasses program }');
Writeln(tOut);
ParseBidiClasses;
ParseBidiBrackets;
ParseArabicLigature;
ParseCombiningClasses;
CloseFile(tOut);
end;
function ListCompareBinary(List: TStringList; Index1, Index2: Integer): Integer;
begin
result := CompareStr(List[Index1], List[Index2]);
end;
procedure ParseUTF8Decomposition;
type TDecompositionKind = string;
const dMultichar = 'arNone';
dInitial = 'arInitial';
dMedial = 'arMedial';
dFinal = 'arFinal';
dIsolated = 'arIsolated';
const UTF8_ARABIC_ALEPH = 'ا';
UTF8_ARABIC_ALEPH_HAMZA_BELOW = 'إ';
UTF8_ARABIC_ALEPH_HAMZA_ABOVE = 'أ';
UTF8_ARABIC_ALEPH_MADDA_ABOVE = 'آ';
UTF8_ARABIC_LAM = 'ل';
var tOut: TextFile;
decomposed, kind, decomposedUTF8, s: string;
decomposedFirstChar: LongInt;
mergedU,nextU, fallbackU: LongInt;
posClose, posSpace: SizeInt;
hasNSM, isLa: Boolean;
correspList: TStringList;
kerningFallback: TStringList;
i, decomposedLen, j: Integer;
typedKind: TDecompositionKind;
hasMarkLeft, hasMarkRight: boolean;
function RemoveUptoTab(AText: string): string;
var
idxTab: SizeInt;
begin
idxTab := pos(#9, AText);
result := copy(AText, idxTab+1, length(AText)-idxTab);
end;
begin
writeln('Parsing decomposition data...');
correspList := TStringList.Create;
kerningFallback := TStringList.Create;
for j := 0 to UnicodeCount-1 do
begin
mergedU := UnicodeData[j].Code;
if UnicodeData[j].BidiClass = 'NSM' then continue;
decomposed := UnicodeData[j].Decomposition;
if decomposed = '' then continue;
typedKind := dMultichar;
if decomposed[1] = '<' then
begin
posClose := pos('>', decomposed);
if posClose = 0 then continue;
kind := copy(decomposed,1,posClose);
delete(decomposed, 1, posClose);
if kind = '<initial>' then typedKind := dInitial else
if kind = '<medial>' then typedKind := dMedial else
if kind = '<final>' then typedKind := dFinal else
if kind = '<isolated>' then typedKind := dIsolated else
if (kind = '<compat>') and (mergedU >= $FB00) and (mergedU <= $FB04) then
typedKind := dMultichar
else
continue;
decomposed := trim(decomposed);
end;
decomposedUTF8 := '';
decomposedLen := 0;
decomposedFirstChar:= 0;
hasMarkLeft := false;
hasMarkRight := false;
hasNSM := false;
while decomposed <> '' do
begin
posSpace := pos(' ',decomposed);
if posSpace = 0 then posSpace := length(decomposed)+1;
nextU := strToInt('$'+copy(decomposed,1,posSpace-1));
if GetUnicodeBidiClass(nextU) = 'NSM' then hasNSM := true;
case GetUnicodeCombiningClass(nextU) of
200,208,212,218,224,228: hasMarkLeft := true;
204,210,216,222,226,232: hasMarkRight := true;
end;
if decomposedLen = 0 then decomposedFirstChar:= nextU;
AppendStr(decomposedUTF8, UnicodeCharToUTF8(nextU));
delete(decomposed, 1, posSpace);
inc(decomposedLen);
end;
isLa := (decomposedUTF8 = UTF8_ARABIC_LAM+UTF8_ARABIC_ALEPH) or
(decomposedUTF8 = UTF8_ARABIC_LAM+UTF8_ARABIC_ALEPH_HAMZA_BELOW) or
(decomposedUTF8 = UTF8_ARABIC_LAM+UTF8_ARABIC_ALEPH_HAMZA_ABOVE) or
(decomposedUTF8 = UTF8_ARABIC_LAM+UTF8_ARABIC_ALEPH_MADDA_ABOVE);
if ((typedKind = dMultichar) and (decomposedLen > 1)
and (hasNSM or (copy(decomposedUTF8,1,1) = 'f'))) or
((typedKind <> dMultichar) and ((decomposedLen = 1) or isLa)) then
correspList.Add(decomposedUTF8+#9+'('+
'de:''' + decomposedUTF8 + '''; ' +
're:''' + UnicodeCharToUTF8(mergedU) + '''; ' +
'join:' + typedKind +
')');
if (typedKind = dMultichar) and (decomposedUTF8 <> '') and not hasMarkLeft and not hasMarkRight and
((decomposedUTF8[1] in ['A'..'Z']) or (copy(decomposedUTF8,1,length('Æ')) = 'Æ') or
(copy(decomposedUTF8,1,length('Ç')) = 'Ç') or
(copy(decomposedUTF8,1,length('Г')) = 'Г') or
(copy(decomposedUTF8,1,length('Ѵ')) = 'Ѵ') or
(copy(decomposedUTF8,1,length('Ω')) = 'Ω') or
(copy(decomposedUTF8,1,length('Ө')) = 'Ө')) then
begin
fallbackU := decomposedFirstChar;
if fallbackU <> 32 then
kerningFallback.Add('(u:$' + inttohex(mergedU,2)+'; fb:$'+ inttohex(fallbackU,2)+')');
end;
end;
AssignFile(tOut, 'generatedutf8.inc');
Rewrite(tOut);
writeln(tOut,'{ This file is generated by dev/parseunicode/parseunicodeclasses program }');
writeln(tOut, 'type');
writeln(tOut, ' TArabicJoin = (arNone, arInitial, arMedial, arFinal, arIsolated);');
writeln(tOut, ' TUTF8Decomposition = record');
writeln(tOut, ' de, re: string; //decomposed, recomposed UTF8');
writeln(tOut, ' join: TArabicJoin;');
writeln(tOut, ' end;');
writeln(tOut, 'const');
writeln(tOut, ' UTF8Decomposition : array[0..', correspList.Count-1, '] of TUTF8Decomposition = (');
correspList.CustomSort(@ListCompareBinary);
for i := 0 to correspList.Count-1 do
if i <> correspList.Count-1 then
writeln(tOut, ' ', RemoveUptoTab(correspList[i]), ',')
else
writeln(tOut, ' ', RemoveUptoTab(correspList[i]));
correspList.Free;
writeln(tOut, ' );');
writeln(tout);
CloseFile(tOut);
AssignFile(tOut, 'generatedkerningfallback.inc');
Rewrite(tOut);
writeln(tOut,'{ This file is generated by dev/parseunicode/parseunicodeclasses program }');
writeln(tOut, 'type');
writeln(tOut, ' TKerningFallbackInfo = record');
writeln(tOut, ' u: integer; //composed charcode');
writeln(tOut, ' fb: integer; //fallback code');
writeln(tOut, ' end;');
writeln(tOut, 'const');
writeln(tOut, ' KerningFallbackInfo : array[0..', kerningFallback.Count-1, '] of TKerningFallbackInfo = (');
s := '';
for i := 0 to kerningFallback.Count-1 do
begin
if i <> kerningFallback.Count-1 then
AppendStr(s, kerningFallback[i] + ', ')
else
AppendStr(s, kerningFallback[i]);
if length(s) > 70 then
begin
writeln(tOut, ' ', s);
s := '';
end;
end;
if s <> '' then
writeln(tOut, ' ', s);
writeln(tOut, ' );');
writeln(tout);
kerningFallback.Free;
CloseFile(tOut);
end;
begin
LoadUnicodeData;
GenerateUnicodeFunctions;
ParseUTF8Decomposition;
writeln('Done.');
end.

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="10"/>
<General>
<Flags>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasTitleStatement Value="False"/>
<MainUnitHasScaledStatement Value="False"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<MainUnit Value="0"/>
<Title Value="testunicodealgo"/>
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
</General>
<BuildModes Count="1">
<Item1 Name="Default" Default="True"/>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
</PublishOptions>
<RunParams>
<local>
<FormatVersion Value="1"/>
</local>
</RunParams>
<RequiredPackages Count="1">
<Item1>
<PackageName Value="BGRABitmapPack4NoGUI"/>
</Item1>
</RequiredPackages>
<Units Count="1">
<Unit0>
<Filename Value="testunicodealgo.lpr"/>
<IsPartOfProject Value="True"/>
</Unit0>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<Target>
<Filename Value="testunicodealgo"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<CodeGeneration>
<Optimizations>
<OptimizationLevel Value="0"/>
</Optimizations>
</CodeGeneration>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">
<Item1>
<Name Value="EAbort"/>
</Item1>
<Item2>
<Name Value="ECodetoolError"/>
</Item2>
<Item3>
<Name Value="EFOpenError"/>
</Item3>
</Exceptions>
</Debugging>
</CONFIG>

View File

@@ -0,0 +1,85 @@
// SPDX-License-Identifier: LGPL-3.0-only (modified to allow linking)
program testunicodealgo;
uses BGRAUTF8, Classes, sysUtils;
var
tIn,tOut: TextFile;
testDef: string;
testElem, unicodeChars: TStringList;
testText: string;
posHash, i: integer;
a: TBidiUTF8Array;
levelStr, orderStr: string;
failCount, successCount: integer;
o: TUnicodeDisplayOrder;
begin
assignfile(tIn, 'BidiCharacterTest.txt');
reset(tIn);
assignfile(tOut, 'BidiCharacterTest.result');
rewrite(tOut);
testElem := TStringList.Create;
testElem.Delimiter := ';';
testElem.StrictDelimiter:= true;
unicodeChars := TStringList.Create;
unicodeChars.Delimiter := ' ';
unicodeChars.StrictDelimiter := true;
while not eof(tIn) do
begin
readln(tIn, testDef);
posHash := pos('#',testDef);
if posHash <> 0 then testDef := copy(testDef,1,posHash-1);
testElem.DelimitedText := testDef;
if testElem.Count = 5 then
begin
unicodeChars.DelimitedText := testElem[0];
testText := '';
for i := 0 to unicodeChars.Count-1 do
testText += UnicodeCharToUTF8(StrToInt('$'+unicodeChars[i]));
if testText = '' then continue;
//writeln(testDef);
case testElem[1] of
'0': a := AnalyzeBidiUTF8(testText,false);
'1': a := AnalyzeBidiUTF8(testText,true);
'2': a := AnalyzeBidiUTF8(testText);
end;
levelStr := '';
for i := 0 to high(a) do
begin
if i > 0 then levelStr += ' ';
if a[i].BidiInfo.IsRemoved then
levelStr += 'x'
else
levelStr += inttostr(a[i].BidiInfo.BidiLevel);
end;
o := GetUTF8DisplayOrder(a);
orderStr := '';
for i := 0 to high(o) do
begin
if i > 0 then orderStr += ' ';
orderStr += inttostr(o[i]);
end;
if (levelStr <> testElem[3]) or (orderStr <> testElem[4]) then
begin
if levelStr = testElem[3] then
write(tOut,'Success;') else write(tOut,'Fail;');
writeln(tOut, testDef, ';', levelStr, ';', orderStr+';', testText);
inc(failCount);
end else
inc(successCount);
end;
end;
writeln(tOut, failCount, ' fail');
writeln(tOut, successCount, ' success');
closefile(tOut);
closefile(tIn);
end.

View File

@@ -0,0 +1,6 @@
List of directories:
- assistant: script to generate instructions for GPT
- makedoc: contains a program to make documentation from source code
- colorspace: contains a program to generate code for colorspaces
- parseunicode: parse the data provided by unicode and test unicode algorithm
- releaser: program to update BGRABitmap version for release

View File

@@ -0,0 +1,59 @@
// SPDX-License-Identifier: LGPL-3.0-linking-exception
unit ArchiveUrl;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, ReleaserTypes;
type
{ TArchiveUrl }
TArchiveUrl = class(TReleaserObject)
private
FUrl: string;
public
constructor Create(AParameters: TStringList; ALogicDir: string); override;
class function IsUnique: boolean; override;
function GetUrlForVersion(AVersion: TVersion): string;
property Url: string read FUrl;
procedure GetVersions({%H-}AVersionList: TStringList); override;
procedure Save; override;
end;
implementation
{ TArchiveUrl }
constructor TArchiveUrl.Create(AParameters: TStringList; ALogicDir: string);
begin
inherited Create(AParameters, ALogicDir);
ExpectParamCount(1);
FUrl := Param[0];
end;
class function TArchiveUrl.IsUnique: boolean;
begin
Result:= true;
end;
function TArchiveUrl.GetUrlForVersion(AVersion: TVersion): string;
begin
result := ReplaceVariables(FUrl, AVersion);
end;
procedure TArchiveUrl.GetVersions(AVersionList: TStringList);
begin
//nothing
end;
procedure TArchiveUrl.Save;
begin
//nothing
end;
end.

View File

@@ -0,0 +1,10 @@
cd ..\..
manager update_BGRABitmap.json
archive https://github.com/bgrabitmap/bgrabitmap/archive/v$(Version).zip
cd bgrabitmap
package bgrabitmappack.lpk
package bgrabitmappack4fpgui.lpk
package bgrabitmappack4nogui.lpk
package bgrabitmappack4android.lpk
const bgrabitmaptypes.pas BGRABitmapVersion

View File

@@ -0,0 +1,174 @@
// SPDX-License-Identifier: LGPL-3.0-linking-exception
unit ConstFile;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, ReleaserTypes;
type
{ TConstFile }
TConstFile = class(TReleaserObject)
private
FFilename, FConstname: String;
FSourceCode: string;
FChanged: boolean;
procedure AnalyzeVersionLine(ALine: string; out AValueStart,
AValueLength: integer);
public
constructor Create(AParameters: TStringList; ALogicDir: string); override;
destructor Destroy; override;
procedure Save; override;
function TryVersion(out AValue: TVersion): boolean;
procedure GetVersions(AVersionList: TStringList); override;
procedure UpdateVersion(AVersion: TVersion); override;
end;
implementation
{ TConstFile }
procedure TConstFile.AnalyzeVersionLine(ALine: string; out AValueStart, AValueLength: integer);
var
s: String;
p: integer;
begin
AValueStart := 0;
AValueLength:= 0;
s := ALine;
p := pos(FConstname+' ',s);
if (p<> 0) and ((p=1) or (s[p-1] in[#0..#32])) then
begin
inc(p, length(FConstName));
while (p <= length(s)) and (s[p] in[#0..#32]) do inc(p);
if (p <= length(s)) and (s[p] = '=') then
begin
inc(p);
while (p <= length(s)) and (s[p] in[#0..#32]) do inc(p);
AValueStart := p;
while (p <= length(s)) and (s[p] in['0'..'9']) do inc(p);
AValueLength:= p-AValueStart;
exit;
end;
end;
end;
constructor TConstFile.Create(AParameters: TStringList; ALogicDir: string);
var
ver: TVersion;
str: TStringStream;
stream: TFileStream;
begin
inherited Create(AParameters, ALogicDir);
ExpectParamCount(2);
FFilename := ExpandFileName(ReplaceVariables(Param[0]));
FConstname := Param[1];
stream := nil;
str := nil;
try
stream := TFileStream.Create(FFilename, fmOpenRead);
str := TStringStream.Create('');
if str.CopyFrom(stream, stream.Size)<>stream.Size then
raise exception.Create('Unable to read file');
FSourceCode := str.DataString;
finally
str.Free;
stream.Free;
end;
if TryVersion(ver) then
writeln('Code file "',ExtractFileName(FFilename),'" version ',VersionToStr(ver))
else
writeln('Code file "',ExtractFileName(FFilename),'" undefined version');
end;
destructor TConstFile.Destroy;
begin
inherited Destroy;
end;
procedure TConstFile.Save;
var
stream: TFileStream;
begin
if FChanged then
begin
writeln('Updating code file "',ExtractFileName(FFilename),'"');
stream := TFileStream.Create(FFilename, fmCreate);
try
if FSourceCode <> '' then
stream.WriteBuffer(FSourceCode[1], length(FSourceCode));
finally
stream.Free;
end;
end;
end;
function TConstFile.TryVersion(out AValue: TVersion): boolean;
var
valueStart,valueLen,errPos: integer;
verValue: LongWord;
begin
AValue.Major:= 0;
AValue.Minor:= 0;
AValue.Release:= 0;
AValue.Build:= 0;
AnalyzeVersionLine(FSourceCode, valueStart, valueLen);
if valueStart > 0 then
begin
val(copy(FSourceCode, valueStart, valueLen), verValue, errPos);
if errPos = 0 then
begin
AValue.Major:= verValue div 1000000;
AValue.Minor := (verValue div 10000) mod 100;
AValue.Release := (verValue div 100) mod 100;
AValue.Build := verValue mod 100;
exit(true);
end;
end;
result := false;
end;
procedure TConstFile.GetVersions(AVersionList: TStringList);
var
ver: TVersion;
verStr: String;
begin
if TryVersion(ver) then
begin
verStr := VersionToStr(ver);
if AVersionList.IndexOf(verStr)=-1 then AVersionList.Add(verStr);
end;
end;
procedure TConstFile.UpdateVersion(AVersion: TVersion);
var
ver: TVersion;
newValue, valueStart, valueLength: Integer;
s: String;
begin
newValue := AVersion.Major*1000000 + AVersion.Minor*10000 + AVersion.Release*100 + AVersion.Build;
if TryVersion(ver) then
begin
if AVersion<>ver then
begin
AnalyzeVersionLine(FSourceCode, valueStart,valueLength);
if valueStart <> 0 then
begin
s := FSourceCode;
delete(s, valueStart,valueLength);
insert(IntToStr(newValue), s,valueStart);
FSourceCode := s;
FChanged:= true;
end;
end;
end else
writeln('Please add manually a constant ',FConstname,' = ',newValue,' in "',ExtractFileName(FFilename),'"');
end;
end.

View File

@@ -0,0 +1,83 @@
// SPDX-License-Identifier: LGPL-3.0-linking-exception
unit CopyFile;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, ReleaserTypes;
type
{ TCopyFile }
TCopyFile = class(TReleaserObject)
private
FSourceFilename, FDestFilename: String;
FVersion: TVersion;
FVersionDefined: boolean;
public
constructor Create(AParameters: TStringList; ALogicDir: string); override;
procedure Save; override;
procedure GetVersions({%H-}AVersionList: TStringList); override;
procedure UpdateVersion(AVersion: TVersion); override;
end;
implementation
{ TCopyFile }
constructor TCopyFile.Create(AParameters: TStringList; ALogicDir: string);
begin
inherited Create(AParameters, ALogicDir);
ExpectParamCount(2);
FSourceFilename := ExpandFileName(Param[0]);
FDestFilename := ExpandFileName(Param[1]);
end;
procedure TCopyFile.Save;
var
dest: String;
streamIn,streamOut: TStream;
buf: array of byte;
bufCount: LongInt;
begin
if not FVersionDefined then exit;
streamIn := TFileStream.Create(ReplaceVariables(FSourceFilename), fmOpenRead);
streamOut := nil;
buf := nil;
try
dest := ReplaceVariables(FDestFilename);
if FileExists(dest) then
writeln('Replacing file "',ExtractFilename(dest),'"');
streamOut := TFileStream.Create(dest, fmCreate);
setlength(buf, 4096);
repeat
bufCount:= streamIn.Read(buf[0], length(buf));
streamOut.WriteBuffer(buf[0], bufCount);
until bufCount = 0;
finally
buf := nil;
streamOut.Free;
streamIn.Free;
end;
end;
procedure TCopyFile.GetVersions(AVersionList: TStringList);
begin
//nothing
end;
procedure TCopyFile.UpdateVersion(AVersion: TVersion);
begin
if not FileExists(ReplaceVariables(FSourceFilename)) then
raise exception.Create('Source file not found: '+FSourceFilename);
if not DirectoryExists(ExtractFilePath(ReplaceVariables(FDestFilename))) then
raise exception.Create('Target directory not found: '+ExtractFilePath(ReplaceVariables(FDestFilename)));
FVersion := AVersion;
FVersionDefined := true;
end;
end.

View File

@@ -0,0 +1,208 @@
// SPDX-License-Identifier: LGPL-3.0-linking-exception
unit MacBundle;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, ReleaserTypes, laz2_XMLRead, laz2_XMLWrite, Laz2_DOM;
type
{ TMacBundle }
TMacBundle = class(TReleaserObject)
private
FBundlepath: string;
FFilename: string;
FXml: TXmlDocument;
FDict: TStringList;
FChanged: boolean;
function GetName: string;
function GetPListFilename: string;
function ReadDict(ARoot: TDOMNode): TStringList;
procedure UpdateDict(ARoot: TDOMNode; ADict: TStringList);
function GetPList: TDOMNode;
public
constructor Create(AParameters: TStringList; ALogicDir: string); override;
destructor Destroy; override;
property Filename: string read FFilename;
function GetVersion: TVersion;
procedure GetVersions(AVersionList: TStringList); override;
procedure CheckVersion(AVersion: TVersion); override;
property Name: string read GetName;
procedure Save; override;
procedure UpdateVersion(AVersion: TVersion); override;
end;
implementation
{ TMacBundle }
function TMacBundle.GetName: string;
begin
result := '';
if Assigned(FDict) then
result := FDict.Values['CFBundleName'];
if result = '' then
result := ChangeFileExt(ExtractFileName(FBundlepath),'');
end;
function TMacBundle.GetPListFilename: string;
begin
result := FBundlepath+PathDelim+'Contents'+PathDelim+'Info.plist';
end;
function TMacBundle.ReadDict(ARoot: TDOMNode): TStringList;
var
dict, entry: TDOMNode;
key: string;
begin
result := TStringList.Create;
if Assigned(ARoot) then
begin
dict := ARoot.FindNode('dict');
if Assigned(dict) then
begin
entry := dict.FirstChild;
while entry <> nil do
begin
if entry.NodeName = 'key' then
begin
key := entry.TextContent;
entry := entry.NextSibling;
if (entry <> nil) and (entry.NodeName = 'string') then
begin
result.Values[key] := entry.TextContent;
entry := entry.NextSibling;
end
else if (entry <> nil) and (entry.NodeName <> 'key') then
entry := entry.NextSibling;
end else
entry := entry.NextSibling;
end;
end else
raise exception.Create('"dict" node not found');
end;
end;
procedure TMacBundle.UpdateDict(ARoot: TDOMNode; ADict: TStringList);
var
dict, entry: TDOMNode;
key: string;
begin
if Assigned(ARoot) then
begin
dict := ARoot.FindNode('dict');
if Assigned(dict) then
begin
entry := dict.FirstChild;
while entry <> nil do
begin
if entry.NodeName = 'key' then
begin
key := entry.TextContent;
entry := entry.NextSibling;
if (entry <> nil) and (entry.NodeName = 'string') then
begin
entry.TextContent := ADict.Values[key];
entry := entry.NextSibling;
end
else if (entry <> nil) and (entry.NodeName <> 'key') then
entry := entry.NextSibling;
end else
entry := entry.NextSibling;
end;
end else
raise exception.Create('"dict" node not found');
end;
end;
function TMacBundle.GetPList: TDOMNode;
begin
result := FXml.FirstChild;
while (result <> nil) and not (result is TDOMElement) do result := result.NextSibling;
if (result = nil) or (result.NodeName <> 'plist') then raise exception.Create('"plist" node not found');
end;
constructor TMacBundle.Create(AParameters: TStringList; ALogicDir: string);
var
stream: TFileStream;
begin
inherited Create(AParameters, ALogicDir);
ExpectParamCount(1);
FBundlepath:= ExpandFileName(ReplaceVariables(Param[0]));
stream := TFileStream.Create(GetPListFilename, fmOpenRead);
try
ReadXMLFile(FXml,stream);
finally
stream.Free;
end;
FDict := ReadDict(GetPList);
writeln('Bundle ',Name,' version ',VersionToStr(GetVersion));
end;
destructor TMacBundle.Destroy;
begin
FXml.Free;
inherited Destroy;
end;
function TMacBundle.GetVersion: TVersion;
begin
if Assigned(FDict) then
result := StrToVersion(FDict.Values['CFBundleVersion'])
else raise exception.Create('Version node not found');
end;
procedure TMacBundle.GetVersions(AVersionList: TStringList);
var
ver: TVersion;
verStr: String;
begin
ver := GetVersion;
verStr := VersionToStr(ver);
if AVersionList.IndexOf(verStr)=-1 then
AVersionList.Add(verStr);
end;
procedure TMacBundle.CheckVersion(AVersion: TVersion);
begin
inherited CheckVersion(AVersion);
if AVersion<>GetVersion then raise exception.Create('Inconsistent version of bundle '+Name);
end;
procedure TMacBundle.Save;
begin
if FChanged then
begin
writeln('Updating bundle ', Name,'...');
WriteXMLFile(FXml, GetPListFilename);
end else
writeln('Bundle unchanged');
end;
procedure TMacBundle.UpdateVersion(AVersion: TVersion);
var
versionStr: String;
begin
if Assigned(FDict) then
begin
versionStr := VersionToStr(AVersion);
if FDict.Values['CFBundleVersion'] <> versionStr then
begin
FDict.Values['CFBundleVersion'] := versionStr;
FChanged := true;
end;
if FDict.Values['CFBundleShortVersionString'] <> versionStr then
begin
FDict.Values['CFBundleShortVersionString'] := versionStr;
FChanged := true;
end;
UpdateDict(GetPList, FDict);
end;
end;
end.

View File

@@ -0,0 +1,260 @@
// SPDX-License-Identifier: LGPL-3.0-linking-exception
unit ManagerFile;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, ReleaserTypes,
fpjson, jsonparser, ArchiveUrl, PackageFile;
type
{ TManagerFile }
TManagerFile = class(TReleaserObject)
private
FArchive: TArchiveUrl;
FFilename: string;
FPackages: TPackageFileList;
FRoot: TJSONObject;
FNew, FChanged: boolean;
FLineEnding: string;
procedure SetArchive(AValue: TArchiveUrl);
function GetPackageUpdateList: TJSONArray;
function GetDataNode: TJSONObject;
function GetDownloadUrl: string;
public
constructor Create(AParameters: TStringList; ALogicDir: string); override;
destructor Destroy; override;
class function IsUnique: boolean; override;
procedure LinkWith(AOtherObject: TReleaserObject); override;
property Archive: TArchiveUrl read FArchive write SetArchive;
procedure GetVersions({%H-}AVersionList: TStringList); override;
procedure CheckVersion(AVersion: TVersion); override;
procedure UpdateVersion(AVersion: TVersion); override;
procedure Save; override;
end;
implementation
{ TManagerFile }
procedure TManagerFile.SetArchive(AValue: TArchiveUrl);
begin
if FArchive=AValue then Exit;
FArchive:=AValue;
end;
function TManagerFile.GetPackageUpdateList: TJSONArray;
const packagePath1 = 'UpdateLazPackages';
packagePath2 = 'UpdatePackageFiles';
var
node: TJSONData;
begin
node := FRoot.FindPath(packagePath1);
if node = nil then node := FRoot.FindPath(packagePath2);
if node <> nil then result := node as TJSONArray
else
begin
result := TJSONArray.Create;
FRoot.Add(packagePath1, result);
end;
end;
function TManagerFile.GetDataNode: TJSONObject;
const dataPath = 'UpdatePackageData';
var
node: TJSONData;
begin
node := FRoot.FindPath(dataPath);
if node <> nil then result := node as TJSONObject
else
begin
result := TJSONObject.Create;
FRoot.Add(dataPath, result);
end;
end;
function TManagerFile.GetDownloadUrl: string;
var
url: string;
data: TJSONObject;
begin
if Archive <> nil then
begin
data := GetDataNode;
url := '';
url := data.Get('DownloadZipURL', url);
result := url;
end
else result := '';
end;
constructor TManagerFile.Create(AParameters: TStringList; ALogicDir: string);
var
stream: TFileStream;
begin
inherited Create(AParameters, ALogicDir);
ExpectParamCount(1);
FFilename:= ExpandFileName(ReplaceVariables(Param[0]));
if FileExists(FFilename) then
begin
FLineEnding:= DetectLineEnding(FFilename);
FNew := false;
stream := TFileStream.Create(FFilename, fmOpenRead);
try
FRoot := GetJSON(stream) as TJSONObject;
finally
stream.Free;
end;
end else
begin
FNew := true;
FRoot := TJSONObject.Create;
end;
FPackages := TPackageFileList.Create;
end;
destructor TManagerFile.Destroy;
begin
FPackages.Free;
FRoot.Free;
inherited Destroy;
end;
class function TManagerFile.IsUnique: boolean;
begin
Result:= true;
end;
procedure TManagerFile.LinkWith(AOtherObject: TReleaserObject);
var
updateList: TJSONArray;
packageEntry: TJSONObject;
name: String;
i: Integer;
updateName: string;
package: TPackageFile;
updateVer: TVersion;
begin
inherited LinkWith(AOtherObject);
if AOtherObject is TArchiveUrl then
Archive := TArchiveUrl(AOtherObject)
else if AOtherObject is TPackageFile then
begin
package := TPackageFile(AOtherObject);
name := ExtractFileName(package.Filename);
updateList := GetPackageUpdateList;
if FNew then
begin
packageEntry := TJSONObject.Create;
packageEntry.Booleans['ForceNotify'] := false;
packageEntry.Integers['InternalVersion'] := 1;
packageEntry.Strings['Name'] := name;
packageEntry.Strings['Version'] := VersionToStr(package.GetVersion, true);
updateList.Add(packageEntry);
FPackages.Add(package);
end else
for i := 0 to updateList.Count-1 do
begin
packageEntry := updateList.Items[i] as TJSONObject;
updateName := packageEntry.Strings['Name'];
updateVer := StrToVersion(packageEntry.Strings['Version']);
if updateName = name then
begin
FPackages.Add(package);
writeln('Package ', package.name,' is used in manager');
if updateVer <> package.GetVersion then
raise exception.Create('Package version specified in manager is inconsistent');
end;
end;
end;
end;
procedure TManagerFile.GetVersions(AVersionList: TStringList);
begin
//version will be provided by packages
end;
procedure TManagerFile.CheckVersion(AVersion: TVersion);
var
url: string;
begin
inherited CheckVersion(AVersion);
if Archive <> nil then
begin
url := GetDownloadUrl;
if (url <> '') and (url <> Archive.GetUrlForVersion(AVersion)) then
raise exception.Create('Archive version is not consistent (DownloadZipURL field of JSON)');
end;
end;
procedure TManagerFile.UpdateVersion(AVersion: TVersion);
var
name, updateName, url: String;
i, j: Integer;
updateList: TJSONArray;
packageEntry, data: TJSONObject;
begin
for i := 0 to FPackages.Count-1 do
begin
name := ExtractFileName(FPackages[i].Filename);
updateList := GetPackageUpdateList;
for j := 0 to updateList.Count-1 do
begin
packageEntry := updateList.Items[j] as TJSONObject;
updateName := packageEntry.Strings['Name'];
if updateName = name then
begin
packageEntry.Strings['Version'] := VersionToStr(AVersion, true);
FChanged := true;
end;
end;
end;
data := GetDataNode;
url := Archive.GetUrlForVersion(AVersion);
data.Strings['DownloadZipURL'] := url;
end;
procedure TManagerFile.Save;
var t: textfile;
data: TJSONObject;
url: String;
begin
if (FPackages.Count = 0) then raise exception.Create('Manager does not have an associated package');
data := GetDataNode;
if FNew then
begin
data.Booleans['DisableInOPM'] := false;
data.Strings['Name'] := FPackages[0].Name;
end;
if Assigned(Archive) then
begin
if GetDownloadUrl = '' then
begin
url := Archive.GetUrlForVersion(FPackages[0].GetVersion);
data.Strings['DownloadZipURL'] := url;
FChanged := true;
end;
end;
if FNew or FChanged then
begin
if FNew then
writeln('Creating manager file...')
else
writeln('Updating manager file...');
assignfile(t, FFilename);
rewrite(t);
write(t, StringReplace(FRoot.FormatJSON, LineEnding, FLineEnding, [rfReplaceAll]));
closefile(t);
end else
writeln('Manager file unchanged');
end;
end.

View File

@@ -0,0 +1,177 @@
// SPDX-License-Identifier: LGPL-3.0-linking-exception
unit PackageFile;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, ReleaserTypes, fgl, laz2_XMLRead, laz2_XMLWrite, Laz2_DOM;
type
{ TPackageFile }
TPackageFile = class(TReleaserObject)
private
FFilename: string;
FLineEnding: string;
FXml: TXmlDocument;
FChanged: boolean;
function GetName: string;
public
constructor Create(AParameters: TStringList; ALogicDir: string); override;
destructor Destroy; override;
property Filename: string read FFilename;
function GetVersion: TVersion;
procedure GetVersions(AVersionList: TStringList); override;
procedure CheckVersion(AVersion: TVersion); override;
property Name: string read GetName;
procedure Save; override;
procedure UpdateVersion(AVersion: TVersion); override;
end;
TPackageFileList = specialize TFPGList<TPackageFile>;
implementation
{ TPackageFile }
function TPackageFile.GetName: string;
var
config, packageNode, nameNode: TDOMNode;
begin
config := FXml.FindNode('CONFIG');
if Assigned(config) then
begin
packageNode := config.FindNode('Package');
if Assigned(packageNode) then
begin
nameNode := packageNode.FindNode('Name');
if Assigned(nameNode) then
begin
with (nameNode as TDOMElement) do
begin
result := GetAttribute('Value');
exit;
end;
end;
end;
end;
result := ChangeFileExt(ExtractFileName(FFilename),'');
end;
constructor TPackageFile.Create(AParameters: TStringList; ALogicDir: string);
var
stream: TFileStream;
begin
inherited Create(AParameters, ALogicDir);
ExpectParamCount(1);
FFilename:= ExpandFileName(ReplaceVariables(Param[0]));
FLineEnding:= DetectLineEnding(FFilename);
stream := TFileStream.Create(FFilename, fmOpenRead);
try
ReadXMLFile(FXml,stream);
finally
stream.Free;
end;
writeln('Package ',Name,' version ',VersionToStr(GetVersion));
end;
destructor TPackageFile.Destroy;
begin
FXml.Free;
inherited Destroy;
end;
function TPackageFile.GetVersion: TVersion;
var
config, packageNode, versionNode: TDOMNode;
begin
config := FXml.FindNode('CONFIG');
if Assigned(config) then
begin
packageNode := config.FindNode('Package');
if Assigned(packageNode) then
begin
versionNode := packageNode.FindNode('Version');
if Assigned(versionNode) then
begin
with (versionNode as TDOMElement) do
begin
result.Major:= StrToIntDef(GetAttribute('Major'),0);
result.Minor:= StrToIntDef(GetAttribute('Minor'),0);
result.Release:= StrToIntDef(GetAttribute('Release'),0);
result.Build:= StrToIntDef(GetAttribute('Build'),0);
exit;
end;
end;
end;
end;
raise exception.Create('Version node not found');
end;
procedure TPackageFile.GetVersions(AVersionList: TStringList);
var
ver: TVersion;
verStr: String;
begin
ver := GetVersion;
verStr := VersionToStr(ver);
if AVersionList.IndexOf(verStr)=-1 then
AVersionList.Add(verStr);
end;
procedure TPackageFile.CheckVersion(AVersion: TVersion);
begin
inherited CheckVersion(AVersion);
if AVersion<>GetVersion then raise exception.Create('Inconsistent version of package '+Name);
end;
procedure TPackageFile.Save;
var textStream: TStringStream;
t: TextFile;
begin
if FChanged then
begin
writeln('Updating package ', Name,'...');
textStream := TStringStream.Create;
WriteXMLFile(FXml, textStream);
AssignFile(t, FFilename);
Rewrite(t);
Write(t, StringReplace(textStream.DataString, LineEnding, FLineEnding, [rfReplaceAll]));
CloseFile(t);
textStream.Free;
end;
end;
procedure TPackageFile.UpdateVersion(AVersion: TVersion);
var
config, packageNode, versionNode: TDOMNode;
begin
config := FXml.FindNode('CONFIG');
if Assigned(config) then
begin
packageNode := config.FindNode('Package');
if Assigned(packageNode) then
begin
versionNode := packageNode.FindNode('Version');
if Assigned(versionNode) then
begin
with (versionNode as TDOMElement) do
begin
FChanged := true;
SetAttribute('Major', inttostr(AVersion.Major));
if AVersion.Minor <> 0 then SetAttribute('Minor', inttostr(AVersion.Minor)) else RemoveAttribute('Minor');
if AVersion.Release <> 0 then SetAttribute('Release', inttostr(AVersion.Release)) else RemoveAttribute('Release');
if AVersion.Build <> 0 then SetAttribute('Build', inttostr(AVersion.Build)) else RemoveAttribute('Build');
exit;
end;
end;
end;
end;
raise exception.Create('Version node not found');
end;
end.

View File

@@ -0,0 +1,200 @@
// SPDX-License-Identifier: LGPL-3.0-linking-exception
unit ProjectFile;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, ReleaserTypes, laz2_XMLRead, laz2_XMLWrite, Laz2_DOM;
type
{ TProjectFile }
TProjectFile = class(TReleaserObject)
private
FFilename: string;
FXml: TXmlDocument;
FChanged: boolean;
function GetName: string;
public
constructor Create(AParameters: TStringList; ALogicDir: string); override;
destructor Destroy; override;
property Filename: string read FFilename;
function GetVersion: TVersion;
procedure GetVersions(AVersionList: TStringList); override;
procedure CheckVersion(AVersion: TVersion); override;
property Name: string read GetName;
procedure Save; override;
procedure UpdateVersion(AVersion: TVersion); override;
end;
implementation
{ TProjectFile }
function TProjectFile.GetName: string;
var
config, projectNode, titleNode, generalNode: TDOMNode;
begin
config := FXml.FindNode('CONFIG');
if Assigned(config) then
begin
projectNode := config.FindNode('ProjectOptions');
if Assigned(projectNode) then
begin
generalNode := projectNode.FindNode('General');
if Assigned(generalNode) then
begin
titleNode := generalNode.FindNode('Title');
if Assigned(titleNode) then
begin
with (titleNode as TDOMElement) do
begin
result := GetAttribute('Value');
exit;
end;
end;
end;
end;
end;
result := ChangeFileExt(ExtractFileName(FFilename),'');
end;
constructor TProjectFile.Create(AParameters: TStringList; ALogicDir: string);
var
stream: TFileStream;
begin
inherited Create(AParameters, ALogicDir);
ExpectParamCount(1);
FFilename:= ExpandFileName(ReplaceVariables(Param[0]));
stream := TFileStream.Create(FFilename, fmOpenRead);
try
ReadXMLFile(FXml,stream);
finally
stream.Free;
end;
writeln('Project ',Name,' version ',VersionToStr(GetVersion));
end;
destructor TProjectFile.Destroy;
begin
FXml.Free;
inherited Destroy;
end;
function TProjectFile.GetVersion: TVersion;
var
config, projectNode, versionNode: TDOMNode;
function GetSubNode(ATag: string): integer;
var
subNode: TDOMNode;
begin
subNode := versionNode.FindNode(ATag);
if Assigned(subNode) then with (subNode as TDOMElement) do
result:= StrToIntDef(GetAttribute('Value'),0)
else
result:= 0;
end;
begin
config := FXml.FindNode('CONFIG');
if Assigned(config) then
begin
projectNode := config.FindNode('ProjectOptions');
if Assigned(projectNode) then
begin
versionNode := projectNode.FindNode('VersionInfo');
if Assigned(versionNode) then
begin
result.Major:= GetSubNode('MajorVersionNr');
result.Minor:= GetSubNode('MinorVersionNr');
result.Release:= GetSubNode('RevisionNr');
result.Build:= GetSubNode('BuildNr');
exit;
end;
end;
end;
raise exception.Create('Version node not found');
end;
procedure TProjectFile.GetVersions(AVersionList: TStringList);
var
ver: TVersion;
verStr: String;
begin
ver := GetVersion;
verStr := VersionToStr(ver);
if AVersionList.IndexOf(verStr)=-1 then
AVersionList.Add(verStr);
end;
procedure TProjectFile.CheckVersion(AVersion: TVersion);
begin
inherited CheckVersion(AVersion);
if AVersion<>GetVersion then raise exception.Create('Inconsistent version of project '+Name);
end;
procedure TProjectFile.Save;
begin
if FChanged then
begin
writeln('Updating project ', Name,'...');
WriteXMLFile(FXml, FFilename);
end else
writeln('Project file unchanged');
end;
procedure TProjectFile.UpdateVersion(AVersion: TVersion);
var
config, projectNode, versionNode: TDOMNode;
procedure UpdateSubNode(ATag: string; AValue: integer);
var
subNode: TDOMElement;
begin
subNode := versionNode.FindNode(ATag) as TDOMElement;
if AValue<>0 then
begin
if subNode=nil then
begin
subNode := FXml.CreateElement(ATag);
versionNode.AppendChild(subNode);
end;
subNode.SetAttribute('Value', inttostr(AValue));
end else
begin
if Assigned(subNode) then
begin
versionNode.RemoveChild(subNode);
subNode.Free;
end;
end;
end;
begin
config := FXml.FindNode('CONFIG');
if Assigned(config) then
begin
projectNode := config.FindNode('ProjectOptions');
if Assigned(projectNode) then
begin
versionNode := projectNode.FindNode('VersionInfo');
if Assigned(versionNode) then
begin
FChanged := true;
UpdateSubNode('MajorVersionNr', AVersion.Major);
UpdateSubNode('MinorVersionNr', AVersion.Minor);
UpdateSubNode('RevisionNr', AVersion.Release);
UpdateSubNode('BuildNr', AVersion.Build);
exit;
end;
end;
end;
raise exception.Create('Version node not found');
end;
end.

View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="12"/>
<General>
<Flags>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasScaledStatement Value="False"/>
<CompatibilityMode Value="True"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<Title Value="Releaser"/>
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
</General>
<BuildModes Count="1">
<Item1 Name="Default" Default="True"/>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
<UseFileFilters Value="True"/>
</PublishOptions>
<RunParams>
<FormatVersion Value="2"/>
</RunParams>
<RequiredPackages Count="1">
<Item1>
<PackageName Value="LazUtils"/>
</Item1>
</RequiredPackages>
<Units Count="11">
<Unit0>
<Filename Value="releaser.lpr"/>
<IsPartOfProject Value="True"/>
<UnitName Value="Releaser"/>
</Unit0>
<Unit1>
<Filename Value="bgrabitmap.logic"/>
<IsPartOfProject Value="True"/>
</Unit1>
<Unit2>
<Filename Value="releasertypes.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="ReleaserTypes"/>
</Unit2>
<Unit3>
<Filename Value="managerfile.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="ManagerFile"/>
</Unit3>
<Unit4>
<Filename Value="archiveurl.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="ArchiveUrl"/>
</Unit4>
<Unit5>
<Filename Value="packagefile.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="PackageFile"/>
</Unit5>
<Unit6>
<Filename Value="constfile.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="ConstFile"/>
</Unit6>
<Unit7>
<Filename Value="projectfile.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="ProjectFile"/>
</Unit7>
<Unit8>
<Filename Value="textline.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="TextLine"/>
</Unit8>
<Unit9>
<Filename Value="copyfile.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="CopyFile"/>
</Unit9>
<Unit10>
<Filename Value="macbundle.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="MacBundle"/>
</Unit10>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<Target>
<Filename Value="releaser"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Linking>
<Debugging>
<DebugInfoType Value="dsDwarf2Set"/>
</Debugging>
</Linking>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">
<Item1>
<Name Value="EAbort"/>
</Item1>
<Item2>
<Name Value="ECodetoolError"/>
</Item2>
<Item3>
<Name Value="EFOpenError"/>
</Item3>
</Exceptions>
</Debugging>
</CONFIG>

View File

@@ -0,0 +1,325 @@
// SPDX-License-Identifier: LGPL-3.0-only (modified to allow linking)
program Releaser;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Classes, SysUtils, CustApp, ReleaserTypes, ManagerFile, ArchiveUrl,
PackageFile, ProjectFile, ConstFile, TextLine, CopyFile, MacBundle
{ you can add units after this };
type
{ TReleaserApp }
TReleaserApp = class(TCustomApplication)
protected
procedure DoRun; override;
public
constructor Create(TheOwner: TComponent); override;
procedure ProcessFile(AFilename: string; AOptions: TStringList);
destructor Destroy; override;
procedure WriteHelp; virtual;
end;
TIfState = (isNone, isSkipTrue, isDoTrue, isSkipFalse, isDoFalse);
{ TReleaserApp }
procedure TReleaserApp.DoRun;
var
ErrorMsg, dir: String;
opts, logicFiles: TStringList;
i: Integer;
findRec: TRawByteSearchRec;
begin
// quick check parameters
opts := TStringList.Create;
logicFiles := TStringList.Create;
ErrorMsg:=CheckOptions('hv', ['help','version:'], opts, logicFiles);
if ErrorMsg<>'' then begin
writeln(ErrorMsg);
Terminate;
Exit;
end;
// parse parameters
if HasOption('h', 'help') then begin
WriteHelp;
Terminate;
Exit;
end;
if logicFiles.Count = 0 then
begin
if FindFirst('*.logic', faAnyFile, findRec)=0 then
repeat
if (findRec.Attr and faDirectory)=0 then logicFiles.Add(ExpandFileName(findRec.Name));
until FindNext(findRec)<>0;
FindClose(findRec);
end;
dir := GetCurrentDir;
for i := 0 to logicFiles.Count-1 do
begin
SetCurrentDir(dir);
ProcessFile(logicFiles[i], opts);
end;
opts.Free;
logicFiles.Free;
// stop program loop
Terminate;
end;
constructor TReleaserApp.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
StopOnException:=True;
end;
procedure TReleaserApp.ProcessFile(AFilename: string;
AOptions: TStringList);
var
objs: TReleaserObjectList;
function GetVersion: TVersion;
var versions: TStringList;
i: integer;
begin
versions := TStringList.Create;
try
for i := 0 to objs.Count-1 do
objs[i].GetVersions(versions);
if versions.Count = 0 then raise exception.Create('Version not found')
else if versions.Count > 1 then writeln('Multiple versions found!');
result := StrToVersion(versions[0]);
finally
versions.Free;
end;
end;
var
t: textfile;
s, cmd: string;
ver, newVer: TVersion;
line: TStringList;
factory: TReleaserObjectFactory;
i, lineNumber, j: Integer;
newVerStr, logicDir, newDir: string;
ifStack: array of TIfState;
skipCommand: boolean;
function PeekIfStack: TIfState;
begin
if ifStack = nil then
result := isNone
else result := ifStack[high(ifStack)];
end;
procedure PokeIfStack(AValue: TIfState);
begin
if ifStack <> nil then
ifStack[high(ifStack)] := AValue;
end;
procedure PopIfStack;
begin
if ifStack <> nil then
setlength(ifStack, length(ifStack)-1);
end;
procedure PushIfStack(AValue: TIfState);
begin
setlength(ifStack, length(ifStack)+1);
ifStack[high(ifStack)] := AValue;
end;
begin
AFilename := ExpandFileName(AFilename);
writeln('Logic file "', AFilename,'"');
assignfile(t, AFilename);
reset(t);
line := TStringList.Create;
objs := TReleaserObjectList.Create;
lineNumber := 0;
ifStack := nil;
try
while not eof(t) do
begin
inc(lineNumber);
readln(t, s);
line.CommaText:= trim(s);
if line.Count > 0 then
begin
cmd := line[0];
line.Delete(0);
skipCommand := false;
if (cmd = 'end') and (PeekIfStack <> isNone) then
begin
PopIfStack;
skipCommand:= true;
end;
if (cmd = 'else') and (PeekIfStack in[isDoTrue,isSkipTrue]) then
begin
if PeekIfStack = isDoTrue then
PokeIfStack(isSkipFalse)
else PokeIfStack(isDoFalse);
skipCommand:= true;
end;
if not skipCommand and (PeekIfStack in[isSkipTrue,isSkipFalse]) then
skipCommand:= true;
factory := nil;
if not skipCommand then
case LowerCase(cmd) of
'cd': begin
if line.Count <> 1 then raise exception.Create('Expecting directory');
logicDir := ExtractFilePath(AFilename);
delete(logicDir, length(logicDir), 1);
newDir := StringReplace(AdaptPathDelim(line[0]),'($LogicDir)',logicDir,[rfReplaceAll]);
SetCurrentDir(newDir);
end;
'manager': factory := TManagerFile;
'archive': factory := TArchiveUrl;
'package': factory := TPackageFile;
'project': factory := TProjectFile;
'bundle': factory := TMacBundle;
'const': factory := TConstFile;
'echo': for i := 0 to line.Count-1 do writeln(line[i]);
'text': factory := TTextLine;
'copy': factory := TCopyFile;
'if': begin
if line.Count = 0 then
raise exception.Create('Expecting condition');
if line[0] = 'exists' then
begin
if line.Count <> 2 then
raise exception.Create('Expecting 1 parameter');
if FileExists(line[1]) or DirectoryExists(line[1]) then
PushIfStack(isDoTrue)
else PushIfStack(isSkipTrue);
end else
raise exception.Create('Unknown condition "'+line[0]);
end;
'else', 'end': raise exception.Create('Unexpected branching "'+cmd+'"');
else
raise exception.Create('Unknown command "'+cmd+'"');
end;
if Assigned(factory) then
begin
if factory.IsUnique then
begin
for i := 0 to objs.Count-1 do
if objs[i] is factory then
raise exception.Create('Unicity constraint not satisfied for '+factory.ClassName);
end;
objs.Add(factory.Create(line, logicDir));
end;
end;
end;
lineNumber := 0;
for i := 0 to objs.Count-1 do
for j := 0 to objs.Count-1 do
objs[i].LinkWith(objs[j]);
ver := GetVersion;
for i := 0 to objs.Count-1 do
objs[i].CheckVersion(ver);
writeln('Current version: ',VersionToStr(ver));
newVerStr := '';
for i := 0 to AOptions.Count-1 do
if AOptions[i].StartsWith('version=') then
begin
newVerStr := copy(AOptions[i], length('version=')+1, length(AOptions[i])-length('version='));
break;
end;
if newVerStr = '' then
begin
write('New version (press Enter to keep the same): ');
readln(newVerStr);
end else
writeln('New version: ', newVerStr);
if Trim(newVerStr)='' then newVer := ver
else newVer := StrToVersion(newVerStr);
if newVer <> ver then
begin
for i := 0 to objs.Count-1 do
objs[i].UpdateVersion(newVer);
end else
begin
for i := 0 to objs.Count-1 do
if (objs[i] is TConstFile) or (objs[i] is TCopyFile) then //constants and copied files are loosely checked
objs[i].UpdateVersion(newVer);
end;
for i := 0 to objs.Count-1 do
objs[i].Save;
writeln('Done.');
except
on ex:exception do
begin
write('Error');
if lineNumber <> 0 then write(' on line ',lineNumber);
writeln(': ', ex.Message);
end;
end;
objs.Free;
line.Free;
closefile(t);
end;
destructor TReleaserApp.Destroy;
begin
inherited Destroy;
end;
procedure TReleaserApp.WriteHelp;
begin
{ add your help code here }
writeln('Update version number and check its consistence.');
writeln;
writeln('Usage: ', ExeName, ' [logicfile1 logicfile2...] [--version=versionNb] [--help]');
writeln;
writeln(' Parameter Description');
writeln(' --------- ----------------------------------------------------------------');
writeln(' versionNb New version number to assign to manager and packages');
writeln;
writeln(' logicfile File containing the location of the version number. If it is not');
writeln(' specified, all logic files in current directory are processed.');
writeln;
writeln(' Sample file: mylib.logic');
writeln(' ----------------------------------------------------------------');
writeln(' cd /mylib');
writeln(' manager update_mylib.json');
writeln(' archive https://github.com/mylib/mylib/archive/v$(Version).zip');
writeln(' package mylib/mylibpack1.lpk');
writeln(' package mylib/mylibpack2.lpk');
writeln(' const mylib/mylibtypes.pas MyLibVersion');
writeln;
writeln(' Sample file: myprog.logic');
writeln(' ----------------------------------------------------------------');
writeln(' cd ($LogicDir)');
writeln(' project myproject.lpi');
writeln(' const myconsts.pas MyProjectVersion');
writeln;
end;
var
Application: TReleaserApp;
begin
Application:=TReleaserApp.Create(nil);
Application.Title:='Releaser';
Application.Run;
Application.Free;
end.

View File

@@ -0,0 +1,199 @@
// SPDX-License-Identifier: LGPL-3.0-linking-exception
unit ReleaserTypes;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, fgl;
type
TVersion = record
case boolean of
false: (Value: array[1..4] of integer);
true: (Major, Minor, Release, Build: integer);
end;
operator =(AVersion1,AVersion2: TVersion): boolean;
function VersionToStr(AVersion: TVersion; AAlwaysIncludeZero: boolean = false): string;
function StrToVersion(AStr: string): TVersion;
type
{ TReleaserObject }
TReleaserObject = class
private
function GetParam(AIndex: integer): string;
function GetParamCount: integer;
protected
FLogicDir: string;
function ReplaceVariables(AText: string; AVersion: TVersion): string; overload;
function ReplaceVariables(AText: string): string; overload;
public
FParameters: TStringList;
constructor Create(AParameters: TStringList; ALogicDir: string); virtual;
procedure ExpectParamCount(ACount: integer);
destructor Destroy; override;
property ParamCount: integer read GetParamCount;
property Param[AIndex: integer]: string read GetParam;
class function IsUnique: boolean; virtual;
procedure LinkWith({%H-}AOtherObject: TReleaserObject); virtual;
procedure GetVersions(AVersionList: TStringList); virtual; abstract;
procedure CheckVersion({%H-}AVersion: TVersion); virtual;
procedure UpdateVersion({%H-}AVersion: TVersion); virtual;
procedure Save; virtual; abstract;
end;
TReleaserObjectFactory = class of TReleaserObject;
TReleaserObjectList = specialize TFPGObjectList<TReleaserObject>;
function AdaptPathDelim(APath: string): string;
function DetectLineEnding(AFilename: string): string;
implementation
operator=(AVersion1, AVersion2: TVersion): boolean;
begin
result := (AVersion1.Major = AVersion2.Major) and
(AVersion1.Minor = AVersion2.Minor) and
(AVersion1.Release = AVersion2.Release) and
(AVersion1.Build = AVersion2.Build);
end;
function VersionToStr(AVersion: TVersion; AAlwaysIncludeZero: boolean): string;
begin
result := IntToStr(AVersion.Major);
if AAlwaysIncludeZero or (AVersion.Minor<>0) or (AVersion.Release<>0) or (AVersion.Build<>0) then
begin
result += '.' + IntToStr(AVersion.Minor);
if AAlwaysIncludeZero or (AVersion.Release<>0) or (AVersion.Build<>0) then
begin
result += '.' + IntToStr(AVersion.Release);
if AAlwaysIncludeZero or (AVersion.Build<>0) then
begin
result += '.' + IntToStr(AVersion.Build);
end;
end;
end;
end;
function StrToVersion(AStr: string): TVersion;
var
lst: TStringList;
i: Integer;
begin
lst := TStringList.Create;
lst.Delimiter:= '.';
lst.DelimitedText := AStr;
if lst.Count > 4 then
begin
lst.Free;
raise exception.Create('Invalid version string');
end;
for i := 1 to 4 do result.Value[i] := 0;
for i := 1 to lst.Count do
result.Value[i] := StrToInt(lst[i-1]);
lst.Free;
end;
function AdaptPathDelim(APath: string): string;
begin
if PathDelim <> '\' then
result := StringReplace(APath, '\', PathDelim, [rfReplaceAll]);
if PathDelim <> '/' then
result := StringReplace(APath, '/', PathDelim, [rfReplaceAll]);
end;
function DetectLineEnding(AFilename: string): string;
var t: TextFile;
c, c2: char;
begin
result := LineEnding;
AssignFile(t, AFilename);
Reset(t);
repeat
read(t, c);
if c in[#13,#10] then
begin
result := c;
if not eof(t) then
begin
read(t, c2);
if (c2 in [#13,#10]) and (c2 <> c) then
result += c2;
end;
break;
end;
until eof(t);
CloseFile(t);
end;
{ TReleaserObject }
function TReleaserObject.GetParam(AIndex: integer): string;
begin
result := FParameters[AIndex];
end;
function TReleaserObject.GetParamCount: integer;
begin
result := FParameters.Count;
end;
function TReleaserObject.ReplaceVariables(AText: string; AVersion: TVersion): string;
begin
result := AText;
result := StringReplace(result, '$(Version)', VersionToStr(AVersion), [rfIgnoreCase,rfReplaceAll]);
result := StringReplace(result, '$(LogicDir)', FLogicDir, [rfIgnoreCase,rfReplaceAll]);
end;
function TReleaserObject.ReplaceVariables(AText: string): string;
begin
result := AText;
result := StringReplace(result, '$(Version)', '?', [rfIgnoreCase,rfReplaceAll]);
result := StringReplace(result, '$(LogicDir)', FLogicDir, [rfIgnoreCase,rfReplaceAll]);
end;
constructor TReleaserObject.Create(AParameters: TStringList; ALogicDir: string);
begin
FParameters := TStringList.Create;
FParameters.AddStrings(AParameters);
FLogicDir := ALogicDir;
end;
procedure TReleaserObject.ExpectParamCount(ACount: integer);
begin
if ACount <> ParamCount then
raise exception.Create('Invalid number of parameters. Found '+inttostr(ParamCount)+' but expected '+inttostr(ACount));
end;
destructor TReleaserObject.Destroy;
begin
FParameters.Free;
inherited Destroy;
end;
class function TReleaserObject.IsUnique: boolean;
begin
result := false;
end;
procedure TReleaserObject.LinkWith(AOtherObject: TReleaserObject);
begin
//nothing
end;
procedure TReleaserObject.CheckVersion(AVersion: TVersion);
begin
//nothing
end;
procedure TReleaserObject.UpdateVersion(AVersion: TVersion);
begin
//nothing
end;
end.

View File

@@ -0,0 +1,150 @@
// SPDX-License-Identifier: LGPL-3.0-linking-exception
unit TextLine;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, ReleaserTypes;
type
{ TTextLine }
TTextLine = class(TReleaserObject)
private
FFilename, FTextLine: String;
FTextLineStart, FTextLineEnd: integer;
FVersion: TVersion;
FText: string;
FChanged: boolean;
public
constructor Create(AParameters: TStringList; ALogicDir: string); override;
destructor Destroy; override;
procedure Save; override;
function GetLineForVersion(AVersion: TVersion): string;
procedure GetVersions({%H-}AVersionList: TStringList); override;
procedure CheckVersion(AVersion: TVersion); override;
procedure UpdateVersion(AVersion: TVersion); override;
end;
implementation
{ TTextLine }
constructor TTextLine.Create(AParameters: TStringList; ALogicDir: string);
var
str: TStringStream;
stream: TFileStream;
begin
inherited Create(AParameters, ALogicDir);
ExpectParamCount(2);
FFilename := ExpandFileName(ReplaceVariables(Param[0]));
FTextLine := Param[1];
FTextLineStart:= 0;
FTextLineEnd:= 0;
stream := nil;
str := nil;
try
stream := TFileStream.Create(FFilename, fmOpenRead);
str := TStringStream.Create('');
if str.CopyFrom(stream, stream.Size)<>stream.Size then
raise exception.Create('Unable to read file');
FText := str.DataString;
finally
str.Free;
stream.Free;
end;
end;
destructor TTextLine.Destroy;
begin
inherited Destroy;
end;
procedure TTextLine.Save;
var
stream: TFileStream;
begin
if FChanged then
begin
writeln('Updating text file "',ExtractFileName(FFilename),'"');
stream := TFileStream.Create(FFilename, fmCreate);
try
if FText <> '' then
stream.WriteBuffer(FText[1], length(FText));
finally
stream.Free;
end;
end;
end;
function TTextLine.GetLineForVersion(AVersion: TVersion): string;
begin
result := ReplaceVariables(FTextLine, AVersion);
end;
procedure TTextLine.GetVersions(AVersionList: TStringList);
begin
//nothing
end;
procedure TTextLine.CheckVersion(AVersion: TVersion);
var
i, start: Integer;
expectLine: string;
procedure TryLine(AEnd: integer);
var
curLine: String;
begin
if AEnd > start then
begin
curLine := copy(FText, start, AEnd-start);
if curLine = expectLine then
begin
FTextLineStart:= start;
FTextLineEnd:= AEnd;
FVersion := AVersion;
end;
end;
start := AEnd+1;
end;
begin
inherited CheckVersion(AVersion);
if FTextLineEnd > FTextLineStart then exit;
expectLine := GetLineForVersion(AVersion);
i := 1;
start := 1;
while (i < length(FText)) and (FTextLineEnd <= FTextLineStart) do
begin
if FText[i] in[#13,#10] then TryLine(i);
inc(i);
end;
if (FTextLineEnd <= FTextLineStart) then TryLine(i);
if FTextLineEnd > FTextLineStart then
writeln('Text file "',ExtractFileName(FFilename),'" line found')
else
writeln('Text file "',ExtractFileName(FFilename),'" line not found');
end;
procedure TTextLine.UpdateVersion(AVersion: TVersion);
var
newLine: String;
begin
if AVersion = FVersion then exit;
newLine := GetLineForVersion(AVersion);
if FTextLineEnd > FTextLineStart then
begin
delete(FText, FTextLineStart, FTextLineEnd-FTextLineStart);
insert(newLine, FText, FTextLineStart);
FTextLineEnd:= FTextLineStart+length(newLine);
FChanged:= true;
end else
writeln('Please add manually a line "',newLine,'" in "',ExtractFileName(FFilename),'"');
end;
end.