feat(developer): description, font and example metadata in packages

Adds info.description, options.fonts, keyboard.examples metadata to
.kps and kmp.json files, updates schemas, and Typescript compiler.
This commit is contained in:
Marc Durdin 2023-08-14 09:23:20 +07:00
parent 25717d1d1c
commit 675f6bc91e
9 changed files with 401 additions and 21 deletions

View file

@ -74,6 +74,7 @@
<xs:element minOccurs="0" maxOccurs="1" name="Copyright" type="km-info" />
<xs:element minOccurs="0" maxOccurs="1" name="Author" type="km-info" />
<xs:element minOccurs="0" maxOccurs="1" name="WebSite" type="km-info" />
<xs:element minOccurs="0" maxOccurs="1" name="Description" type="km-info" />
</xs:all>
</xs:complexType>
</xs:element>
@ -110,6 +111,7 @@
<xs:element minOccurs="0" maxOccurs="1" name="OSKFont" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" name="DisplayFont" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" name="Languages" type="km-keyboard-languages" />
<xs:element minOccurs="0" maxOccurs="1" name="Examples" type="km-keyboard-examples" />
</xs:all>
</xs:complexType>
</xs:element>
@ -209,4 +211,17 @@
</xs:sequence>
</xs:complexType>
<xs:complexType name="km-keyboard-examples">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="Example">
<xs:complexType>
<xs:attribute name="ID" type="xs:string" />
<xs:attribute name="Keys" type="xs:string" />
<xs:attribute name="Text" type="xs:string" />
<xs:attribute name="Note" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>

View file

@ -19,6 +19,11 @@ export interface KmpJsonFileOptions {
executeProgram?: string;
msiFilename?: string;
msiOptions?: string;
/**
* List of all font files, grouped by font family
* Compiler extracts font metadata from .ttf,.otf,.woff,.woff2
*/
fonts?: {[family:string]: string[]};
}
export interface KmpJsonFileInfo {
@ -27,6 +32,11 @@ export interface KmpJsonFileInfo {
name?: KmpJsonFileInfoItem;
copyright?: KmpJsonFileInfoItem;
author?: KmpJsonFileInfoItem;
/**
* A Markdown description of the keyboard, intended for use in websites
* referencing the keyboard
*/
description?: KmpJsonFileInfoItem;
}
export interface KmpJsonFileInfoItem {
@ -59,6 +69,7 @@ export interface KmpJsonFileKeyboard {
displayFont?: string;
rtl?: boolean;
languages?: KmpJsonFileLanguage[];
examples?: KmpJsonFileExample[];
}
export interface KmpJsonFileStartMenu {
@ -74,3 +85,22 @@ export interface KmpJsonFileStartMenuItem {
icon?: string;
location?: string;
}
export interface KmpJsonFileExample {
/**
* BCP 47 identifier for the example
*/
id: string;
/**
* A space-separated list of keys, modifiers indicated with "+", spacebar is "space", plus key is "shift+=" or "plus"
*/
keys: string;
/**
* The text that would be generated by typing those keys
*/
text?: string;
/**
* A short description of what the text means or represents
*/
note?: string;
}

View file

@ -51,6 +51,7 @@ export interface KpsFileInfo {
author?: KpsFileInfoItem;
webSite?: KpsFileInfoItem;
version?: KpsFileInfoItem;
description?: KpsFileInfoItem;
}
export interface KpsFileInfoItem {
@ -64,8 +65,11 @@ export interface KpsFileContentFiles {
export interface KpsFileContentFile {
name: string;
/** @deprecated */
description: string;
/** @deprecated */
copyLocation: string;
/** @deprecated */
fileType: string;
}
@ -96,6 +100,7 @@ export interface KpsFileKeyboard {
displayFont?: string;
rTL?: string;
languages?: KpsFileLanguages;
examples?: KpsFileLanguageExamples;
}
export interface KpsFileKeyboards {
@ -124,3 +129,31 @@ export interface KpsFileStrings {
//TODO: validate this structure
string: string[] | string;
}
export interface KpsFileLanguageExamples {
example: KpsFileLanguageExample | KpsFileLanguageExample[];
}
/**
* An example key sequence intended to demonstrate how the keyboard works
*/
export interface KpsFileLanguageExample {
$: {
/**
* BCP 47 identifier for the example
*/
ID: string;
/**
* A space-separated list of keys, modifiers indicated with "+", spacebar is "space", plus key is "shift+=" or "plus"
*/
keys: string;
/**
* The text that would be generated by typing those keys
*/
text?: string;
/**
* A short description of what the text means or represents
*/
note?: string;
}
}

View file

@ -67,25 +67,20 @@ export class KmpCompiler {
// Fill in additional fields
//
let keys: [keyof KpsFile.KpsFileOptions, keyof KmpJsonFile.KmpJsonFileOptions][] = [
['executeProgram','executeProgram'],
['graphicFile', 'graphicFile'],
['msiFileName','msiFilename'],
['msiOptions', 'msiOptions'],
['readMeFile', 'readmeFile']
];
if(kps.options) {
for (let [src,dst] of keys) {
if (kps.options[src]) {
if(dst == 'graphicFile' || dst == 'readmeFile') {
kmp.options[dst] = /[/\\]?([^/\\]*)$/.exec(kps.options[src])[1];
} else {
kmp.options[dst] = kps.options[src];
}
}
kmp.options.executeProgram = kps.options?.executeProgram || undefined;
if(kps.options.graphicFile) {
kmp.options.graphicFile = /[/\\]?([^/\\]*)$/.exec(kps.options.graphicFile)[1];
}
kmp.options.msiFilename = kps.options.msiFileName;
kmp.options.msiOptions = kps.options.msiOptions;
if(kps.options.readMeFile) {
kmp.options.readmeFile = /[/\\]?([^/\\]*)$/.exec(kps.options.readMeFile)[1];
}
}
// TODO: this.addFontMetadata();
//
// Add basic metadata
//
@ -139,7 +134,12 @@ export class KmpCompiler {
rtl:keyboard.rTL == 'True' ? true : undefined,
languages: keyboard.languages ?
this.kpsLanguagesToKmpLanguages(this.arrayWrap(keyboard.languages.language) as KpsFile.KpsFileLanguage[]) :
[]
[],
examples: keyboard.examples ?
(this.arrayWrap(keyboard.examples.example) as KpsFile.KpsFileLanguageExample[]).map(
e => ({id: e.$.ID, keys: e.$.keys, text: e.$.text, note: e.$.note})
) as KmpJsonFile.KmpJsonFileExample[] :
undefined
}));
}
@ -237,13 +237,14 @@ export class KmpCompiler {
['copyright','copyright'],
['name','name'],
['version','version'],
['webSite','website']
['webSite','website'],
['description','description'],
];
for (let [src,dst] of keys) {
if (info[src]) {
ni[dst] = {description: info[src]._ ?? (typeof info[src] == 'string' ? info[src].toString() : '')};
if(info[src].$ && info[src].$.URL) ni[dst].url = info[src].$.URL;
ni[dst] = {description: (info[src]._ ?? (typeof info[src] == 'string' ? info[src].toString() : '').trim())};
if(info[src].$ && info[src].$.URL) ni[dst].url = info[src].$.URL.trim();
}
}
@ -264,8 +265,6 @@ export class KmpCompiler {
return language.map((element) => { return { name: element._, id: element.$.ID } });
};
private stripUndefined(o: any) {
for(const key in o) {
if(o[key] === undefined) {

File diff suppressed because one or more lines are too long

Binary file not shown.

View file

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<Package>
<System>
<KeymanDeveloperVersion>15.0.266.0</KeymanDeveloperVersion>
<FileVersion>7.0</FileVersion>
</System>
<Options>
<ExecuteProgram></ExecuteProgram>
<ReadMeFile>readme.htm</ReadMeFile>
<GraphicFile>splash.gif</GraphicFile>
<MSIFileName></MSIFileName>
<MSIOptions></MSIOptions>
<FollowKeyboardVersion/>
</Options>
<StartMenu>
<Folder></Folder>
<Items/>
</StartMenu>
<Info>
<Name URL="">Khmer Angkor</Name>
<Copyright URL="">© 2015-2022 SIL International</Copyright>
<Author URL="mailto:makara_sok@sil.org">Makara Sok</Author>
<Version URL=""></Version>
<WebSite URL="https://keyman.com/keyboards/khmer_angkor">https://keyman.com/keyboards/khmer_angkor</WebSite>
<Description># Khmer Angkor
Khmer Unicode keyboard layout based on the NiDA keyboard layout. Automatically corrects many common keying errors, including:
* Using wrong vowel combination
* Typing clusters out of order
* Typing wrong mark for consonant shifters
* And more!</Description>
</Info>
<Files>
<File>
<Name>khmer_angkor.js</Name>
<Description>File khmer_angkor.js</Description>
<CopyLocation>0</CopyLocation>
<FileType>.js</FileType>
</File>
<File>
<Name>khmer_angkor.kvk</Name>
<Description>File khmer_angkor.kvk</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kvk</FileType>
</File>
<File>
<Name>khmer_angkor.kmx</Name>
<Description>Keyboard Khmer Angkor</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
</File>
<File>
<Name>welcome\keyboard_layout.png</Name>
<Description>File keyboard_layout.png</Description>
<CopyLocation>0</CopyLocation>
<FileType>.png</FileType>
</File>
<File>
<Name>welcome\welcome.htm</Name>
<Description>File welcome.htm</Description>
<CopyLocation>0</CopyLocation>
<FileType>.htm</FileType>
</File>
<File>
<Name>..\shared\fonts\khmer\mondulkiri\FONTLOG.txt</Name>
<Description>File FONTLOG.txt</Description>
<CopyLocation>0</CopyLocation>
<FileType>.txt</FileType>
</File>
<File>
<Name>..\shared\fonts\khmer\mondulkiri\Mondulkiri-R.ttf</Name>
<Description>Font Khmer Mondulkiri</Description>
<CopyLocation>0</CopyLocation>
<FileType>.ttf</FileType>
</File>
<File>
<Name>..\shared\fonts\khmer\mondulkiri\OFL.txt</Name>
<Description>File OFL.txt</Description>
<CopyLocation>0</CopyLocation>
<FileType>.txt</FileType>
</File>
<File>
<Name>..\shared\fonts\khmer\mondulkiri\OFL-FAQ.txt</Name>
<Description>File OFL-FAQ.txt</Description>
<CopyLocation>0</CopyLocation>
<FileType>.txt</FileType>
</File>
<File>
<Name>welcome\KAK_Documentation_EN.pdf</Name>
<Description>File KAK_Documentation_EN.pdf</Description>
<CopyLocation>0</CopyLocation>
<FileType>.pdf</FileType>
</File>
<File>
<Name>welcome\KAK_Documentation_KH.pdf</Name>
<Description>File KAK_Documentation_KH.pdf</Description>
<CopyLocation>0</CopyLocation>
<FileType>.pdf</FileType>
</File>
<File>
<Name>readme.htm</Name>
<Description>File readme.htm</Description>
<CopyLocation>0</CopyLocation>
<FileType>.htm</FileType>
</File>
<File>
<Name>welcome\image002.png</Name>
<Description>File image002.png</Description>
<CopyLocation>0</CopyLocation>
<FileType>.png</FileType>
</File>
<File>
<Name>..\shared\fonts\khmer\busrakbd\khmer_busra_kbd.ttf</Name>
<Description>Font KhmerBusraKbd</Description>
<CopyLocation>0</CopyLocation>
<FileType>.ttf</FileType>
</File>
<File>
<Name>splash.gif</Name>
<Description>File splash.gif</Description>
<CopyLocation>0</CopyLocation>
<FileType>.gif</FileType>
</File>
</Files>
<Keyboards>
<Keyboard>
<Name>Khmer Angkor</Name>
<ID>khmer_angkor</ID>
<Version>1.3</Version>
<OSKFont>..\shared\fonts\khmer\busrakbd\khmer_busra_kbd.ttf</OSKFont>
<DisplayFont>..\shared\fonts\khmer\mondulkiri\Mondulkiri-R.ttf</DisplayFont>
<Languages>
<Language ID="km">Central Khmer (Khmer, Cambodia)</Language>
</Languages>
<Examples>
<Example ID="km" keys="x j m E r" text="ខ្មែរ" note="Name of language"/>
<Example ID="km" keys="B j r H r a C a N a c k j r k m j B u C a" text="ព្រះរាជាណាចក្រកម្ពុជា" note="'Kingdom of Cambodia'"/>
</Examples>
</Keyboard>
</Keyboards>
<Strings/>
</Package>

View file

@ -0,0 +1,131 @@
{
"system": {
"keymanDeveloperVersion": "16.0.60.0",
"fileVersion": "7.0"
},
"options": {
"readmeFile": "readme.htm",
"graphicFile": "splash.gif"
},
"info": {
"name": {
"description": "Khmer Angkor"
},
"copyright": {
"description": "\u00A9 2015-2022 SIL International"
},
"author": {
"description": "Makara Sok",
"url": "mailto:makara_sok@sil.org"
},
"version": {
"description": "1.3"
},
"website": {
"description": "https://keyman.com/keyboards/khmer_angkor",
"url": "https://keyman.com/keyboards/khmer_angkor"
},
"description": {
"description": "# Khmer Angkor\r\n\r\nKhmer Unicode keyboard layout based on the NiDA keyboard layout. Automatically corrects many common keying errors, including:\r\n\r\n* Using wrong vowel combination\r\n* Typing clusters out of order\r\n* Typing wrong mark for consonant shifters\r\n* And more!"
}
},
"files": [
{
"name": "khmer_angkor.js",
"description": "File khmer_angkor.js"
},
{
"name": "khmer_angkor.kvk",
"description": "File khmer_angkor.kvk"
},
{
"name": "khmer_angkor.kmx",
"description": "Keyboard Khmer Angkor"
},
{
"name": "keyboard_layout.png",
"description": "File keyboard_layout.png"
},
{
"name": "welcome.htm",
"description": "File welcome.htm"
},
{
"name": "FONTLOG.txt",
"description": "File FONTLOG.txt"
},
{
"name": "Mondulkiri-R.ttf",
"description": "Font Khmer Mondulkiri"
},
{
"name": "OFL.txt",
"description": "File OFL.txt"
},
{
"name": "OFL-FAQ.txt",
"description": "File OFL-FAQ.txt"
},
{
"name": "KAK_Documentation_EN.pdf",
"description": "File KAK_Documentation_EN.pdf"
},
{
"name": "KAK_Documentation_KH.pdf",
"description": "File KAK_Documentation_KH.pdf"
},
{
"name": "readme.htm",
"description": "File readme.htm"
},
{
"name": "image002.png",
"description": "File image002.png"
},
{
"name": "khmer_busra_kbd.ttf",
"description": "Font KhmerBusraKbd"
},
{
"name": "splash.gif",
"description": "File splash.gif"
},
{
"name": "kmp.inf",
"description": "Package information"
},
{
"name": "kmp.json",
"description": "Package information (JSON)"
}
],
"keyboards": [
{
"name": "Khmer Angkor",
"id": "khmer_angkor",
"version": "1.3",
"oskFont": "khmer_busra_kbd.ttf",
"displayFont": "Mondulkiri-R.ttf",
"languages": [
{
"name": "Central Khmer (Khmer, Cambodia)",
"id": "km"
}
],
"examples": [
{
"id": "km",
"keys": "x j m E r",
"text": "ខ្មែរ",
"note": "Name of language"
},
{
"id": "km",
"keys": "B j r H r a C a N a c k j r k m j B u C a",
"text": "ព្រះរាជាណាចក្រកម្ពុជា",
"note": "'Kingdom of Cambodia'"
}
]
}
]
}

View file

@ -155,6 +155,34 @@ describe('KmpCompiler', function () {
assert.deepEqual(kmpJsonActual, kmpJsonFixture);
});
it(`should support .kps 17.0 metadata correctly`, function () {
callbacks.clear();
const kpsPath = makePathToFixture('kmp_2.0', 'khmer_angkor.kps');
const kmpJsonRefPath = makePathToFixture('kmp_2.0', 'kmp.json');
debugger;
let kmpJsonActual = kmpCompiler.transformKpsToKmpObject(kpsPath);
if(kmpJsonActual == null) {
callbacks.printMessages();
assert.isNotNull(kmpJsonActual);
}
let kmpJsonFixture = JSON.parse(fs.readFileSync(kmpJsonRefPath, 'utf-8'));
assert.isNotNull(kmpJsonFixture);
// Blank out system.keymanDeveloperVersion which will vary
kmpJsonActual.system.keymanDeveloperVersion = '-';
kmpJsonFixture.system.keymanDeveloperVersion = '-';
// Strip file paths to basename for the actual (this is currently part of the
// zip phase and not unit testable without refactoring)
for(const file of kmpJsonActual.files) {
file.name = path.basename(file.name);
}
assert.deepEqual(kmpJsonActual, kmpJsonFixture);
});
/*
* Testing Warnings and Errors
*/