feat(developer): kmc-convert-start to use deadkeys(read&map them from .ukelele file)

This commit is contained in:
Sabine 2024-10-29 10:44:37 +01:00
parent 2d6785d984
commit f21a7d3f11
2 changed files with 152 additions and 28 deletions

View file

@ -65,9 +65,9 @@ export class KeylayoutToKmnConverter {
*/
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 use var a = document.getElementById("target");
*/
const options = {
@ -75,42 +75,136 @@ export class KeylayoutToKmnConverter {
attributeNamePrefix: '@_', // to access the attribute
};
const xmlFile = readFileSync(`${process.cwd()}/data/MySample.keylayout`, 'utf8');
//const xmlFile = readFileSync(`${process.cwd()}/data/MySample.keylayout`, 'utf8');
const xmlFile = readFileSync(`${process.cwd()}/data/My_dk_Keyboard.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_inLayer = jsonObj.keyboard.keyMapSet[0].keyMap[0].key.length
const nrOfKeys_inLayer = jsonObj.keyboard.keyMapSet[0].keyMap[0].key.length // will all have the same length later
// create Object:any for storing Uint8tarray will be array[Uint8Array] Uint8Array might have several numbers (unicode)
// 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
const modifier_array: any[] = [] // array holding all MODIFIER strings e.g. "anyShift caps anyOption" -why not put modifiers into Uint8Array along with values of keys of layer
const keys_action_all_Layers :any[] = [] // array holding all values with ACTION attribute (needed for finding deadkeys)
const deadkeys_all_Layers :any[] = [] // array holding all DEADKEYS for each mod state â, ê, ,....
const data_all_Layers :any[] = [] // array holds ALL DATA: keys, output, action, dedkeys,...
// loop through all ss-combin
for (let j = 0; j < nrOfStates; j++) {
// get modifier list e.g. "anyshift caps? anyOption"
modifier_array[j] = jsonObj.keyboard.modifierMap.keyMapSelect[j].modifier['@_keys']
// create a new keys_in_Layer (type Uint8tarray)
const keys_single_Layer :Uint8Array[] = []
// create a new array of keys_in_Layer (type Uint8tarray)
const keys_output_One_Layer :Uint8Array[] = []
const keys_action_One_Layer :Uint8Array[] = []
for (let i = 0; i < nrOfKeys_inLayer; i++) {
// .........................................................
// get all keys for attribute "output" ( y,c,b,...)
// .........................................................
for (let i = 0; i < jsonObj.keyboard.keyMapSet[0].keyMap[j].key.length; 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' ]);
keys_output_One_Layer[i] = new TextEncoder().encode(jsonObj.keyboard.keyMapSet[0].keyMap[j].key[i]['@_output' ]);
}
// .........................................................
// get all keys for attribute "action" ( ^,a,e,i,...)
// .........................................................
if(jsonObj.keyboard.keyMapSet[0].keyMap[j].key[i]['@_action'] !== "\0" ) {
keys_action_One_Layer[i] = new TextEncoder().encode(jsonObj.keyboard.keyMapSet[0].keyMap[j].key[i]['@_action' ]);
}
}
keys_all_Layers[j] = keys_single_Layer
// .........................................................
// create array of "action" and array of "output"
// .........................................................
data_all_Layers[j] = keys_output_One_Layer // save all @output data ( will be used for conversion, write...)
keys_action_all_Layers[j] = keys_action_One_Layer // save all @action data ( will be used for deadkeys)
}
// 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;
console.log('modifiers: ', modifier_array)
console.log('********************************************************')
// .........................................................
// create array of "deadkey" / "deadkey names"
// .........................................................
const dk_vec2d:string[][] = []
for (let jj= 0; jj< jsonObj.keyboard.actions.action.length; jj++ ) {
// if there is a "next" attribute-> it is a dk
if(jsonObj.keyboard.actions.action[jj].when['@_next'] !== undefined ) {
const vec1d:string[] = []
vec1d.push(jsonObj.keyboard.actions.action[jj].when['@_next'])
vec1d.push(jsonObj.keyboard.actions.action[jj]['@_id'])
dk_vec2d.push(vec1d)
}
}
console.log('dk_vec2d',dk_vec2d )
console.log('********************************************************')
// .........................................................
// create array of deadkey base ^ ´ `
// .........................................................
const dk :string[] = [];
for ( let k=0; k < dk_vec2d.length; k++) {
dk.push(dk_vec2d[k][1] )
}
console.log('dk: ', dk)
console.log('********************************************************')
// .........................................................
// create array of "deadkeyables"
// .........................................................
const deadkeyables :string[][]=[]
for ( let j=0; j<jsonObj.keyboard.keyMapSet[0].keyMap.length; j++ ) {
const deadkeyables_one :string[]=[]
for ( let i=0; i<jsonObj.keyboard.keyMapSet[0].keyMap[0].key.length; i++ ) {
const resulting_character = new TextDecoder().decode(keys_action_all_Layers[j][i])
if( resulting_character !=="")
deadkeyables_one.push(resulting_character)
}
deadkeyables.push(deadkeyables_one)
// filter out dk -> plain deadkeyables ( a,^, e,i,´,u => a,e,i,u)
deadkeyables[j] = deadkeyables[j].filter( function( el ) {
return dk.indexOf( el ) < 0;
});
}
console.log('deadkeyables per modifier state: ', deadkeyables)
console.log('********************************************************')
// .........................................................
// create deadkeys-array
// .........................................................
// for all 'dk'
for ( let k=0; k<dk_vec2d.length;k++) {
const deadkeys_One_dk :any[] = []
// for all 'action'
for (let jj= 0; jj< jsonObj.keyboard.actions.action.length; jj++ ) {
// for all 'when'
for (let ii= 0; ii< jsonObj.keyboard.actions.action[jj].when.length; ii++ ) {
if (jsonObj.keyboard.actions.action[jj].when[ii]['@_state'] === dk_vec2d[k][0] ) {
deadkeys_One_dk.push(jsonObj.keyboard.actions.action[jj].when[ii]['@_output'])
}
}
}
deadkeys_all_Layers.push(deadkeys_One_dk)
}
console.log('deadkeys per modifier state: ', deadkeys_all_Layers )
console.log('********************************************************')
// is there a better way??
data_all_Layers[nrOfStates] = modifier_array // add all normal key data
data_all_Layers.push(dk_vec2d) // add dk-mapping ( dk1 <-> '^' )
data_all_Layers.push(dk) // add plain dk ( '^', '´','`')
data_all_Layers.push(deadkeyables) // add char that can be modified with dk ( a,e,i,o,u)
data_all_Layers.push(deadkeys_all_Layers) // add modified keys ( â,ê,î,ô,û)
return data_all_Layers
return((data_all_Layers.length === nrOfStates + 5) && data_all_Layers[0].length === nrOfKeys_inLayer) ? data_all_Layers : null;
}
/*public fill_singleLayer_AllKeys(index_kmn:any):any {
return kk.index_kmn
}*/
/**
* @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
@ -121,7 +215,7 @@ export class KeylayoutToKmnConverter {
const data_kmn :any[] = [];
for (let j = 0; j < data_ukelele.length-1 ; j++) {
for (let j = 0; j < data_ukelele.length-5 ; j++) {
const keys_singleLayer :Uint8Array[] = []
// now convert & sort
@ -139,14 +233,21 @@ export class KeylayoutToKmnConverter {
// keyman VK-code == mac VK
// later more here...
//console.log("data_ukelele[j][1]", data_ukelele[0][1] , '--> keys_singleLayer[24]',keys_singleLayer[24] )
data_kmn[j] = keys_singleLayer
}
//copy the modifier states
//copy the modifier states __ better idea?
//data_kmn[data_ukelele.length - 1] = data_ukelele[data_ukelele.length - 1]
data_kmn[data_ukelele.length - 5] = data_ukelele[data_ukelele.length - 5]
data_kmn[data_ukelele.length - 4] = data_ukelele[data_ukelele.length - 4]
data_kmn[data_ukelele.length - 3] = data_ukelele[data_ukelele.length - 3]
data_kmn[data_ukelele.length - 2] = data_ukelele[data_ukelele.length - 2]
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
return data_kmn
//return (data_kmn.length === data_ukelele.length ) ? data_kmn : null
}
/** @brief write data to file
@ -154,6 +255,7 @@ export class KeylayoutToKmnConverter {
* @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',
@ -165,6 +267,9 @@ export class KeylayoutToKmnConverter {
'K_xDF', 'K_OEM_102'
]
// *************************************************************
// **** write stores *******************************************
// *************************************************************
let data = "\n"
data += "c\n"
data += "c Keyman keyboard generated by kmn-convert\n"
@ -180,19 +285,25 @@ export class KeylayoutToKmnConverter {
data += "group(main) using keys\n"
data += "\n"
// *************************************************************
// **** write rules ********************************************
// *************************************************************
// if caps is used we need to add NCAPS in kmn-file
console.log("kmn_array",kmn_array)
let isCAPSused = false
for (let i= 0 ; i < kmn_array[kmn_array.length-1].length; i++) {
if( String((kmn_array[kmn_array.length-1][i]).indexOf("caps") !== -1 ) ) {
if ( String((kmn_array[kmn_array.length-1][i]).indexOf("caps") !== -1 ) ) {
isCAPSused = true
}
}
for (let i = 0; i < kmn_array.length-1; i++) {
//for (let i = 0; i < kmn_array.length-1; i++) {
for (let i = 0; i < kmn_array.length-5; 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], isCAPSused)
//const label_modifier = this.create_modifier(kmn_array[kmn_array.length-1][i], isCAPSused)
const label_modifier = this.create_modifier(kmn_array[kmn_array.length-5][i], isCAPSused)
// get the character that will be written as result in kmn-file
const resulting_character = new TextDecoder().decode(kmn_array[i][j])
@ -201,10 +312,23 @@ export class KeylayoutToKmnConverter {
if (resulting_character !== '')
data += `+ [` + label_modifier + ' ' + kmn_Key_Name[j] + `] > \'` + resulting_character +'\'\n'
}
data += '\n'
}
// *************************************************************
// **** write deadkeys *****************************************
// *************************************************************
// val vk dk
/*+ [K_BKQUOTE] > dk(005e)
+ [K_EQUAL] > dk(00b4)
+ [SHIFT K_EQUAL] > dk(0060)*/
data += "match > use(deadkeys)\n\n"
data += "group(deadkeys)\n"
data += "store(" + ".....)\n"
data += '\n'
writeFileSync("data/MyResult.kmn", data, { flag: "w"})
console.log(" _S2 write finished\n")
@ -295,4 +419,4 @@ export class KeylayoutToKmnConverter {
}
return unique_Modifier.join(" ").replace(/\s+/g, " ").trim().toUpperCase()
}
}
}

View file

@ -33,9 +33,9 @@ describe('KeylayoutToKmnConverter', function() {
it('should throw on all elements loaded', async function () {
// some keys, no deadkeys
const inputFilename = makePathToFixture('../data/Mysample.keylayout');
//const inputFilename = makePathToFixture('../data/Mysample.keylayout');
// all keys, some deadkeys
//const inputFilename = makePathToFixture('../data/My_dk_Keyboard.keylayout');
const inputFilename = makePathToFixture('../data/My_dk_Keyboard.keylayout');
const outputFilename = makePathToFixture('../data/MyResult.kmn');