mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-11 16:37:28 +00:00
feat(developer): kmc-convert-first draft of read/convert/write using Uint8Array
This commit is contained in:
parent
cf29264e60
commit
632f2fc5ce
2 changed files with 173 additions and 64 deletions
|
|
@ -77,6 +77,17 @@
|
|||
<key code="27" output="ß"/>
|
||||
<key code="47" output=""/>
|
||||
</keyMap>
|
||||
<!--anyShift caps? anyOption-->
|
||||
<keyMap index="3">
|
||||
<key code="0" output="A"/>
|
||||
<key code="6" output="Y"/>
|
||||
<key code="8" output="C"/>
|
||||
<key code="11" output="B"/>
|
||||
<key code="12" output="Q"/>
|
||||
<key code="20" output="3"/>
|
||||
<key code="27" output="ß"/>
|
||||
<key code="47" output=""/>
|
||||
</keyMap>
|
||||
</keyMapSet>
|
||||
<keyMapSet id="JIS">
|
||||
<keyMap index="0" baseMapSet="ANSI" baseIndex="0">
|
||||
|
|
|
|||
|
|
@ -21,6 +21,13 @@ export class KeylayoutToKmnConverter {
|
|||
// be available as class properties
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief run read/convert/write
|
||||
* @param inputFilename the ukelele .keylayout-file to be converted
|
||||
* @param outputFilename the resulting keyman .kmn-file
|
||||
* @param binaryData ... _S2
|
||||
* @return
|
||||
*/
|
||||
async run(inputFilename: string, outputFilename: string, binaryData: Uint8Array): Promise<ConverterToKmnArtifacts> {
|
||||
|
||||
if(!inputFilename || !outputFilename || !binaryData) {
|
||||
|
|
@ -51,86 +58,101 @@ export class KeylayoutToKmnConverter {
|
|||
|
||||
throw new Error('Not finished yet');
|
||||
}
|
||||
// ............................................................................................................
|
||||
// ............................................................................................................
|
||||
// ............................................................................................................
|
||||
|
||||
/**
|
||||
* @brief read filename ( a .keylayout-file) and write contents into Uint8Array keys_all_Layers
|
||||
* @param filename the ukelele .keylayout-file to be converted
|
||||
* @return in case of success Uint8Array keys_all_Layers; else null
|
||||
*/
|
||||
public read(filename: string):Uint8Array[] {
|
||||
/*
|
||||
- // _S2 TODO and what about deadkeys???
|
||||
- // _S2 TODO which format to use in output ? + [K_A] > 'a' (character code) or + [K_A] > U+0061 (virt Keycode)
|
||||
- // _S2 which stores?
|
||||
- // _S2 how to use names of shiftstates*/
|
||||
/*
|
||||
// _S2 TODO and what about deadkeys???
|
||||
// _S2 TODO which format to use in output ? + [K_A] > 'a' (character code) or + [K_A] > U+0061 (virt Keycode)
|
||||
// _S2 which stores?
|
||||
*/
|
||||
|
||||
const options = {
|
||||
ignoreAttributes: false,
|
||||
attributeNamePrefix: '@_', // you have to assign this to use this to access the attribute
|
||||
attributeNamePrefix: '@_', // to access the attribute
|
||||
};
|
||||
|
||||
|
||||
//xml file from https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ms762271(v=vs.85)
|
||||
const xmlFile = readFileSync(`${process.cwd()}/data/MySample.keylayout`, 'utf8');
|
||||
const parser = new XMLParser(options);
|
||||
const jsonObj = parser.parse(xmlFile); // get plain Object
|
||||
|
||||
const nrOfStates = jsonObj.keyboard.keyMapSet[0].keyMap.length
|
||||
const nrOfKeys = jsonObj.keyboard.keyMapSet[0].keyMap[0].key.length
|
||||
const nrOfKeys_inLayer = jsonObj.keyboard.keyMapSet[0].keyMap[0].key.length
|
||||
|
||||
// create Object:any for storing Uint8tarray will be array[Uint8Array] Uint8Array might have several numbers (unicode)
|
||||
const Keys_all_Layers :any[] = []
|
||||
// all keys for all layers
|
||||
const keys_all_Layers :any[] = []
|
||||
const modifier_array: any[] = [] // why not put modifiers into Uint8Array along with values of keys of layer
|
||||
|
||||
for (let j = 0; j < nrOfStates; j++) {
|
||||
|
||||
modifier_array[j] = jsonObj.keyboard.modifierMap.keyMapSelect[j].modifier['@_keys']
|
||||
|
||||
// create a new keys_in_Layer (type Uint8tarray)
|
||||
const keys_in_Layer :Uint8Array[] = []
|
||||
for (let i = 0; i < nrOfKeys; i++) {
|
||||
if(jsonObj.keyboard.keyMapSet[0].keyMap[j].key[i]['@_output'] !== "\0" ) {
|
||||
// textencoder converts string -> bytes ( 'A' -> [ 65 ], '☺' -> [ 226, 152, 186 ])
|
||||
// textencoder is of Uint8Array(1) for A and Uint8Array(3) for ☺
|
||||
const textencoder = new TextEncoder().encode(jsonObj.keyboard.keyMapSet[0].keyMap[j].key[i]['@_output' ]);
|
||||
keys_in_Layer[i] = textencoder
|
||||
}
|
||||
const keys_single_Layer :Uint8Array[] = []
|
||||
|
||||
for (let i = 0; i < nrOfKeys_inLayer; i++) {
|
||||
if(jsonObj.keyboard.keyMapSet[0].keyMap[j].key[i]['@_output'] !== "\0" ) {
|
||||
// textencoder converts string -> bytes ( 'A' -> [ 65 ], '☺' -> [ 226, 152, 186 ])
|
||||
// textencoder is of Uint8Array(1) for A and Uint8Array(3) for ☺
|
||||
keys_single_Layer[i] = new TextEncoder().encode(jsonObj.keyboard.keyMapSet[0].keyMap[j].key[i]['@_output' ]);
|
||||
}
|
||||
// add [nrOfStates] Objects:Uint8tarray to Keys_all_Layers
|
||||
Keys_all_Layers[j] = keys_in_Layer
|
||||
}
|
||||
|
||||
console.log(" _S2 read finished\n")
|
||||
return(Keys_all_Layers.length === nrOfStates && Keys_all_Layers[0].length === nrOfKeys) ? Keys_all_Layers : null;
|
||||
}
|
||||
|
||||
// ............................................................................................................
|
||||
|
||||
public convert(inArray: any[]):any[] {
|
||||
|
||||
const outArray :any[] = [];
|
||||
const nrOfMaxKeys = 50
|
||||
|
||||
for (let j = 0; j < inArray.length ; j++) {
|
||||
const outkeys_in_Layer :Uint8Array[] = []
|
||||
|
||||
// initialize keys
|
||||
for (let i = 0; i < nrOfMaxKeys; i++) {
|
||||
outkeys_in_Layer[i] = new TextEncoder().encode("\0");
|
||||
}
|
||||
|
||||
// now convert
|
||||
outkeys_in_Layer[0] = inArray[j][0];
|
||||
outkeys_in_Layer[24] = inArray[j][1];
|
||||
outkeys_in_Layer[2] = inArray[j][2];
|
||||
outkeys_in_Layer[1] = inArray[j][3];
|
||||
outkeys_in_Layer[16] = inArray[j][4];
|
||||
outkeys_in_Layer[29] = inArray[j][5];
|
||||
outkeys_in_Layer[38] = inArray[j][6];
|
||||
outkeys_in_Layer[46] = inArray[j][7];
|
||||
|
||||
outArray[j] = outkeys_in_Layer
|
||||
keys_all_Layers[j] = keys_single_Layer
|
||||
}
|
||||
console.log(" _S2 convert finished\n")
|
||||
return (outArray.length === inArray.length ) ? outArray : null
|
||||
// add the modifiers-> better idea?
|
||||
keys_all_Layers[nrOfStates] = modifier_array
|
||||
|
||||
// Keys_all_Layers needs to have nrOfStates entries + 1 for modifiers
|
||||
return((keys_all_Layers.length === nrOfStates + 1) && keys_all_Layers[0].length === nrOfKeys_inLayer) ? keys_all_Layers : null;
|
||||
}
|
||||
|
||||
public write(writeArray: any[]):boolean {
|
||||
/**
|
||||
* @brief convert data of .keylayout-file to kmn-file This will convert/rename modifiers, position of Keys and deadkeys and save into an array
|
||||
* @param data_ukelele (Uint8Array) data of the ukelele .keylayout-file
|
||||
* @return outArray Uint8Array keys_all_Layers, the converted data for kmn-files if all layers have been converted; else null
|
||||
*/
|
||||
public convert(data_ukelele: any[]):any[] {
|
||||
// sorting order: alphabetically OK?
|
||||
|
||||
const KeyArray = [
|
||||
const data_kmn :any[] = [];
|
||||
|
||||
for (let j = 0; j < data_ukelele.length-1 ; j++) {
|
||||
const keys_singleLayer :Uint8Array[] = []
|
||||
|
||||
// now convert & sort
|
||||
keys_singleLayer[0] = data_ukelele[j][0];
|
||||
keys_singleLayer[24] = data_ukelele[j][1];
|
||||
keys_singleLayer[2] = data_ukelele[j][2];
|
||||
keys_singleLayer[1] = data_ukelele[j][3];
|
||||
keys_singleLayer[16] = data_ukelele[j][4];
|
||||
keys_singleLayer[29] = data_ukelele[j][5];
|
||||
keys_singleLayer[38] = data_ukelele[j][6];
|
||||
keys_singleLayer[46] = data_ukelele[j][7];
|
||||
// later more here...
|
||||
|
||||
data_kmn[j] = keys_singleLayer
|
||||
}
|
||||
|
||||
//copy the modifier states
|
||||
data_kmn[data_ukelele.length - 1] = data_ukelele[data_ukelele.length - 1]
|
||||
console.log(" _S2 convert finished\n", data_kmn
|
||||
)
|
||||
|
||||
return (data_kmn.length === data_ukelele.length ) ? data_kmn : null
|
||||
}
|
||||
|
||||
/** @brief write data to file
|
||||
* @param kmn_array the array holding keyboard data
|
||||
* @return true if data has been written; false if not
|
||||
*/
|
||||
public write(kmn_array: any[]):boolean {
|
||||
const kmn_Key_Name = [
|
||||
'K_A','K_B','K_C','K_D','K_E','K_F','K_G','K_H','K_I','K_J','K_K','K_L','K_M','K_N','K_O','K_P','K_Q','K_R','K_S','K_T','K_U','K_V','K_W','K_X','K_Y','K_Z',
|
||||
'K_0','K_1','K_2','K_3','K_4','K_5','K_6','K_7','K_8','K_9',
|
||||
'K_SPACE',
|
||||
|
|
@ -156,16 +178,92 @@ public write(writeArray: any[]):boolean {
|
|||
data += "group(main) using keys\n"
|
||||
data += "\n"
|
||||
|
||||
for (let i = 0; i < writeArray.length; i++) {
|
||||
for (let j = 0; j < writeArray[0].length; j++) {
|
||||
const textdecoder = new TextDecoder().decode(writeArray[i][j])
|
||||
if (textdecoder !== '\0' )
|
||||
data += `+ [` + KeyArray[j] + `] > \'` + textdecoder +'\'\n'
|
||||
for (let i = 0; i < kmn_array.length-1; i++) {
|
||||
for (let j = 0; j < kmn_array[0].length; j++) {
|
||||
|
||||
// get the modifier for the layer
|
||||
const label_modifier = this.create_modifier(kmn_array[kmn_array.length-1][i])
|
||||
// get the character that will be written as result in kmn-file
|
||||
const resulting_character = new TextDecoder().decode(kmn_array[i][j])
|
||||
|
||||
// remove if-stmt later, only here for better visability
|
||||
if (resulting_character !== '')
|
||||
data += `+ [` + label_modifier + ' ' + kmn_Key_Name[j] + `] > \'` + resulting_character +'\'\n'
|
||||
|
||||
}
|
||||
data += '\n'
|
||||
}
|
||||
|
||||
writeFileSync("data/MyResult.kmn", data, { flag: "w"})
|
||||
console.log(" _S2 write finished\n")
|
||||
return true;
|
||||
|
||||
if( data.length > 0)
|
||||
return true;
|
||||
else
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief check if search_modifier is available in modifier_array
|
||||
* @param search_modifier the value that will be searched for in modifier_array
|
||||
* @param modifier_array the array holding all modifiers used
|
||||
* @return true if search_modifier is found; false if not
|
||||
*/
|
||||
public isInArray(search_modifier:string, modifier_array: string[]):boolean {
|
||||
for (let i =0; i < modifier_array.length; i++) {
|
||||
if (search_modifier === String(modifier_array[i]))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief create modifier in kmn-style from modifier of .keylayout-file
|
||||
* @param keylayout_modifier the modifier value used in the .keylayout-file
|
||||
* @return kmn_modifier the modifier value used in the .kmn-file
|
||||
*/
|
||||
public create_modifier(keylayout_modifier:any):string {
|
||||
let add_modifier = ""
|
||||
let kmn_modifier = ""
|
||||
|
||||
// copy each modifier into element of array modifier_state
|
||||
const modifier_state: string[] = keylayout_modifier.split(" ");
|
||||
|
||||
// TODO review these conditions-> what else do I need?
|
||||
// TODO spelling entries of .keylayout( uppercase/lowercase, control<->ctrl,...)
|
||||
// TODO anyOption?
|
||||
// opt?+ LOPT?+ ROPT? -> what will be result??
|
||||
|
||||
for (let i = 0; i < modifier_state.length; i++) {
|
||||
|
||||
if ( String(modifier_state[i]) === "anyOption") add_modifier = "OPT "
|
||||
else if ( String(modifier_state[i]) === "anyShift") add_modifier = "SHIFT "
|
||||
else if ( String(modifier_state[i]) === "anyControl") add_modifier = "CTRL "
|
||||
|
||||
else if ( String(modifier_state[i]) === "anyOption?") add_modifier = "" // does anyOption?... happen?
|
||||
else if ( String(modifier_state[i]) === "anyShift?") add_modifier = ""
|
||||
else if ( String(modifier_state[i]) === "anyControl?") add_modifier = ""
|
||||
|
||||
else if( (String(modifier_state[i]) === "shift?" ) && this.isInArray('rshift',modifier_state)) add_modifier = "RSHIFT "
|
||||
else if( (String(modifier_state[i]) === "shift?" ) && this.isInArray('lshift',modifier_state)) add_modifier = "LSHIFT "
|
||||
else if( (String(modifier_state[i]) === "shift?" ) && this.isInArray('rshift',modifier_state) && this.isInArray('lshift',modifier_state)) add_modifier = "SHIFT "
|
||||
|
||||
else if( (String(modifier_state[i]) === "option?" ) && this.isInArray('rOption',modifier_state)) add_modifier = "ROPT "
|
||||
else if( (String(modifier_state[i]) === "option?" ) && this.isInArray('lOption',modifier_state)) add_modifier = "LOPT "
|
||||
else if( (String(modifier_state[i]) === "option?" ) && this.isInArray('rOption',modifier_state) && this.isInArray('lOption',modifier_state)) add_modifier = "OPT "
|
||||
|
||||
else if( (String(modifier_state[i]) === "ctrl?" ) && this.isInArray('rControl',modifier_state)) add_modifier = "RCTRL "
|
||||
else if( (String(modifier_state[i]) === "ctrl?" ) && this.isInArray('lControl',modifier_state)) add_modifier = "LCTRL "
|
||||
else if( (String(modifier_state[i]) === "ctrl?" ) && this.isInArray('rControl',modifier_state) && this.isInArray('lControl',modifier_state)) add_modifier = "CTRL "
|
||||
|
||||
else if( String(modifier_state[i]) === "caps?") add_modifier = "" // capital letters or not?
|
||||
else if( String(modifier_state[i]) === "shift?") add_modifier = ""
|
||||
else if( String(modifier_state[i]) === "ctrl?") add_modifier = ""
|
||||
|
||||
else add_modifier = String(modifier_state[i])
|
||||
|
||||
kmn_modifier += add_modifier
|
||||
}
|
||||
return kmn_modifier.trim().toUpperCase()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue