diff --git a/windows/src/engine/kmcomapi/kmcomapi.dpr b/windows/src/engine/kmcomapi/kmcomapi.dpr
index 2a60ffbbc8..29ca83ce39 100644
--- a/windows/src/engine/kmcomapi/kmcomapi.dpr
+++ b/windows/src/engine/kmcomapi/kmcomapi.dpr
@@ -145,7 +145,8 @@ uses
VKeys in '..\..\global\delphi\general\VKeys.pas',
Windows8LanguageList in '..\..\global\delphi\general\Windows8LanguageList.pas',
BCP47SuppressScriptRegistry in '..\..\global\delphi\general\BCP47SuppressScriptRegistry.pas',
- BCP47Tag in '..\..\global\delphi\general\BCP47Tag.pas';
+ BCP47Tag in '..\..\global\delphi\general\BCP47Tag.pas',
+ JsonUtil in '..\..\global\delphi\general\JsonUtil.pas';
{$R *.TLB}
diff --git a/windows/src/engine/kmcomapi/kmcomapi.dproj b/windows/src/engine/kmcomapi/kmcomapi.dproj
index 1cc46008ed..9a2b083879 100644
--- a/windows/src/engine/kmcomapi/kmcomapi.dproj
+++ b/windows/src/engine/kmcomapi/kmcomapi.dproj
@@ -252,6 +252,7 @@
+
Cfg_2
diff --git a/windows/src/global/delphi/general/JsonUtil.pas b/windows/src/global/delphi/general/JsonUtil.pas
index 9282c12342..0391858a01 100644
--- a/windows/src/global/delphi/general/JsonUtil.pas
+++ b/windows/src/global/delphi/general/JsonUtil.pas
@@ -84,7 +84,7 @@ begin
if i < JSONValue.Count - 1 then
OutputStrings[OutputStrings.Count-1] := OutputStrings[OutputStrings.Count-1] + ','; // I4440
end;
- OutputStrings.add(StringOfChar(' ', indent * INDENT_SIZE) + ']');
+ OutputStrings.add(StringOfChar(' ', (indent-1) * INDENT_SIZE) + ']');
end;
procedure PrettyPrintJSON(JSONValue: TJSONValue; OutputStrings: TStrings; indent: integer = 0);
diff --git a/windows/src/global/delphi/general/PackageInfo.pas b/windows/src/global/delphi/general/PackageInfo.pas
index af9c0ec76c..597cba9330 100644
--- a/windows/src/global/delphi/general/PackageInfo.pas
+++ b/windows/src/global/delphi/general/PackageInfo.pas
@@ -40,6 +40,7 @@ uses
System.Classes,
System.Generics.Collections,
System.IniFiles,
+ System.JSON,
System.Sysutils,
Winapi.Windows,
Xml.XMLDoc,
@@ -50,6 +51,8 @@ uses
{ Package Information Classes }
+function GetJsonValueString(o: TJSONObject; const n: string): string;
+
type
TPackageInfoEntryType = (pietName, pietVersion, pietCopyright, pietAuthor, pietWebsite, pietOther);
@@ -150,6 +153,8 @@ type
procedure SaveIni(AIni: TIniFile); virtual;
procedure LoadXML(ARoot: IXMLNode); virtual;
procedure SaveXML(ARoot: IXMLNode); virtual;
+ procedure LoadJSON(ARoot: TJSONObject); virtual;
+ procedure SaveJSON(ARoot: TJSONObject); virtual;
end;
{ Package Options }
@@ -178,6 +183,8 @@ type
procedure SaveIni(AIni: TIniFile); virtual;
procedure LoadXML(ARoot: IXMLNode); virtual;
procedure SaveXML(ARoot: IXMLNode); virtual;
+ procedure LoadJSON(ARoot: TJSONObject); virtual;
+ procedure SaveJSON(ARoot: TJSONObject); virtual;
property LoadLegacy: Boolean read FLoadLegacy write FLoadLegacy;
property FileVersion: WideString read FFileVersion write SetFileVersion;
property ExecuteProgram: WideString read FExecuteProgram write SetExecuteProgram;
@@ -218,6 +225,8 @@ type
procedure SaveIni(AIni: TIniFile); virtual;
procedure LoadXML(ARoot: IXMLNode); virtual;
procedure SaveXML(ARoot: IXMLNode); virtual;
+ procedure LoadJSON(ARoot: TJSONObject); virtual;
+ procedure SaveJSON(ARoot: TJSONObject); virtual;
function IndexOf(Name: WideString): Integer; overload;
@@ -246,6 +255,8 @@ type
procedure SaveIni(AIni: TIniFile); virtual;
procedure LoadXML(ARoot: IXMLNode); virtual;
procedure SaveXML(ARoot: IXMLNode); virtual;
+ procedure LoadJSON(ARoot: TJSONObject); virtual;
+ procedure SaveJSON(ARoot: TJSONObject); virtual;
end;
TPackageStartMenu = class(TPackageBaseObject)
@@ -261,6 +272,8 @@ type
procedure SaveIni(AIni: TIniFile); virtual;
procedure LoadXML(ARoot: IXMLNode); virtual;
procedure SaveXML(ARoot: IXMLNode); virtual;
+ procedure LoadJSON(ARoot: TJSONObject); virtual;
+ procedure SaveJSON(ARoot: TJSONObject); virtual;
end;
{ Package contained files }
@@ -291,6 +304,8 @@ type
procedure SaveIni(AIni: TIniFile); virtual;
procedure LoadXML(ARoot: IXMLNode); virtual;
procedure SaveXML(ARoot: IXMLNode); virtual;
+ procedure LoadJSON(ARoot: TJSONObject); virtual;
+ procedure SaveJSON(ARoot: TJSONObject); virtual;
function IndexOfFileType(FFileType: TKMFileType): Integer;
function FromFileName(Filename: WideString): TPackageContentFile;
@@ -338,6 +353,8 @@ type
procedure SaveIni(AIni: TIniFile); virtual;
procedure LoadXML(ARoot: IXMLNode); virtual;
procedure SaveXML(ARoot: IXMLNode); virtual;
+ procedure LoadJSON(ARoot: TJSONObject); virtual;
+ procedure SaveJSON(ARoot: TJSONObject); virtual;
end;
TPackageKeyboardLanguage = class(TPackageBaseObject)
@@ -359,6 +376,8 @@ type
function XMLRootNode: WideString; virtual;
procedure DoLoadXML(ARoot: IXMLNode); virtual;
procedure DoSaveXML(ARoot: IXMLNode); virtual;
+ procedure DoLoadJSON(ARoot: TJSONObject); virtual;
+ procedure DoSaveJSON(ARoot: TJSONObject); virtual;
public
Options: TPackageOptions;
StartMenu: TPackageStartMenu;
@@ -376,6 +395,9 @@ type
procedure LoadXML; //virtual;
procedure SaveXML; //virtual;
+ procedure LoadJSON;
+ procedure SaveJSON;
+
function SaveXMLToText: WideString;
procedure LoadXMLFromText(Text: WideString);
@@ -393,6 +415,7 @@ uses
System.TypInfo,
System.Variants,
+ JsonUtil,
KeymanVersion,
utildir,
utilsystem,
@@ -406,6 +429,78 @@ const
SDisplayFontNotOwnedCorrectly = 'The display font file ''%s'' referred to is not part of the package.';
SOSKFontNotOwnedCorrectly = 'The OSK font file ''%s'' referred to is not part of the package.';
+
+
+
+const
+ SXML_PackageKeyboards = 'Keyboards';
+ SXML_PackageKeyboard_Name = 'Name';
+ SXML_PackageKeyboard_ID = 'ID';
+ SXML_PackageKeyboard_Version = 'Version';
+ SXML_PackageKeyboard_OSKFont = 'OSKFont';
+ SXML_PackageKeyboard_DisplayFont = 'DisplayFont';
+ SXML_PackageKeyboard_Languages = 'Languages';
+
+ SXML_PackageKeyboard_Language = 'Language';
+ SXML_PackageKeyboard_Language_ID = 'ID';
+ SXML_PackageKeyboard_Language_Name = 'Name';
+
+const
+ SJSON_System = 'system';
+ SJSON_System_KeymanDeveloperVersion = 'keymanDeveloperVersion';
+ SJSON_System_FileVersion = 'fileVersion';
+
+ SJSON_Options = 'options';
+ SJSON_Options_ExecuteProgram = 'executeProgram';
+ SJSON_Options_ReadMeFile = 'readmeFile';
+ SJSON_Options_GraphicFile = 'graphicFile';
+
+ SJSON_Registry = 'registry';
+ SJSON_Registry_Root = 'root';
+ SJSON_Registry_Key = 'key';
+ SJSON_Registry_Name = 'name';
+ SJSON_Registry_ValueType = 'valueType';
+ SJSON_Registry_Value = 'value';
+
+ SJSON_StartMenu = 'startMenu';
+ SJSON_StartMenu_Folder = 'folder';
+ SJSON_StartMenu_AddUninstallEntry = 'addUninstallEntry';
+ SJSON_StartMenu_Items = 'items';
+ SJSON_StartMenu_Items_Name = 'name';
+ SJSON_StartMenu_Items_Filename = 'filename';
+ SJSON_StartMenu_Items_Arguments = 'arguments';
+ SJSON_StartMenu_Items_Icon = 'icon';
+ SJSON_StartMenu_Items_Location = 'location';
+
+ SJSON_Info = 'info';
+ SJSON_Info__Description = 'description';
+ SJSON_Info__URL = 'url';
+
+ SJSON_Info_Website = 'website';
+ SJSON_Info_Version = 'version';
+ SJSON_Info_Name = 'name';
+ SJSON_Info_Copyright = 'copyright';
+ SJSON_Info_Author = 'author';
+
+ SJSON_PackageInfoEntryTypeNames: array[TPackageInfoEntryType] of string =
+ (SJSON_Info_Name, SJSON_Info_Version, SJSON_Info_Copyright, SJSON_Info_Author, SJSON_Info_Website, '');
+
+ SJSON_Files = 'files';
+ SJSON_Files_Name = 'name';
+ SJSON_Files_Description = 'description';
+ SJSON_Files_CopyLocation = 'copyLocation';
+
+ SJSON_Keyboards = 'keyboards';
+ SJSON_Keyboard_Name = 'name';
+ SJSON_Keyboard_ID = 'id';
+ SJSON_Keyboard_Version = 'version';
+ SJSON_Keyboard_OSKFont = 'oskFont';
+ SJSON_Keyboard_DisplayFont = 'displayFont';
+
+ SJSON_Keyboard_Languages = 'languages';
+ SJSON_Keyboard_Language_ID = 'id';
+ SJSON_Keyboard_Language_Name = 'name';
+
{-------------------------------------------------------------------------------
- TPackageOptions -
------------------------------------------------------------------------------}
@@ -482,6 +577,22 @@ begin
FRegistryKeys.LoadIni(AIni);
end;
+procedure TPackageOptions.LoadJSON(ARoot: TJSONObject);
+var
+ FSystem, FOptions: TJSONObject;
+begin
+ FSystem := ARoot.Values[SJSON_System] as TJSONObject;
+ FOptions := ARoot.Values[SJSON_Options] as TJSONObject;
+
+ FileVersion := GetJsonValueString(FSystem, SJSON_System_FileVersion);
+ ExecuteProgram := GetJsonValueString(FOptions, SJSON_Options_ExecuteProgram);
+ ReadmeFile := Package.Files.FromFileName(GetJsonValueString(FOptions, SJSON_Options_ReadMeFile));
+ GraphicFile := Package.Files.FromFileName(GetJsonValueString(FOptions, SJSON_Options_GraphicFile));
+ if Assigned(ReadmeFile) then ReadmeFile.AddNotifyObject(ReadmeRemoved);
+ if Assigned(GraphicFile) then GraphicFile.AddNotifyObject(GraphicRemoved);
+ FRegistryKeys.LoadJSON(ARoot);
+end;
+
procedure TPackageOptions.SaveIni(AIni: TIniFile);
begin
AIni.WriteString('Package', 'Version', FileVersion);
@@ -493,6 +604,26 @@ begin
FRegistryKeys.SaveIni(AIni);
end;
+procedure TPackageOptions.SaveJSON(ARoot: TJSONObject);
+var
+ FOptions, FSystem: TJSONObject;
+begin
+ FSystem := TJSONObject.Create;
+ ARoot.AddPair(SJSON_System, FSystem);
+ FSystem.AddPair(SJSON_System_KeymanDeveloperVersion, GetVersionString);
+ FSystem.AddPair(SJSON_System_FileVersion, FileVersion);
+
+ FOptions := TJSONObject.Create;
+ ARoot.AddPair(SJSON_Options, FOptions);
+ if ExecuteProgram <> '' then
+ FOptions.AddPair(SJSON_Options_ExecuteProgram, ExecuteProgram);
+ if Assigned(ReadmeFile) then
+ FOptions.AddPair(SJSON_Options_ReadMeFile, ReadmeFile.RelativeFileName);
+ if Assigned(GraphicFile) then
+ FOptions.AddPair(SJSON_Options_GraphicFile, GraphicFile.RelativeFileName);
+ FRegistryKeys.SaveJSON(ARoot);
+end;
+
procedure TPackageOptions.SetExecuteProgram(Value: WideString);
begin
FExecuteProgram := Value;
@@ -556,7 +687,7 @@ begin
FInfoType := pietOther;
for piet := Low(PackageInfoEntryTypeNames) to High(PackageInfoEntryTypeNames) do
- if PackageInfoEntryTypeNames[piet] = LowerCase(FName) then
+ if SameText(PackageInfoEntryTypeNames[piet], FName) then
FInfoType := piet;
end;
@@ -629,6 +760,34 @@ begin
end;
end;
+procedure TPackageInfoEntryList.LoadJSON(ARoot: TJSONObject);
+var
+ name: string;
+ i: Integer;
+ inf: TPackageInfoEntry;
+ FItems: TJSONObject;
+ FNode: TJSONObject;
+ v: TPackageInfoEntryType;
+begin
+ Clear;
+ FItems := ARoot.Values[SJSON_Info] as TJSONObject;
+ for i := 0 to FItems.Count - 1 do
+ begin
+ name := FItems.Pairs[i].JsonString.Value;
+ for v := Low(TPackageInfoEntryType) to High(TPackageInfoEntryType) do
+ if SJSON_PackageInfoEntryTypeNames[v] = name then
+ begin
+ FNode := FItems.Pairs[i].JsonValue as TJSONObject;
+ inf := TPackageInfoEntry.Create(Package);
+ inf.Name := name;
+ inf.Description := GetJsonValueString(FNode, SJSON_Info__Description);
+ inf.URL := GetJsonValueString(FNode, SJSON_Info__URL);
+ Add(inf);
+ Break;
+ end;
+ end;
+end;
+
procedure TPackageInfoEntryList.SaveIni(AIni: TIniFile);
var
i: Integer;
@@ -637,6 +796,26 @@ begin
AIni.WriteString('Info', Items[i].Name, Format('"%s","%s"', [Items[i].Description, Items[i].URL]));
end;
+procedure TPackageInfoEntryList.SaveJSON(ARoot: TJSONObject);
+var
+ i: Integer;
+ AInfoRoot, ANode: TJSONObject;
+begin
+ AInfoRoot := TJSONObject.Create;
+ ARoot.AddPair(SJSON_Info, AInfoRoot);
+ for i := 0 to Count - 1 do
+ begin
+ if SJSON_PackageInfoEntryTypeNames[Items[i].InfoType] <> '' then
+ begin
+ ANode := TJSONObject.Create;
+ AInfoRoot.AddPair(SJSON_PackageInfoEntryTypeNames[Items[i].InfoType], ANode);
+ ANode.AddPair(SJSON_Info__Description, Items[i].Description);
+ if Items[i].URL <> '' then
+ ANode.AddPair(SJSON_Info__URL, Items[i].URL);
+ end;
+ end;
+end;
+
function TPackageInfoEntryList.DescIndexOf(Name: WideString): WideString;
var
i: Integer;
@@ -729,16 +908,16 @@ end;
- TPackageStartMenuEntryList -
------------------------------------------------------------------------------}
-procedure TPackageStartMenuEntryList.LoadXML(ARoot: IXMLNode);
+function StrToStartMenuEntryLocation(s: WideString): TPackageStartMenuEntryLocation;
+var
+ v: Integer;
+begin
+ v := System.TypInfo.GetEnumValue(TypeInfo(TPackageStartMenuEntryLocation), s);
+ if v = -1 then Result := psmelStartMenu
+ else Result := TPackageStartMenuEntryLocation(v);
+end;
- function StrToStartMenuEntryLocation(s: WideString): TPackageStartMenuEntryLocation;
- var
- v: Integer;
- begin
- v := System.TypInfo.GetEnumValue(TypeInfo(TPackageStartMenuEntryLocation), s);
- if v = -1 then Result := psmelStartMenu
- else Result := TPackageStartMenuEntryLocation(v);
- end;
+procedure TPackageStartMenuEntryList.LoadXML(ARoot: IXMLNode);
var
sme: TPackageStartMenuEntry;
i: Integer;
@@ -805,6 +984,31 @@ begin
end;
end;
+procedure TPackageStartMenuEntryList.LoadJSON(ARoot: TJSONObject);
+var
+ i: Integer;
+ AItems: TJSONArray;
+ AItem: TJSONObject;
+ sme: TPackageStartMenuEntry;
+begin
+ AItems := ARoot.Values[SJSON_StartMenu_Items] as TJSONArray;
+ if not Assigned(AItems) then
+ Exit;
+
+ for i := 0 to AItems.Count - 1 do
+ begin
+ AItem := AItems.Items[i] as TJSONObject;
+
+ sme := TPackageStartMenuEntry.Create(Package);
+ sme.Name := GetJsonValueString(AItem, SJSON_StartMenu_Items_Name);
+ sme.Prog := GetJsonValueString(AItem, SJSON_StartMenu_Items_Filename);
+ sme.Params := GetJsonValueString(AItem, SJSON_StartMenu_Items_Arguments);
+ sme.Icon := GetJsonValueString(AItem, SJSON_StartMenu_Items_Icon);
+ sme.Location := StrToStartMenuEntryLocation(GetJsonValueString(AItem, SJSON_StartMenu_Items_Location));
+ Self.Add(sme);
+ end;
+end;
+
procedure TPackageStartMenuEntryList.SaveIni(AIni: TIniFile);
var
i: Integer;
@@ -813,6 +1017,30 @@ begin
AIni.WriteString('StartMenuEntries', Items[i].Name, Format('"%s","%s"', [Items[i].Prog, Items[i].Params]));
end;
+procedure TPackageStartMenuEntryList.SaveJSON(ARoot: TJSONObject);
+var
+ i: Integer;
+ AItems: TJSONArray;
+ AItem: TJSONObject;
+begin
+ AItems := TJSONArray.Create;
+ ARoot.AddPair(SJSON_StartMenu_Items, AItems);
+ for i := 0 to Count - 1 do
+ begin
+ AItem := TJSONObject.Create;
+ AItems.Add(AItem);
+ AItem.AddPair(SJSON_StartMenu_Items_Name, Items[i].Name);
+ AItem.AddPair(SJSON_StartMenu_Items_Filename, Items[i].Prog);
+ if Items[i].Params <> '' then
+ AItem.AddPair(SJSON_StartMenu_Items_Arguments, Items[i].Params);
+ if Items[i].Icon <> '' then
+ AItem.AddPair(SJSON_StartMenu_Items_Icon, Items[i].Icon);
+ if Items[i].Location <> psmelStartMenu then
+ AItem.AddPair(SJSON_StartMenu_Items_Location,
+ System.TypInfo.GetEnumName(TypeInfo(TPackageStartMenuEntryLocation), Ord(Items[i].Location)));
+ end;
+end;
+
procedure TPackageStartMenuEntryList.Assign(Source: TPackageStartMenuEntryList);
var
i: Integer;
@@ -880,6 +1108,21 @@ begin
Entries.LoadIni(AIni);
end;
+procedure TPackageStartMenu.LoadJSON(ARoot: TJSONObject);
+var
+ ANode: TJSONObject;
+begin
+ ANode := ARoot.Values[SJSON_StartMenu] as TJSONObject;
+ if not Assigned(ANode) then
+ Exit;
+
+ Path := GetJsonValueString(ANode, SJSON_StartMenu_Folder);
+ DoCreate := Path <> '';
+ AddUninstallEntry := Assigned(ANode.Values[SJSON_StartMenu_AddUninstallEntry]) and
+ (ANode.Values[SJSON_StartMenu_AddUninstallEntry] is TJSONTrue);
+ Entries.LoadJSON(ANode);
+end;
+
procedure TPackageStartMenu.SaveIni(AIni: TIniFile);
begin
AIni.WriteString('StartMenu', 'Path', Path);
@@ -888,6 +1131,22 @@ begin
Entries.SaveIni(AIni);
end;
+procedure TPackageStartMenu.SaveJSON(ARoot: TJSONObject);
+var
+ ANode: TJSONObject;
+begin
+ if (Path = '') and (Entries.Count = 0) then
+ Exit;
+
+ ANode := TJSONObject.Create;
+ ARoot.AddPair(SJSON_StartMenu, ANode);
+ if Path <> '' then
+ ANode.AddPair(SJSON_StartMenu_Folder, Path);
+ if AddUninstallEntry then
+ ANode.AddPair(SJSON_StartMenu_AddUninstallEntry, TJSONBool.Create(True));
+ Entries.SaveJSON(ANode);
+end;
+
{-------------------------------------------------------------------------------
- TPackageSubFile -
------------------------------------------------------------------------------}
@@ -983,7 +1242,7 @@ begin
ChildNodes['Name'].NodeValue := Items[i].FileName;
ChildNodes['Description'].NodeValue := Items[i].Description;
ChildNodes['CopyLocation'].NodeValue := Ord(Items[i].CopyLocation);
- ChildNodes['FileType'].NodeValue := ExtractFileExt(Items[i].FFileName);
+ ChildNodes['FileType'].NodeValue := ExtractFileExt(Items[i].FFileName);
end;
end;
end;
@@ -1017,6 +1276,28 @@ begin
end;
end;
+procedure TPackageContentFileList.LoadJSON(ARoot: TJSONObject);
+var
+ subfile: TPackageContentFile;
+ i: Integer;
+ FItems: TJSONArray;
+ FItem: TJSONObject;
+begin
+ Clear;
+ FItems := ARoot.Values[SJSON_Files] as TJSONArray;
+ for i := 0 to FItems.Count - 1 do
+ begin
+ FItem := FItems.Items[i] as TJSONObject;
+
+ subfile := TPackageContentFile.Create(Package);
+ subfile.FileName := GetJsonValueString(FItem, SJSON_Files_Name);
+ subfile.Description := GetJsonValueString(FItem, SJSON_Files_Description);
+ if Assigned(FItem.Values[SJSON_Files_CopyLocation]) then
+ subfile.FCopyLocation := TPackageFileCopyLocation((FItem.Values[SJSON_Files_CopyLocation] as TJSONNumber).AsInt);
+ Add(subfile);
+ end;
+end;
+
procedure TPackageContentFileList.SaveIni(AIni: TIniFile);
var
i: Integer;
@@ -1026,6 +1307,28 @@ begin
Items[i].FileName, Ord(Items[i].CopyLocation)]));
end;
+procedure TPackageContentFileList.SaveJSON(ARoot: TJSONObject);
+var
+ i: Integer;
+ AItems: TJSONArray;
+ AItem: TJSONObject;
+begin
+ if Count = 0 then
+ Exit;
+
+ AItems := TJSONArray.Create;
+ ARoot.AddPair(SJSON_Files, AItems);
+ for i := 0 to Count - 1 do
+ begin
+ AItem := TJSONObject.Create;
+ AItems.Add(AItem);
+ AItem.AddPair(SJSON_Files_Name, Items[i].FileName);
+ AItem.AddPair(SJSON_Files_Description, Items[i].Description);
+ if Items[i].CopyLocation <> pfclPackage then
+ AItem.AddPair(SJSON_Files_CopyLocation, TJSONNumber.Create(Ord(Items[i].CopyLocation)));
+ end;
+end;
+
procedure TPackageContentFileList.Assign(Source: TPackageContentFileList);
var
i: Integer;
@@ -1130,6 +1433,21 @@ begin
end;
end;
+procedure TPackage.LoadJSON;
+var
+ FJSON: TJSONObject;
+begin
+ with TStringStream.Create('', TEncoding.UTF8) do
+ try
+ LoadFromFile(FileName);
+ FJSON := TJSONObject.ParseJSONValue(DataString) as TJSONObject;
+ finally
+ Free;
+ end;
+
+ DoLoadJSON(FJSON);
+end;
+
procedure PackageLoadError(Message: WideString);
begin
raise EPackageInfo.Create(Message);
@@ -1209,13 +1527,21 @@ begin
end;
end;
+procedure TPackage.DoLoadJSON(ARoot: TJSONObject);
+begin
+ StartMenu.LoadJSON(ARoot);
+ Info.LoadJSON(ARoot);
+ Files.LoadJSON(ARoot);
+ Options.LoadJSON(ARoot);
+ Keyboards.LoadJSON(ARoot);
+end;
+
procedure TPackage.DoLoadXML(ARoot: IXMLNode);
var
FVersion: WideString;
begin
-
FVersion := VarToWideStr(ARoot.ChildNodes['System'].ChildNodes['FileVersion'].NodeValue);
- if (FVersion <> SKeymanVersion70) and (FVersion <> SKeymanVersion80) and (FVersion <> SKeymanVersion) then
+ if FVersion <> SKeymanVersion70 then
PackageLoadError('Package file version '+FVersion+' is not recognised.');
StartMenu.LoadXML(ARoot);
@@ -1225,6 +1551,15 @@ begin
Keyboards.LoadXML(ARoot);
end;
+procedure TPackage.DoSaveJSON(ARoot: TJSONObject);
+begin
+ Options.SaveJSON(ARoot);
+ StartMenu.SaveJSON(ARoot);
+ Info.SaveJSON(ARoot);
+ Files.SaveJSON(ARoot);
+ Keyboards.SaveJSON(ARoot);
+end;
+
procedure TPackage.DoSaveXML(ARoot: IXMLNode);
begin
ARoot.ChildNodes['System'].ChildNodes['KeymanDeveloperVersion'].NodeValue := GetVersionString;
@@ -1254,6 +1589,34 @@ begin
end;
end;
+procedure TPackage.SaveJSON;
+var
+ FJSON: TJSONObject;
+ s: TStringList;
+begin
+ if FileExists(FileName) then DeleteFileCleanAttr(FileName); // I4574
+
+ FJSON := TJSONObject.Create;
+ try
+ DoSaveJSON(FJSON);
+ s := TStringList.Create;
+ try
+ PrettyPrintJSON(FJSON, s);
+ with TStringStream.Create(s.Text, TEncoding.UTF8) do
+ try
+ // Use TStringStream to avoid BOM from TStringList
+ SaveToFile(FileName);
+ finally
+ Free;
+ end;
+ finally
+ s.Free;
+ end;
+ finally
+ FJSON.Free;
+ end;
+end;
+
procedure TPackage.SaveXML;
var
doc: IXMLDocument;
@@ -1418,6 +1781,48 @@ begin
end;
end;
+procedure TPackageRegistryKeyList.LoadJSON(ARoot: TJSONObject);
+var
+ i: Integer;
+ ANode: TJSONArray;
+ AKeyNode: TJSONObject;
+ pkr: TPackageRegistryKey;
+ name, value, root, key, valuetype: WideString;
+begin
+ Clear;
+
+ ANode := ARoot.Values[SJSON_Registry] as TJSONArray;
+ if not Assigned(ANode) then Exit;
+
+ for i := 0 to ANode.Count - 1 do
+ begin
+ AKeyNode := ANode.Items[i] as TJSONObject;
+ pkr := TPackageRegistryKey.Create(Package);
+
+ name := GetJsonValueString(AKeyNode, SJSON_Registry_Name);
+ value := GetJsonValueString(AKeyNode, SJSON_Registry_Value);
+ root := GetJsonValueString(AKeyNode, SJSON_Registry_Root);
+ key := GetJsonValueString(AKeyNode, SJSON_Registry_Key);
+
+ if root = 'HKCU' then pkr.Root := HKEY_CURRENT_USER
+ else if root = 'HKLM' then pkr.Root := HKEY_LOCAL_MACHINE
+ else if root = 'HKCR' then pkr.Root := HKEY_CLASSES_ROOT
+ else pkr.Root := HKEY_CURRENT_USER;
+
+ pkr.Name := name;
+ pkr.Key := key;
+
+ valuetype := GetJsonValueString(AKeyNode, SJSON_Registry_ValueType);
+ if valuetype = 'D'
+ then pkr.ValueType := rkvtDWord
+ else pkr.ValueType := rkvtString;
+
+ pkr.Value := value;
+
+ Add(pkr);
+ end;
+end;
+
procedure TPackageRegistryKeyList.SaveIni(AIni: TIniFile);
var
pkr: TPackageRegistryKey;
@@ -1439,6 +1844,38 @@ begin
end;
end;
+procedure TPackageRegistryKeyList.SaveJSON(ARoot: TJSONObject);
+var
+ i: Integer;
+ AKeyNode: TJSONObject;
+ ANode: TJSONArray;
+ pkr: TPackageRegistryKey;
+ root, valuetype: WideString;
+begin
+ if Count = 0 then
+ Exit;
+
+ ANode := TJSONArray.Create;
+ ARoot.AddPair(SJSON_Registry, ANode);
+ for i := 0 to Count - 1 do
+ begin
+ pkr := Items[i];
+ if pkr.Root = HKEY_CURRENT_USER then root := 'HKCU'
+ else if pkr.Root = HKEY_LOCAL_MACHINE then root := 'HKLM'
+ else if pkr.Root = HKEY_CLASSES_ROOT then root := 'HKCR'
+ else root := 'HKCU';
+
+ if pkr.ValueType = rkvtString then valuetype := 'S' else valuetype := 'D';
+ AKeyNode := TJSONObject.Create;
+ ANode.Add(AKeyNode);
+ AKeyNode.AddPair(SJSON_Registry_Root, root);
+ AKeyNode.AddPair(SJSON_Registry_Key, pkr.Key);
+ AKeyNode.AddPair(SJSON_Registry_Name, pkr.Name);
+ AKeyNode.AddPair(SJSON_Registry_ValueType, valuetype);
+ AKeyNode.AddPair(SJSON_Registry_Value, pkr.Value);
+ end;
+end;
+
procedure TPackageRegistryKeyList.LoadXML(ARoot: IXMLNode);
var
i: Integer;
@@ -1630,15 +2067,6 @@ begin
end;
end;
-const
- SPackageKeyboard_Name = 'Name';
- SPackageKeyboard_ID = 'ID';
- SPackageKeyboard_Version = 'Version';
- SPackageKeyboard_OSKFont = 'OSKFont';
- SPackageKeyboard_DisplayFont = 'DisplayFont';
- SPackageKeyboard_Language = 'Language';
- SPackageKeyboard_Language_ID = 'ID';
-
procedure TPackageKeyboardList.LoadIni(AIni: TIniFile);
var
s: TStringList;
@@ -1659,15 +2087,15 @@ begin
Break;
keyboard := TPackageKeyboard.Create(Package);
- keyboard.Name := AIni.ReadString(FSectionName, SPackageKeyboard_Name, '');
- keyboard.ID := AIni.ReadString(FSectionName, SPackageKeyboard_ID, '');
- keyboard.Version := AIni.ReadString(FSectionName, SPackageKeyboard_Version, '1.0');
- keyboard.OSKFont := Package.Files.FromFileNameEx(AIni.ReadString(FSectionName, SPackageKeyboard_OSKFont, ''));
- keyboard.DisplayFont := Package.Files.FromFileNameEx(AIni.ReadString(FSectionName, SPackageKeyboard_DisplayFont, ''));
+ keyboard.Name := AIni.ReadString(FSectionName, SXML_PackageKeyboard_Name, '');
+ keyboard.ID := AIni.ReadString(FSectionName, SXML_PackageKeyboard_ID, '');
+ keyboard.Version := AIni.ReadString(FSectionName, SXML_PackageKeyboard_Version, '1.0');
+ keyboard.OSKFont := Package.Files.FromFileNameEx(AIni.ReadString(FSectionName, SXML_PackageKeyboard_OSKFont, ''));
+ keyboard.DisplayFont := Package.Files.FromFileNameEx(AIni.ReadString(FSectionName, SXML_PackageKeyboard_DisplayFont, ''));
for j := 0 to MaxInt do
begin
- ln := AIni.ReadString(FSectionName, SPackageKeyboard_Language+IntToStr(j), '');
+ ln := AIni.ReadString(FSectionName, SXML_PackageKeyboard_Language+IntToStr(j), '');
if ln = '' then
Break;
FLanguage := TPackageKeyboardLanguage.Create(Package);
@@ -1683,6 +2111,49 @@ begin
end;
end;
+procedure TPackageKeyboardList.LoadJSON(ARoot: TJSONObject);
+var
+ keyboard: TPackageKeyboard;
+ i, j: Integer;
+ ALanguages, ANode: TJSONArray;
+ AKeyboard, ALanguage: TJSONObject;
+ FLanguage: TPackageKeyboardLanguage;
+begin
+ Clear;
+
+ ANode := ARoot.Values[SJSON_Keyboards] as TJSONArray;
+ if not Assigned(ANode) then
+ Exit;
+
+ for i := 0 to ANode.Count - 1 do
+ begin
+ AKeyboard := ANode.Items[i] as TJSONObject;
+
+ keyboard := TPackageKeyboard.Create(Package);
+ keyboard.Name := GetJsonValueString(AKeyboard, SJSON_Keyboard_Name);
+ keyboard.ID := GetJsonValueString(AKeyboard,SJSON_Keyboard_ID);
+ keyboard.Version := GetJsonValueString(AKeyboard, SJSON_Keyboard_Version);
+ keyboard.OSKFont := Package.Files.FromFileNameEx(GetJsonValueString(AKeyboard, SJSON_Keyboard_OSKFont));
+ keyboard.DisplayFont := Package.Files.FromFileNameEx(GetJsonValueString(AKeyboard, SJSON_Keyboard_DisplayFont));
+
+ ALanguages := AKeyboard.Values[SJSON_Keyboard_Languages] as TJSONArray;
+ if Assigned(ALanguages) then
+ begin
+ for j := 0 to ALanguages.Count - 1 do
+ begin
+ ALanguage := ALanguages.Items[j] as TJSONObject;
+
+ FLanguage := TPackageKeyboardLanguage.Create(Package);
+ FLanguage.ID := GetJsonValueString(ALanguage, SJSON_Keyboard_Language_ID);
+ FLanguage.Name := GetJsonValueString(ALanguage, SJSON_Keyboard_Language_Name);
+ keyboard.Languages.Add(FLanguage);
+ end;
+ end;
+
+ Add(keyboard);
+ end;
+end;
+
procedure TPackageKeyboardList.LoadXML(ARoot: IXMLNode);
var
keyboard: TPackageKeyboard;
@@ -1698,11 +2169,11 @@ begin
AKeyboard := ANode.ChildNodes[i];
keyboard := TPackageKeyboard.Create(Package);
- keyboard.Name := VarToStr(AKeyboard.ChildValues[SPackageKeyboard_Name]);
- keyboard.ID := VarToStr(AKeyboard.ChildValues[SPackageKeyboard_ID]);
- keyboard.Version := VarToStr(AKeyboard.ChildValues[SPackageKeyboard_Version]);
- keyboard.OSKFont := Package.Files.FromFileNameEx(VarToStr(AKeyboard.ChildValues[SPackageKeyboard_OSKFont]));
- keyboard.DisplayFont := Package.Files.FromFileNameEx(VarToStr(AKeyboard.ChildValues[SPackageKeyboard_DisplayFont]));
+ keyboard.Name := VarToStr(AKeyboard.ChildValues[SXML_PackageKeyboard_Name]);
+ keyboard.ID := VarToStr(AKeyboard.ChildValues[SXML_PackageKeyboard_ID]);
+ keyboard.Version := VarToStr(AKeyboard.ChildValues[SXML_PackageKeyboard_Version]);
+ keyboard.OSKFont := Package.Files.FromFileNameEx(VarToStr(AKeyboard.ChildValues[SXML_PackageKeyboard_OSKFont]));
+ keyboard.DisplayFont := Package.Files.FromFileNameEx(VarToStr(AKeyboard.ChildValues[SXML_PackageKeyboard_DisplayFont]));
ALanguages := AKeyboard.ChildNodes['Languages'];
if Assigned(ALanguages) then
@@ -1712,7 +2183,7 @@ begin
ALanguage := ALanguages.ChildNodes[j];
FLanguage := TPackageKeyboardLanguage.Create(Package);
- FLanguage.ID := ALanguage.Attributes[SPackageKeyboard_Language_ID];
+ FLanguage.ID := ALanguage.Attributes[SXML_PackageKeyboard_Language_ID];
FLanguage.Name := ALanguage.NodeValue;
keyboard.Languages.Add(FLanguage);
end;
@@ -1730,17 +2201,54 @@ begin
for i := 0 to Count-1 do
begin
FSectionName := 'Keyboard'+IntToStr(i);
- AIni.WriteString(FSectionName, SPackageKeyboard_Name, Items[i].Name);
- AIni.WriteString(FSectionName, SPackageKeyboard_ID, Items[i].ID);
- AIni.WriteString(FSectionName, SPackageKeyboard_Version, Items[i].Version);
+ AIni.WriteString(FSectionName, SXML_PackageKeyboard_Name, Items[i].Name);
+ AIni.WriteString(FSectionName, SXML_PackageKeyboard_ID, Items[i].ID);
+ AIni.WriteString(FSectionName, SXML_PackageKeyboard_Version, Items[i].Version);
if Assigned(Items[i].OSKFont) then
- AIni.WriteString(FSectionName, SPackageKeyboard_OSKFont, ExtractFileName(Items[i].OSKFont.FileName));
+ AIni.WriteString(FSectionName, SXML_PackageKeyboard_OSKFont, Items[i].OSKFont.RelativeFileName);
if Assigned(Items[i].DisplayFont) then
- AIni.WriteString(FSectionName, SPackageKeyboard_DisplayFont, ExtractFileName(Items[i].DisplayFont.FileName));
+ AIni.WriteString(FSectionName, SXML_PackageKeyboard_DisplayFont, Items[i].DisplayFont.RelativeFileName);
for j := 0 to Items[i].Languages.Count-1 do
begin
- AIni.WriteString(FSectionName, SPackageKeyboard_Language+IntToStr(j), Items[i].Languages[j].ID+','+Items[i].Languages[j].Name);
+ AIni.WriteString(FSectionName, SXML_PackageKeyboard_Language+IntToStr(j), Items[i].Languages[j].ID+','+Items[i].Languages[j].Name);
+ end;
+ end;
+end;
+
+procedure TPackageKeyboardList.SaveJSON(ARoot: TJSONObject);
+var
+ i, j: Integer;
+ AKeyboard, ALanguage: TJSONObject;
+ AKeyboards, ALanguages: TJSONArray;
+begin
+ if Count = 0 then
+ Exit;
+
+ AKeyboards := TJSONArray.Create;
+ ARoot.AddPair(SJSON_Keyboards, AKeyboards);
+
+ for i := 0 to Count - 1 do
+ begin
+ AKeyboard := TJSONObject.Create;
+ AKeyboards.Add(AKeyboard);
+
+ AKeyboard.AddPair(SJSON_Keyboard_Name, Items[i].Name);
+ AKeyboard.AddPair(SJSON_Keyboard_ID, Items[i].ID);
+ AKeyboard.AddPair(SJSON_Keyboard_Version, Items[i].Version);
+ if Assigned(Items[i].OSKFont) then
+ AKeyboard.AddPair(SJSON_Keyboard_OSKFont, Items[i].OSKFont.RelativeFileName);
+ if Assigned(Items[i].DisplayFont) then
+ AKeyboard.AddPair(SJSON_Keyboard_DisplayFont, Items[i].DisplayFont.RelativeFileName);
+
+ ALanguages := TJSONArray.Create;
+ AKeyboard.AddPair(SJSON_Keyboard_Languages, ALanguages);
+ for j := 0 to Items[i].Languages.Count - 1 do
+ begin
+ ALanguage := TJSONObject.Create;
+ ALanguages.Add(ALanguage);
+ ALanguage.AddPair(SJSON_Keyboard_Language_Name, Items[i].Languages[j].Name);
+ ALanguage.AddPair(SJSON_Keyboard_Language_ID, Items[i].Languages[j].ID);
end;
end;
end;
@@ -1756,22 +2264,32 @@ begin
begin
AKeyboard := ANode.AddChild('Keyboard');
- AKeyboard.ChildNodes[SPackageKeyboard_Name].NodeValue := Items[i].Name;
- AKeyboard.ChildNodes[SPackageKeyboard_ID].NodeValue := Items[i].ID;
- AKeyboard.ChildNodes[SPackageKeyboard_Version].NodeValue := Items[i].Version;
+ AKeyboard.ChildNodes[SXML_PackageKeyboard_Name].NodeValue := Items[i].Name;
+ AKeyboard.ChildNodes[SXML_PackageKeyboard_ID].NodeValue := Items[i].ID;
+ AKeyboard.ChildNodes[SXML_PackageKeyboard_Version].NodeValue := Items[i].Version;
if Assigned(Items[i].OSKFont) then
- AKeyboard.ChildNodes[SPackageKeyboard_OSKFont].NodeValue := ExtractFileName(Items[i].OSKFont.FileName);
+ AKeyboard.ChildNodes[SXML_PackageKeyboard_OSKFont].NodeValue := Items[i].OSKFont.RelativeFileName;
if Assigned(Items[i].DisplayFont) then
- AKeyboard.ChildNodes[SPackageKeyboard_DisplayFont].NodeValue := ExtractFileName(Items[i].DisplayFont.FileName);
+ AKeyboard.ChildNodes[SXML_PackageKeyboard_DisplayFont].NodeValue := Items[i].DisplayFont.RelativeFileName;
ALanguages := AKeyboard.AddChild('Languages');
for j := 0 to Items[i].Languages.Count - 1 do
begin
ALanguage := ALanguages.AddChild('Language');
ALanguage.NodeValue := Items[i].Languages[j].Name;
- ALanguage.Attributes[SPackageKeyboard_Language_ID] := Items[i].Languages[j].ID;
+ ALanguage.Attributes[SXML_PackageKeyboard_Language_ID] := Items[i].Languages[j].ID;
end;
end;
end;
+function GetJsonValueString(o: TJSONObject; const n: string): string;
+var
+ v: TJSONValue;
+begin
+ v := o.Values[n];
+ if not Assigned(v)
+ then Result := ''
+ else Result := v.Value;
+end;
+
end.
diff --git a/windows/src/global/delphi/general/kmpinffile.pas b/windows/src/global/delphi/general/kmpinffile.pas
index de506a42b4..4159a7afaf 100644
--- a/windows/src/global/delphi/general/kmpinffile.pas
+++ b/windows/src/global/delphi/general/kmpinffile.pas
@@ -59,7 +59,7 @@ begin
end;
{-------------------------------------------------------------------------------
- kmp.inf file format -
+ kmp.inf 5.0 file format -
=================== -
-
[Install] -
diff --git a/windows/src/global/delphi/general/kpsfile.pas b/windows/src/global/delphi/general/kpsfile.pas
index 30d3b49176..f3a798cb39 100644
--- a/windows/src/global/delphi/general/kpsfile.pas
+++ b/windows/src/global/delphi/general/kpsfile.pas
@@ -29,6 +29,7 @@ interface
uses
System.Classes,
System.IniFiles,
+ System.JSON,
System.SysUtils,
Winapi.Windows,
Xml.XMLIntf,
@@ -47,6 +48,8 @@ type
procedure Assign(Source: TPackageOptions); override;
procedure LoadXML(ARoot: IXMLNode); override;
procedure SaveXML(ARoot: IXMLNode); override;
+ procedure LoadJSON(ARoot: TJSONObject); override;
+ procedure SaveJSON(ARoot: TJSONObject); override;
property MSIFileName: WideString read FMSIFileName write FMSIFileName;
property MSIOptions: WideString read FMSIOptions write FMSIOptions; // I3126
end;
@@ -58,6 +61,8 @@ type
procedure Import(AIni: TIniFile); override;
procedure DoLoadXML(ARoot: IXMLNode); override;
procedure DoSaveXML(ARoot: IXMLNode); override;
+ procedure DoLoadJSON(ARoot: TJSONObject); override;
+ procedure DoSaveJSON(ARoot: TJSONObject); override;
public
function KPSOptions: TKPSOptions;
constructor Create;
@@ -79,6 +84,12 @@ uses
utilfiletypes,
utilsystem;
+const
+ SJSON_Options = 'options';
+ SJSON_Options_MSIFileName = 'msiFilename';
+ SJSON_Options_MSIOptions = 'msiOptions';
+ SJSON_Strings = 'strings';
+
{ TKPSFile }
constructor TKPSFile.Create;
@@ -102,6 +113,24 @@ begin
Files[i].FileName := ExpandFileNameEx(FileName, Files[i].FileName);
end;
+procedure TKPSFile.DoLoadJSON(ARoot: TJSONObject);
+var
+ i: Integer;
+ ANode: TJSONObject;
+begin
+ inherited;
+
+ if FileName <> '' then
+ for i := 0 to Files.Count - 1 do
+ Files[i].FileName := ExpandFileNameEx(FileName, Files[i].FileName);
+
+ FStrings.Clear;
+ ANode := ARoot.Values[SJSON_Strings] as TJSONObject;
+ if ANode <> nil then
+ for i := 0 to ANode.Count - 1 do
+ FStrings.Add(ANode.Pairs[i].JsonString.Value + '=' + ANode.Pairs[i].Value);
+end;
+
procedure TKPSFile.DoLoadXML(ARoot: IXMLNode);
var
i: Integer;
@@ -131,6 +160,32 @@ begin
Files[i].FileName := ExpandFileNameEx(FileName, Files[i].FileName);
end;
+procedure TKPSFile.DoSaveJSON;
+var
+ i: Integer;
+ ANode: TJSONObject;
+begin
+ Options.FileVersion := SKeymanVersion70;
+
+ if FileName <> '' then
+ for i := 0 to Files.Count - 1 do
+ Files[i].FileName := ExtractRelativePath(FileName, Files[i].FileName);
+
+ inherited;
+
+ if FileName <> '' then
+ for i := 0 to Files.Count - 1 do
+ Files[i].FileName := ExpandFileNameEx(FileName, Files[i].FileName);
+
+ if FStrings.Count > 0 then
+ begin
+ ANode := TJSONObject.Create;
+ ARoot.AddPair(SJSON_Strings, ANode);
+ for i := 0 to FStrings.Count - 1 do
+ ANode.AddPair(FStrings.Names[i], FStrings.ValueFromIndex[i]);
+ end;
+end;
+
procedure TKPSFile.DoSaveXML(ARoot: IXMLNode);
var
i: Integer;
@@ -290,6 +345,16 @@ begin
FMSIOptions := (Source as TKPSOptions).MSIOptions; // I3126
end;
+procedure TKPSOptions.LoadJSON(ARoot: TJSONObject);
+var
+ FOptions: TJSONObject;
+begin
+ inherited;
+ FOptions := ARoot.Values[SJSON_Options] as TJSONObject;
+ FMSIFileName := GetJsonValueString(FOptions, SJSON_Options_MSIFileName);
+ FMSIOptions := GetJsonValueString(FOptions, SJSON_Options_MSIOptions);
+end;
+
procedure TKPSOptions.LoadXML(ARoot: IXMLNode);
begin
inherited;
@@ -297,6 +362,18 @@ begin
FMSIOptions := VarToWideStr(ARoot.ChildNodes['Options'].ChildNodes['MSIOptions'].NodeValue); // I3126
end;
+procedure TKPSOptions.SaveJSON(ARoot: TJSONObject);
+var
+ FOptions: TJSONObject;
+begin
+ inherited;
+ FOptions := ARoot.Values[SJSON_Options] as TJSONObject;
+ if FMSIFileName <> '' then
+ FOptions.AddPair(SJSON_Options_MSIFileName, FMSIFileName);
+ if FMSIOptions <> '' then
+ FOptions.AddPair(SJSON_Options_MSIOptions, FMSIOptions);
+end;
+
procedure TKPSOptions.SaveXML(ARoot: IXMLNode);
begin
inherited;
diff --git a/windows/src/unit-tests/package-info/PackageInfoTest.pas b/windows/src/unit-tests/package-info/PackageInfoTest.pas
index caf939eabb..c5798e7fd5 100644
--- a/windows/src/unit-tests/package-info/PackageInfoTest.pas
+++ b/windows/src/unit-tests/package-info/PackageInfoTest.pas
@@ -26,6 +26,9 @@ type
[Test]
procedure TestXMLInOut;
+ [Test]
+ procedure TestJSONInOut;
+
[Test]
procedure TestRoundTrip;
@@ -116,6 +119,16 @@ begin
try
p1.FileName := DataPath + 'test2.out.inf';
p1.LoadIni;
+ p1.FileName := DataPath + 'test2.out.json';
+ p1.SaveJSON;
+ finally
+ p1.Free;
+ end;
+
+ p1 := TKpsFile.Create;
+ try
+ p1.FileName := DataPath + 'test2.out.json';
+ p1.LoadJSON;
p1.FileName := DataPath + 'test2.out.xml';
p1.SaveXML;
finally
@@ -143,6 +156,16 @@ begin
try
p1.FileName := DataPath + 'test3.out.xml';
p1.LoadXML;
+ p1.FileName := DataPath + 'test3.out.json';
+ p1.SaveJSON;
+ finally
+ p1.Free;
+ end;
+
+ p1 := TKpsFile.Create;
+ try
+ p1.FileName := DataPath + 'test3.out.json';
+ p1.LoadJSON;
p1.FileName := DataPath + 'test3.out.inf';
p1.SaveIni;
finally
@@ -169,6 +192,23 @@ begin
DoCompare(DataPath + 'test.xml', DataPath + 'test4.out.xml');
end;
+procedure TPackageInfoTest.TestJSONInOut;
+var
+ p1: TPackage;
+begin
+ p1 := TKpsFile.Create;
+ try
+ p1.FileName := DataPath + 'test.json';
+ p1.LoadJSON;
+ p1.FileName := DataPath + 'test5.out.json';
+ p1.SaveJSON;
+ finally
+ p1.Free;
+ end;
+
+ DoCompare(DataPath + 'test.json', DataPath + 'test5.out.json');
+end;
+
procedure TPackageInfoTest.DoCompare(f1, f2: string);
var
k1, k2: TKpsFile;
@@ -177,14 +217,14 @@ begin
k2 := TKpsFile.Create;
try
k1.FileName := f1;
- if SameText(ExtractFileExt(f1), '.xml')
- then k1.LoadXml
- else k1.LoadIni;
+ if SameText(ExtractFileExt(f1), '.xml') then k1.LoadXml
+ else if SameText(ExtractFileExt(f1), '.json') then k1.LoadJSON
+ else k1.LoadIni;
k2.FileName := f2;
- if SameText(ExtractFileExt(f2), '.xml')
- then k2.LoadXml
- else k2.LoadIni;
+ if SameText(ExtractFileExt(f2), '.xml') then k2.LoadXml
+ else if SameText(ExtractFileExt(f2), '.json') then k2.LoadJSON
+ else k2.LoadIni;
DoCompareKpsFiles(f1, f2, k1, k2);
finally
diff --git a/windows/src/unit-tests/package-info/PackageInfoTestSuite.dpr b/windows/src/unit-tests/package-info/PackageInfoTestSuite.dpr
index a02499a8e0..5565088043 100644
--- a/windows/src/unit-tests/package-info/PackageInfoTestSuite.dpr
+++ b/windows/src/unit-tests/package-info/PackageInfoTestSuite.dpr
@@ -8,7 +8,6 @@ uses
{$IFDEF TESTINSIGHT}
TestInsight.DUnitX,
{$ENDIF }
-
PackageInfo in '..\..\global\delphi\general\PackageInfo.pas',
utilfiletypes in '..\..\global\delphi\general\utilfiletypes.pas',
utilstr in '..\..\global\delphi\general\utilstr.pas',
@@ -23,11 +22,11 @@ uses
GetOsVersion in '..\..\global\delphi\general\GetOsVersion.pas',
kpsfile in '..\..\global\delphi\general\kpsfile.pas',
PackageFileFormats in '..\..\global\delphi\general\PackageFileFormats.pas',
-
DUnitX.Loggers.Console,
DUnitX.Loggers.Xml.NUnit,
DUnitX.TestFramework,
- PackageInfoTest in 'PackageInfoTest.pas';
+ PackageInfoTest in 'PackageInfoTest.pas',
+ JsonUtil in '..\..\global\delphi\general\JsonUtil.pas';
var
runner : ITestRunner;
diff --git a/windows/src/unit-tests/package-info/PackageInfoTestSuite.dproj b/windows/src/unit-tests/package-info/PackageInfoTestSuite.dproj
index 8cb444285f..ab6a27c15e 100644
--- a/windows/src/unit-tests/package-info/PackageInfoTestSuite.dproj
+++ b/windows/src/unit-tests/package-info/PackageInfoTestSuite.dproj
@@ -89,21 +89,22 @@
MainSource
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Cfg_2
Base
@@ -515,6 +516,7 @@
+ False
True
False
diff --git a/windows/src/unit-tests/package-info/test/test.json b/windows/src/unit-tests/package-info/test/test.json
new file mode 100644
index 0000000000..6437c8acd9
--- /dev/null
+++ b/windows/src/unit-tests/package-info/test/test.json
@@ -0,0 +1,187 @@
+{
+ "system" : {
+ "keymanDeveloperVersion" : "0.0.0.0",
+ "fileVersion" : "7.0"
+ },
+ "options" : {
+ "readmeFile" : "readme.htm",
+ "graphicFile" : "sideimage.bmp"
+ },
+ "startMenu" : {
+ "folder" : "GFF Amharic Keyboard",
+ "addUninstallEntry" : true,
+ "items" : [
+ {
+ "name" : "Welcome",
+ "filename" : "..\\..\\..\\gff\\gff_amh_7\\source\\welcome.htm"
+ },
+ {
+ "name" : "Amharic Keyboard Manual (Amharic)",
+ "filename" : "AmharicTyping-Amharic.pdf"
+ },
+ {
+ "name" : "Amharic Keyboard Manual (English)",
+ "filename" : "AmharicTyping-English.pdf"
+ },
+ {
+ "name" : "OFL (Abyssinica SIL Licence)",
+ "filename" : "OFL.txt"
+ },
+ {
+ "name" : "GPL (WashRa + Free Serif Licence)",
+ "filename" : "GNU-GPLv2.txt"
+ }
+ ]
+ },
+ "info" : {
+ "website" : {
+ "description" : "http://keyboards.ethiopic.org",
+ "url" : "http://keyboards.ethiopic.org"
+ },
+ "version" : {
+ "description" : "8.0.0"
+ },
+ "name" : {
+ "description" : "GFF Amharic Keyboard"
+ },
+ "copyright" : {
+ "description" : "Creative Commons Attribution 3.0"
+ },
+ "author" : {
+ "description" : "The Ge'ez Frontier Foundation",
+ "url" : "mailto:keyboards@ethiopic.org"
+ }
+ },
+ "files" : [
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\readme.htm",
+ "description" : "File readme.htm"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\sideimage.bmp",
+ "description" : "File sideimage.bmp"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\gff\\gff_amh_7\\source\\AmharicTyping-Amharic.pdf",
+ "description" : "File AmharicTyping-Amharic.pdf"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\gff\\gff_amh_7\\source\\AmharicTyping-English.pdf",
+ "description" : "File AmharicTyping-English.pdf"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\OFL.txt",
+ "description" : "File OFL.txt"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\GNU-GPLv2.txt",
+ "description" : "File GNU-GPLv2.txt"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\gff\\gff_amh_7\\source\\welcome.htm",
+ "description" : "File Welcome.htm"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\AbyssinicaSIL-R.ttf",
+ "description" : "Font Abyssinica SIL"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\fantuwua.ttf",
+ "description" : "Font Ethiopic Fantuwua"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\FreeSerif.otf",
+ "description" : "Font Free Serif"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\goffer.ttf",
+ "description" : "Font Ethiopic Yigezu Bisrat Goffer"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\hiwua.ttf",
+ "description" : "Font Ethiopic Hiwua"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\jiret.ttf",
+ "description" : "Font Ethiopia Jiret"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\jiretsl.ttf",
+ "description" : "Font Ethiopia Jiret Slant"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\tint.ttf",
+ "description" : "Font Ethiopic Tint"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\washrab.ttf",
+ "description" : "Font Ethiopic WashRa Bold"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\washrabsl.ttf",
+ "description" : "Font Ethiopic WashRa Bold Slant"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\washrasb.ttf",
+ "description" : "Font Ethiopic WashRa SemiBold"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\washrasbsl.ttf",
+ "description" : "Font Ethiopic WashRa SemiBold Slant"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\wookianos.ttf",
+ "description" : "Font Ethiopic Wookianos"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\yebse.ttf",
+ "description" : "Font Ethiopic Yebse"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\gff\\gff_amh_7\\build\\gff_amh_7.kvk",
+ "description" : "File gff_amh_7.kvk"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\gff\\gff_amh_7\\build\\gff_amh_7.kmx",
+ "description" : "Keyboard Amharic"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\gff\\gff_ethiopic_7\\build\\gff_ethiopic_7.kvk",
+ "description" : "File gff_ethiopic_7.kvk"
+ },
+ {
+ "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\gff\\gff_ethiopic_7\\build\\gff_ethiopic_7.kmx",
+ "description" : "Keyboard Ethiopic - (Language Neutral)"
+ }
+ ],
+ "keyboards" : [
+ {
+ "name" : "Amharic",
+ "id" : "gff_amh_7",
+ "version" : "8.0",
+ "oskFont" : "..\\..\\..\\shared\\fonts\\geez\\wookianos.ttf",
+ "displayFont" : "..\\..\\..\\shared\\fonts\\geez\\fantuwua.ttf",
+ "languages" : [
+ {
+ "name" : "Amharic",
+ "id" : "amh"
+ },
+ {
+ "name" : "Ge'ez",
+ "id" : "gez"
+ }
+ ]
+ },
+ {
+ "name" : "Ethiopic Language Neutral",
+ "id" : "gff_ethiopic_7",
+ "version" : "8.0",
+ "languages" : [
+ {
+ "name" : "Ge'ez",
+ "id" : "gez"
+ }
+ ]
+ }
+ ]
+}