| 1 | <?php
|
|---|
| 2 | # FIXME Compose auslesen
|
|---|
| 3 |
|
|---|
| 4 | # Funktionen
|
|---|
| 5 | # -------------------------------
|
|---|
| 6 |
|
|---|
| 7 | # Unicode Codepoint Funktion von http://iki.fi/hsivonen/php-utf8/
|
|---|
| 8 | # (GPL)
|
|---|
| 9 | function utf8ToUnicode(&$str)
|
|---|
| 10 | {
|
|---|
| 11 | $mState = 0; // cached expected number of octets after the current octet
|
|---|
| 12 | // until the beginning of the next UTF8 character sequence
|
|---|
| 13 | $mUcs4 = 0; // cached Unicode character
|
|---|
| 14 | $mBytes = 1; // cached expected number of octets in the current sequence
|
|---|
| 15 |
|
|---|
| 16 | $out = array();
|
|---|
| 17 |
|
|---|
| 18 | $len = strlen($str);
|
|---|
| 19 | for($i = 0; $i < $len; $i++) {
|
|---|
| 20 | $in = ord($str{$i});
|
|---|
| 21 | if (0 == $mState) {
|
|---|
| 22 | // When mState is zero we expect either a US-ASCII character or a
|
|---|
| 23 | // multi-octet sequence.
|
|---|
| 24 | if (0 == (0x80 & ($in))) {
|
|---|
| 25 | // US-ASCII, pass straight through.
|
|---|
| 26 | $out[] = $in;
|
|---|
| 27 | $mBytes = 1;
|
|---|
| 28 | } else if (0xC0 == (0xE0 & ($in))) {
|
|---|
| 29 | // First octet of 2 octet sequence
|
|---|
| 30 | $mUcs4 = ($in);
|
|---|
| 31 | $mUcs4 = ($mUcs4 & 0x1F) << 6;
|
|---|
| 32 | $mState = 1;
|
|---|
| 33 | $mBytes = 2;
|
|---|
| 34 | } else if (0xE0 == (0xF0 & ($in))) {
|
|---|
| 35 | // First octet of 3 octet sequence
|
|---|
| 36 | $mUcs4 = ($in);
|
|---|
| 37 | $mUcs4 = ($mUcs4 & 0x0F) << 12;
|
|---|
| 38 | $mState = 2;
|
|---|
| 39 | $mBytes = 3;
|
|---|
| 40 | } else if (0xF0 == (0xF8 & ($in))) {
|
|---|
| 41 | // First octet of 4 octet sequence
|
|---|
| 42 | $mUcs4 = ($in);
|
|---|
| 43 | $mUcs4 = ($mUcs4 & 0x07) << 18;
|
|---|
| 44 | $mState = 3;
|
|---|
| 45 | $mBytes = 4;
|
|---|
| 46 | } else if (0xF8 == (0xFC & ($in))) {
|
|---|
| 47 | /* First octet of 5 octet sequence.
|
|---|
| 48 | *
|
|---|
| 49 | * This is illegal because the encoded codepoint must be either
|
|---|
| 50 | * (a) not the shortest form or
|
|---|
| 51 | * (b) outside the Unicode range of 0-0x10FFFF.
|
|---|
| 52 | * Rather than trying to resynchronize, we will carry on until the end
|
|---|
| 53 | * of the sequence and let the later error handling code catch it.
|
|---|
| 54 | */
|
|---|
| 55 | $mUcs4 = ($in);
|
|---|
| 56 | $mUcs4 = ($mUcs4 & 0x03) << 24;
|
|---|
| 57 | $mState = 4;
|
|---|
| 58 | $mBytes = 5;
|
|---|
| 59 | } else if (0xFC == (0xFE & ($in))) {
|
|---|
| 60 | // First octet of 6 octet sequence, see comments for 5 octet sequence.
|
|---|
| 61 | $mUcs4 = ($in);
|
|---|
| 62 | $mUcs4 = ($mUcs4 & 1) << 30;
|
|---|
| 63 | $mState = 5;
|
|---|
| 64 | $mBytes = 6;
|
|---|
| 65 | } else {
|
|---|
| 66 | /* Current octet is neither in the US-ASCII range nor a legal first
|
|---|
| 67 | * octet of a multi-octet sequence.
|
|---|
| 68 | */
|
|---|
| 69 | return false;
|
|---|
| 70 | }
|
|---|
| 71 | } else {
|
|---|
| 72 | // When mState is non-zero, we expect a continuation of the multi-octet
|
|---|
| 73 | // sequence
|
|---|
| 74 | if (0x80 == (0xC0 & ($in))) {
|
|---|
| 75 | // Legal continuation.
|
|---|
| 76 | $shift = ($mState - 1) * 6;
|
|---|
| 77 | $tmp = $in;
|
|---|
| 78 | $tmp = ($tmp & 0x0000003F) << $shift;
|
|---|
| 79 | $mUcs4 |= $tmp;
|
|---|
| 80 | if (0 == --$mState) {
|
|---|
| 81 | /* End of the multi-octet sequence. mUcs4 now contains the final
|
|---|
| 82 | * Unicode codepoint to be output
|
|---|
| 83 | *
|
|---|
| 84 | * Check for illegal sequences and codepoints.
|
|---|
| 85 | */
|
|---|
| 86 | // From Unicode 3.1, non-shortest form is illegal
|
|---|
| 87 | if (((2 == $mBytes) && ($mUcs4 < 0x0080)) ||
|
|---|
| 88 | ((3 == $mBytes) && ($mUcs4 < 0x0800)) ||
|
|---|
| 89 | ((4 == $mBytes) && ($mUcs4 < 0x10000)) ||
|
|---|
| 90 | (4 < $mBytes) ||
|
|---|
| 91 | // From Unicode 3.2, surrogate characters are illegal
|
|---|
| 92 | (($mUcs4 & 0xFFFFF800) == 0xD800) ||
|
|---|
| 93 | // Codepoints outside the Unicode range are illegal
|
|---|
| 94 | ($mUcs4 > 0x10FFFF)) {
|
|---|
| 95 | return false;
|
|---|
| 96 | }
|
|---|
| 97 | if (0xFEFF != $mUcs4) {
|
|---|
| 98 | // BOM is legal but we don't want to output it
|
|---|
| 99 | $out[] = $mUcs4;
|
|---|
| 100 | }
|
|---|
| 101 | //initialize UTF8 cache
|
|---|
| 102 | $mState = 0;
|
|---|
| 103 | $mUcs4 = 0;
|
|---|
| 104 | $mBytes = 1;
|
|---|
| 105 | }
|
|---|
| 106 | } else {
|
|---|
| 107 | /* ((0xC0 & (*in) != 0x80) && (mState != 0))
|
|---|
| 108 | *
|
|---|
| 109 | * Incomplete multi-octet sequence.
|
|---|
| 110 | */
|
|---|
| 111 | return false;
|
|---|
| 112 | }
|
|---|
| 113 | } # if state ...
|
|---|
| 114 | } # for-loop
|
|---|
| 115 | return $out;
|
|---|
| 116 | }
|
|---|
| 117 |
|
|---|
| 118 | # Liest alle Tasten, samt zugehörigen Zeichen aus der Referenz
|
|---|
| 119 | function getNeoKeys () {
|
|---|
| 120 | # Referenz laden
|
|---|
| 121 | $reference = file_get_contents('http://neo-layout.org/svn/A-REFERENZ-A/neo20.txt');
|
|---|
| 122 |
|
|---|
| 123 | # Haupttastatur finden
|
|---|
| 124 | preg_match('/┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────────┐\n(.*)\n└──────┴──────┴──────┴──────────────────────────────────────┴──────┴──────┴──────┴──────┘/s', $reference, $found);
|
|---|
| 125 | # Tastaturreihen aufspalten
|
|---|
| 126 | $rows = preg_split('/\n├.*\n/', $found[1]);
|
|---|
| 127 |
|
|---|
| 128 | $n = 1;
|
|---|
| 129 | # Für jede Reihe:
|
|---|
| 130 | foreach ($rows as $row) {
|
|---|
| 131 | $sub_rows = preg_split('/\n/', $row);
|
|---|
| 132 |
|
|---|
| 133 | # Finde Zeichen in beiden Zeilen.
|
|---|
| 134 | # U030F ist ein combining-character und tritt zusammen mit einem Leerzeichen auf, damit es angezeigt wird
|
|---|
| 135 | preg_match_all('/│(.) (\x{030F} |.) (.)(?=│)/u', $sub_rows[0], $r1);
|
|---|
| 136 | preg_match_all('/│(.) (.) (.)(?=│)/u', $sub_rows[1], $r2);
|
|---|
| 137 |
|
|---|
| 138 | # Für jede Taste:
|
|---|
| 139 | for ($i = 0; $i < count($r1[0]); $i++) {
|
|---|
| 140 | # Überspringe Enter-Taste, die nicht dazugehört
|
|---|
| 141 | if ($r1[2][$i] == "\xE2\x86\xB2" && $i == 11) {
|
|---|
| 142 | $n--;
|
|---|
| 143 | continue;
|
|---|
| 144 | }
|
|---|
| 145 | # Lade die Zeichen der beiden Zeilen in das Array, geordnet nach ihrer Ebene
|
|---|
| 146 | $key[$n+$i.'_1'] = $r2[1][$i];
|
|---|
| 147 | $key[$n+$i.'_2'] = $r1[1][$i];
|
|---|
| 148 | $key[$n+$i.'_3'] = $r2[2][$i];
|
|---|
| 149 | $key[$n+$i.'_4'] = $r1[2][$i];
|
|---|
| 150 | $key[$n+$i.'_5'] = $r2[3][$i];
|
|---|
| 151 | $key[$n+$i.'_6'] = $r1[3][$i];
|
|---|
| 152 | }
|
|---|
| 153 | $n += $i;
|
|---|
| 154 | }
|
|---|
| 155 | return $key;
|
|---|
| 156 | }
|
|---|
| 157 |
|
|---|
| 158 | # Nutzt utf8ToUnicode(), um einen Unicode-Codepoint zu finden, der dann hexadezimal formatiert zurückgegeben wird.
|
|---|
| 159 | function getUnicodeCodepoint ($char) {
|
|---|
| 160 | $codepoints = utf8ToUnicode($char);
|
|---|
| 161 | if (!$codepoints[0]) return false;
|
|---|
| 162 | return strtoupper(str_pad(dechex($codepoints[0]), 4, "0", STR_PAD_LEFT));
|
|---|
| 163 | }
|
|---|
| 164 |
|
|---|
| 165 |
|
|---|
| 166 |
|
|---|
| 167 | # Unicode Daten für die Zeichen-Benennung holen.
|
|---|
| 168 | # ----------------------------------------------
|
|---|
| 169 | ini_set('memory_limit', '128M');
|
|---|
| 170 | # Sehr hoher Speicherbedarf, deshalb wäre es eigentlich besser UnicodeData in einer Datenbank zu haben
|
|---|
| 171 | $ucdfull = file_get_contents('http://www.unicode.org/Public/UNIDATA/UnicodeData.txt');
|
|---|
| 172 | $ucdfull = explode("\n", $ucdfull);
|
|---|
| 173 | # Array erstellen, um leichter auf die Daten zugreifen zu können
|
|---|
| 174 | $ucd = array();
|
|---|
| 175 | foreach ($ucdfull as $chardata) {
|
|---|
| 176 | $chardata = explode(';', $chardata);
|
|---|
| 177 | $ucd[$chardata[0]] = $chardata;
|
|---|
| 178 | }
|
|---|
| 179 |
|
|---|
| 180 | # Erzeugbare Zeichen suchen
|
|---|
| 181 | # ---------------------------------------------
|
|---|
| 182 |
|
|---|
| 183 | # Alle Tasten mit ihren Zeichen aus der Referenz laden
|
|---|
| 184 | $keys = getNeoKeys();
|
|---|
| 185 |
|
|---|
| 186 | # Diese Zeichen müssen noch angehäng werden, da die Leertaste nicht eingelesen wird
|
|---|
| 187 | # Position der Zeichen ist irrelevant, siehe weiter unten
|
|---|
| 188 | $keys[] = ' '; # SPACE
|
|---|
| 189 | $keys[] = ' '; # NO-BREAK SPACE
|
|---|
| 190 | $keys[] = ' '; # NARROW NO-BREAK SPACE
|
|---|
| 191 |
|
|---|
| 192 | # Duplikate entfernen und alles sortieren
|
|---|
| 193 | $chars = array_unique($keys);
|
|---|
| 194 |
|
|---|
| 195 |
|
|---|
| 196 | sort($chars);
|
|---|
| 197 |
|
|---|
| 198 | # Für jedes Zeichen die Kombination(en) auslesen, mit der/denen es erzeugt werden kann
|
|---|
| 199 | # ------------------------------------------------
|
|---|
| 200 |
|
|---|
| 201 | foreach ($chars as $char) {
|
|---|
| 202 | $char = array('char' => $char);
|
|---|
| 203 |
|
|---|
| 204 | # Ausnahmen, deren Kombinationen falsch sind, oder uns nicht interessieren:
|
|---|
| 205 | # Diese Zeichen werden nicht erstellt, sondern sind in der Referenz nur zur Verdeutlichung da.
|
|---|
| 206 | if (in_array($char['char'], array('⇞','⇟','⇠','⇡','⇢','⇣','⇱','⇲','⇥','⌦','⌧','⌫','↲','↶','⎀'))) continue; # Control-Keys
|
|---|
| 207 | # Auch die Toten Tasten werden nicht normal erzeugt. Deren Zeichen können aber durch Auslesen der Compose gewonnen werden.
|
|---|
| 208 | if (in_array($char['char'], array('˜','ˇ','¯','ˆ','˚','˘',"\xCC\x8F\x20",'`','¨','῾','¸','˝','˙','´','᾿'))) continue; # Dead-Keys
|
|---|
| 209 |
|
|---|
| 210 | $char['codepoint'] = 'U+' . getUnicodeCodepoint($char['char']);
|
|---|
| 211 | # Namen des Zeichens aus der UnicodeData auslesen
|
|---|
| 212 | $char['name'] = $ucd[getUnicodeCodepoint($char['char'])][1];
|
|---|
| 213 |
|
|---|
| 214 | # Da $keys mit den Positionen als Index angeordnet ist, bietet es sich an,
|
|---|
| 215 | # das Array umzukehren und gleichzeitig nach dem entsprechenden Zeichen zu suchen
|
|---|
| 216 | $char_positions = array_keys($keys, $char['char'], true);
|
|---|
| 217 |
|
|---|
| 218 | $char['combos'] = array();
|
|---|
| 219 | foreach ($char_positions as $pos) {
|
|---|
| 220 | # Tastenposition aus Arraywert lesen
|
|---|
| 221 | $keypos = substr($pos, 0, strpos($pos, '_'));
|
|---|
| 222 | # Keine Toten Tasten absuchen, würde sonst z.B. "<Mod3> + <´>" für SOLIDUS finden
|
|---|
| 223 | if (in_array($keypos, array('1', '13', '25'))) continue; # 1 ≙ T1; 13 ≙ T2; 25 ≙ T3
|
|---|
| 224 |
|
|---|
| 225 | # Je nach Level werden unterschiedliche Modifier gebraucht
|
|---|
| 226 | $level = substr($pos, -1);
|
|---|
| 227 | switch ($level) {
|
|---|
| 228 | case '1':
|
|---|
| 229 | $combo = '<{{{' . $keys[$keypos . '_1'] . '}}}>';
|
|---|
| 230 | break;
|
|---|
| 231 | case '2':
|
|---|
| 232 | $combo = '<Shift> + <{{{' . $keys[$keypos . '_1'] . '}}}>';
|
|---|
| 233 | break;
|
|---|
| 234 | case '3':
|
|---|
| 235 | $combo = '<Mod3> + <{{{' . $keys[$keypos . '_1'] . '}}}>';
|
|---|
| 236 | break;
|
|---|
| 237 | case '4':
|
|---|
| 238 | $combo = '<Mod4> + <{{{' . $keys[$keypos . '_1'] . '}}}>';
|
|---|
| 239 | break;
|
|---|
| 240 | case '5':
|
|---|
| 241 | $combo = '<Shift> + <Mod3> + <{{{' . $keys[$keypos . '_1'] . '}}}>';
|
|---|
| 242 | break;
|
|---|
| 243 | case '6':
|
|---|
| 244 | $combo = '<Mod3> + <Mod4> + <{{{' . $keys[$keypos . '_1'] . '}}}>';
|
|---|
| 245 | break;
|
|---|
| 246 |
|
|---|
| 247 | }
|
|---|
| 248 | $char['combos'][] = $combo;
|
|---|
| 249 |
|
|---|
| 250 | }
|
|---|
| 251 | # Die Leertaste hat keine Positionierung, deshalb manuelle Namensvergabe
|
|---|
| 252 | # Das Leerzeichen tritt in der Referenz leider mehrfach auf (an Stellen, die noch Leer sind):
|
|---|
| 253 | if ($char['name'] == 'SPACE') $char['combos'] = array('<Leertaste>');
|
|---|
| 254 | if ($char['name'] == 'DIGIT ZERO') $char['combos'][] = '<Mod4> + <Leertaste>';
|
|---|
| 255 | if ($char['name'] == 'NO-BREAK SPACE') $char['combos'] = array('<Shift> + <Mod3> + <Leertaste>');
|
|---|
| 256 | if ($char['name'] == 'NARROW NO-BREAK SPACE') $char['combos'] = array('<Mod3> + <Mod4> + <Leertaste>');
|
|---|
| 257 |
|
|---|
| 258 | # FIXME Sortieren der Combos, einfachste zuerst.
|
|---|
| 259 |
|
|---|
| 260 | # Zusammensetzen der Variablen und Ausgabe als wiki-formatierte Tabelle.
|
|---|
| 261 | # ------------------------------------------------
|
|---|
| 262 | echo '||{{{' . $char['char'] . '}}}||' . $char['codepoint'] . '||' . $char['name'] . '||' . implode('[[BR]]', $char['combos']) . '||' . "\n";
|
|---|
| 263 | }
|
|---|
| 264 |
|
|---|
| 265 | ?> |
|---|