mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-11 16:37:28 +00:00
feat(developer): kmc-convert-start to use deadkeys(read&map them from .ukelele file)
This commit is contained in:
parent
2d6785d984
commit
f21a7d3f11
2 changed files with 152 additions and 28 deletions
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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');
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue