Skip to content

Latest commit

 

History

History
805 lines (654 loc) · 29.6 KB

nf_z71_300.md

File metadata and controls

805 lines (654 loc) · 29.6 KB

NF Z71-300

Interfaces utilisateurs – Dispositions de clavier bureautique français.

Avertissement

Ce document contient des notes compilées lors du développement d’un pilote clavier qui implémente la norme NF Z71-300 relative à la disposition de clavier bureautique français.

Ce document n’est pas une référence, il contient des erreurs et des approximations.

Copyright © 2019, Cyril Lugan

License: MIT

Introduction

La norme définit la disposition des touches du module alphanumérique du clavier conformément à la série de normes ISO/CEI 9995 – relative à la disposition des claviers conçus pour la bureautique.

Objectifs par rapport aux dispositions établies par l’usage en France :

  • Être adapté à la saisie de la langue française.
  • Permettre la saisie des autres Langues de France et langues latines.
  • Réduire les disparités entre les différents fabricants de matériels et systèmes d’exploitation.
  • Améliorer l’ergonomie tout en évitant de provoquer de difficultés d’appropriation.
  • Rendre plus accessibles de nouveaux jeux de caractères utiles pour rédiger des documents spécifiques ou techniques.

Deux dispositions y sont normalisées. Une disposition « AZERTY », destinée à remplacer les dispositions « AZERTY » établies par l’usage. Elle a été développée à l'aide d'algorithmes informatiques. La méthodologie employée (Feit et al. 2018) et son historique (Nancel 2018) y sont présentés à titre d’information.

La disposition « BÉPO » y est également normalisée. Elle privilégie l’ergonomie et la performance à la familiarité avec les dispositions « AZERTY ». Son utilisation est particulièrement adaptée aux méthodes de dactylographie. Ce document contient peu de notes à son sujet, car elle est libre et très bien documentée.

Division logique du clavier en groupes et niveaux

D’après NF Z71-300, § 5.3.

Deux types de mécanismes sont reconnus pour la sélection de groupe et de niveau :

  • La sélection de niveau 2 ()
  • La sélection de groupe 2 (généralement AltGr ou )

Le niveau 1 et le groupe 1 sont implicitement sélectionnés lorsqu’aucune sélection n’est active.

Fonctionnement des touches mortes

D’après NF Z71-300, § 5.4.

Le fonctionnement des touches mortes décrite par ISO/CEI 9995-11 § 5.1 s’applique.

Liste des caractères graphiques pris en charge

# Supported chars and combinations with dead keys will be added
# when defined along the document.

supported_chars = set()
supported_combinations = {}

Caractères de l’alphabet latin

D’après NF Z71-300, § 6.2.1, Tableau 5 :

latin_letters = [
    ('A', 'a'), # U+0041, U+0061 (LETTER A)
    ('B', 'b'), # U+0042, U+0062 (LETTER B)
    ('C', 'c'), # U+0043, U+0063 (LETTER C)
    ('D', 'd'), # U+0044, U+0064 (LETTER D)
    ('E', 'e'), # U+0045, U+0065 (LETTER E)
    ('F', 'f'), # U+0046, U+0066 (LETTER F)
    ('G', 'g'), # U+0047, U+0067 (LETTER G)
    ('H', 'h'), # U+0048, U+0068 (LETTER H)
    ('I', 'i'), # U+0049, U+0069 (LETTER I)
    ('J', 'j'), # U+004A, U+006A (LETTER J)
    ('K', 'k'), # U+004B, U+006B (LETTER K)
    ('L', 'l'), # U+004C, U+006C (LETTER L)
    ('M', 'm'), # U+004D, U+006D (LETTER M)
    ('N', 'n'), # U+004E, U+006E (LETTER N)
    ('O', 'o'), # U+004F, U+006F (LETTER O)
    ('P', 'p'), # U+0050, U+0070 (LETTER P)
    ('Q', 'q'), # U+0051, U+0071 (LETTER Q)
    ('R', 'r'), # U+0052, U+0072 (LETTER R)
    ('S', 's'), # U+0053, U+0073 (LETTER S)
    ('T', 't'), # U+0054, U+0074 (LETTER T)
    ('U', 'u'), # U+0055, U+0075 (LETTER U)
    ('V', 'v'), # U+0056, U+0076 (LETTER V)
    ('W', 'w'), # U+0057, U+0077 (LETTER W)
    ('X', 'x'), # U+0058, U+0078 (LETTER X)
    ('Y', 'y'), # U+0059, U+0079 (LETTER Y)
    ('Z', 'z'), # U+005A, U+007A (LETTER Z)
    ('Æ', 'æ'), # U+00C6, U+00E6 (LETTER AE)
    ('Œ', 'œ'), # U+0152, U+0153 (LIGATURE OE)
    ('ẞ', 'ß'), # U+1E9E, U+00DF (LETTER SHARP S)
    ('Þ', 'þ'), # U+00DE, U+00FE (LETTER THORN)
    ('Ð', 'ð'), # U+00D0, U+00F0 (LETTER ETH)
    ('Ŋ', 'ŋ'), # U+014A, U+014B (LETTER ENG)
    ('IJ', 'ij'), # U+0132, U+0133 (LIGATURE IJ)
    ('Ʒ', 'ʒ'), # U+01B7, U+0292 (LETTER EZH)
    ('Ə', 'ə'), # U+018F, U+0259 (LETTER SCHWA)
    ('İ', 'ı'), # U+0130, U+0131 (LETTER I WITH DOT ABOVE, LETTER DOTLESS I)
    ('ϴ', None), # U+03F4 (GREEK CAPITAL THETA SYMBOL)
    (None, 'ʼ'), # U+02BC (MODIFIER LETTER APOSTROPHE)
    (None, 'ſ'), # U+017F (LETTER LONG S)
]

for lower, upper in latin_letters:
    if lower is not None:
        supported_chars.add(lower)
    if upper is not None:
        supported_chars.add(upper)

assert 'N' in supported_chars
assert 'n' in supported_chars
assert None not in supported_chars

Signes quasi-alphabétiques

D’après NF Z71-300, § 6.2.2, Tableau 6 :

almost_alphabethical_symbols = [
    '&', # U+0026 (AMPERSAND)
    '©', # U+00A9 (COPYRIGHT SIGN)
    '®', # U+00AE (REGISTERED SIGN)
    '™', # U+2122 (TRADE MARK SIGN)
    '§', # U+00A7 (SECTION SIGN)
    'ª', # U+00AA (FEMININE ORDINAL INDICATOR)
    'º', # U+00BA (MASCULINE ORDINAL INDICATOR)
    '@', # U+0040 (COMMERCIAL AT)
]

supported_chars.update(almost_alphabethical_symbols)
assert 'ª' in supported_chars

Signes numériques et scientifiques

D’après NF Z71-300, § 6.2.3, Tableau 7 :

digits = [
    ('0', '⁰', '₀'), # U+0030, U+2070, U+2080
    ('1', '¹', '₁'), # U+0031, U+00B9, U+2081
    ('2', '²', '₂'), # U+0032, U+00B2, U+2082
    ('3', '³', '₃'), # U+0033, U+00B3, U+2083
    ('4', '⁴', '₄'), # U+0034, U+2074, U+2084
    ('5', '⁵', '₅'), # U+0035, U+2075, U+2085
    ('6', '⁶', '₆'), # U+0036, U+2076, U+2086
    ('7', '⁷', '₇'), # U+0037, U+2077, U+2087
    ('8', '⁸', '₈'), # U+0038, U+2078, U+2088
    ('9', '⁹', '₉'), # U+0039, U+2079, U+2089
]

for line in digits:
    supported_chars.update(line)

assert '7' in supported_chars
assert '⁷' in supported_chars
assert '₇' in supported_chars

D’après NF Z71-300, § 6.2.3, Tableau 8 :

symbols = [
    '#', # U+0023 (NUMBER SIGN)
    '%', # U+0025 (PERCENT SIGN)
    '+', # U+002B (PLUS SIGN)
    '/', # U+002F (SOLIDUS)
    '<', # U+003C (LESS-THAN SIGN)
    '=', # U+003D (EQUALS SIGN)
    '>', # U+003E (GREATER-THAN SIGN)
    '\\', # U+005C (REVERSE SOLIDUS)
    '^', # U+005E (CIRCUMFLEX ACCENT)
    '_', # U+005F (LOW LINE)
    '`', # U+0060 (GRAVE ACCENT)
    '|', # U+007C (VERTICAL LINE)
    '~', # U+007E (TILDE)
    '°', # U+00B0 (DEGREE SIGN)
    '±', # U+00B1 (PLUS-MINUS SIGN)
    '¼', # U+00BC (VULGAR FRACTION ONE QUARTER)
    '½', # U+00BD (VULGAR FRACTION ONE HALF)
    '×', # U+00D7 (MULTIPLICATION SIGN)
    '÷', # U+00F7 (DIVISION SIGN)
    '‰', # U+2030 (PER MILLE SIGN)
    '−', # U+2212 (MINUS SIGN)
    '≤', # U+2264 (LESS-THAN OR EQUAL TO)
    '≥', # U+2265 (GREATER-THAN OR EQUAL TO)
    '≠', # U+2260 (NOT EQUAL TO)
    '≃', # U+2243 (ASYMPTOTICALLY EQUAL TO)
    '≄', # U+2244 (NOT ASYMPTOTICALLY EQUAL TO)
    '√', # U+221A (SQUARE ROOT)
    '∞', # U+221E (INFINITY)
    'µ', # U+00B5 (MICRO SIGN)
]

supported_chars.update(symbols)
assert '≃' in supported_chars

Séparateur décimal

Le séparateur décimal est la virgule « , » (U+002C).

Lettres grecques

D’après NF Z71-300, § 6.2.5, Tableau 9 :

greek_letters = {
    'A': ('α', 'Α'), # U+03B1, U+0391 (ALPHA)
    'B': ('β', 'Β'), # U+03B2, U+0392 (BETA)
    'G': ('γ', 'Γ'), # U+03B3, U+0393 (GAMMA)
    'D': ('δ', 'Δ'), # U+03B4, U+0394 (DELTA)
    'E': ('ε', 'Ε'), # U+03B5, U+0395 (EPSILON)
    'Z': ('ζ', 'Ζ'), # U+03B6, U+0396 (ZETA)
    'H': ('η', 'Η'), # U+03B7, U+0397 (ETA)
    'U': ('θ', 'Θ'), # U+03B8, U+0398 (THETA)
    'I': ('ι', 'Ι'), # U+03B9, U+0399 (IOTA)
    'K': ('κ', 'Κ'), # U+03BA, U+039A (KAPPA)
    'L': ('λ', 'Λ'), # U+03BB, U+039B (LAMDA)
    'M': ('μ', 'Μ'), # U+03BC, U+039C (MU)
    'N': ('ν', 'Ν'), # U+03BD, U+039D (NU)
    'J': ('ξ', 'Ξ'), # U+03BE, U+039E (XI)
    'O': ('ο', 'Ο'), # U+03BF, U+039F (OMICRON)
    'P': ('π', 'Π'), # U+03C0, U+03A0 (PI)
    'R': ('ρ', 'Ρ'), # U+03C1, U+03A1 (RHO)
    'S': ('σ', 'Σ'), # U+03C3, U+03A3 (SIGMA)
    'W': ('ς', None), # U+03C2 (FINAL SIGMA)
    'T': ('τ', 'Τ'), # U+03C4, U+03A4 (TAU)
    'Y': ('υ', 'Υ'), # U+03C5, U+03A5 (UPSILON)
    'F': ('φ', 'Φ'), # U+03C6, U+03A6 (PHI)
    'X': ('χ', 'Χ'), # U+03C7, U+03A7 (CHI)
    'C': ('ψ', 'Ψ'), # U+03C8, U+03A8 (PSI)
    'V': ('ω', 'Ω'), # U+03C9, U+03A9 (OMEGA)
}

for latin_upper, greek_cases in greek_letters.items():
    greek_lower, greek_upper = greek_cases
    supported_combinations[('◌µ', latin_upper.lower())] = greek_lower
    supported_chars.add(greek_lower)
    if greek_upper is not None:
        supported_combinations[('◌µ', latin_upper)] = greek_upper
        supported_chars.add(greek_upper)

assert supported_combinations[('◌µ', 'd')] == 'δ'
assert supported_combinations[('◌µ', 'D')] == 'Δ'
assert 'δ' in supported_chars
assert 'Δ' in supported_chars
assert None not in supported_chars

Le symbole micro, différent de la lettre grecque Mu, s’obtient en pressant la touche morte des lettres grecques suivie d’elle-même ou de l’espace.

supported_chars.add('µ')
supported_combinations[('◌µ', ' ')] = 'µ'
supported_combinations[('◌µ', "\u00A0")] = 'µ'

Signes typographiques

D’après NF Z71-300, § 6.2.6, Tableau 10 :

typographical_symbols = [
    '!', # U+0021 (EXCLAMATION MARK)
    '"', # U+0022 (QUOTATION MARK)
    "'", # U+0027 (APOSTROPHE)
    '*', # U+002A (ASTERISK)
    ',', # U+002C (COMMA)
    '-', # U+002D (HYPHEN-MINUS)
    '.', # U+002E (FULL STOP)
    ':', # U+003A (COLON)
    ';', # U+003B (SEMICOLON)
    '?', # U+003F (QUESTION MARK)
    '¡', # U+00A1 (INVERTED EXCLAMATION MARK)
    '·', # U+00B7 (MIDDLE DOT)
    '¿', # U+00BF (INVERTED QUESTION MARK)
    '‑', # U+2011 (NON-BREAKING HYPHEN)
    '–', # U+2013 (EN DASH)
    '—', # U+2014 (EM DASH)
    '’', # U+2019 (RIGHT SINGLE QUOTATION MARK)
    '†', # U+2020 (DAGGER)
    '‡', # U+2021 (DOUBLE DAGGER)
    '…', # U+2026 (HORIZONTAL ELLIPSIS)
]

supported_chars.update(typographical_symbols)
assert '†' in supported_chars

D’après NF Z71-300, § 6.2.6, Tableau 11 :

matching_typographical_symbols = [
    '(', # U+0028 (LEFT PARENTHESIS)
    ')', # U+0029 (RIGHT PARENTHESIS)
    '[', # U+005B (LEFT SQUARE BRACKET)
    ']', # U+005D (RIGHT SQUARE BRACKET)
    '{', # U+007B (LEFT CURLY BRACKET)
    '}', # U+007D (RIGHT CURLY BRACKET)
    '«', # U+00AB (LEFT-POINTING DOUBLE ANGLE QUOTATION MARK)
    '»', # U+00BB (RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK)
    '‹', # U+2039 (SINGLE LEFT-POINTING ANGLE QUOTATION MARK)
    '›', # U+203A (SINGLE RIGHT-POINTING ANGLE QUOTATION MARK)
    '‚', # U+201A (SINGLE LOW-9 QUOTATION MARK)
    '‘', # U+2018 (LEFT SINGLE QUOTATION MARK)
    '“', # U+201C (LEFT DOUBLE QUOTATION MARK)
    '”', # U+201D (RIGHT DOUBLE QUOTATION MARK)
    '„', # U+201E (DOUBLE LOW-9 QUOTATION MARK)
]

supported_chars.update(matching_typographical_symbols)
assert '»' in supported_chars

D’après NF Z71-300, § 6.2.6, Tableau 12 :

typographical_spaces = [
    "\u0020", # SPACE (espace)
    "\u00A0", # NO-BREAK SPACE (espace insécable)
    "\u202F", # NARROW NO-BREAK SPACE (espace fine insécable)
    "\u2003", # EM SPACE (espace cadratin)
]

supported_chars.update(typographical_spaces)
assert ' ' in supported_chars
  • L’espace est utilisée entre deux mots, elle peut être coupée par un retour à la ligne.
  • L’espace insécable est utilisée entre deux mots ou avant la ponctuation « : », elle ne peut pas être coupée par un retour à la ligne.
  • L’espace fine insécable est utilisée avant les ponctuations « ; ! ? », entre les chevrons « » et les milliers d’un nombre.
  • L’espace cadratin est utilisée en typographie anglaise, ou devant une abréviation composée d’une lettre suivie d’un point (comme M.).

Symboles monétaires

D’après NF Z71-300, § 6.2.7, Tableau 13.

currency_symbols = [
    '€', # U+20AC (EURO SIGN)
    '$', # U+0024 (DOLLAR SIGN)
    '£', # U+00A3 (POUND SIGN)
]

supported_chars.update(currency_symbols)

D’après NF Z71-300, § 6.2.7, Tableau 14.

Les autres symboles sont accessibles par la touche morte ¤.

extended_currency_symbols = {
    'A': (None, '₳', None, None), # U+20B3
    'B': ('₿', '฿', None, None), # U+20BF, U+0E3F
    'C': ('¢', '₵', '₢', '₡'), # U+00A2, U+20B5, U+20A2, U+20A1
    'D': ('₫', '₯', None, None), # U+20AB, U+20AF
    'E': ('₠', None, None, '¤'), # U+20A0, U+00A4
    'F': ('ƒ', '₣', None, None), # U+0192, U+20A3
    'G': ('₲', None, None, None), # U+20B2
    'H': ('₴', None, None, None), # U+20B4
    'K': ('₭', None, None, None), # U+20AD
    'L': ('₺', '₤', '₾', None), # U+20BA, U+20A4, U+20BE
    'M': ('₥', 'ℳ', '₼', None), # U+20A5, U+2133, U+20BC
    'N': ('₦', None, None, None), # U+20A6
    'P': ('₱', '₧', '₰', None), # U+20B1, U+20A7, U+20B0
    'R': ('₽', '₹', '₨', None), # U+20BD, U+20B9, U+20A8
    'S': ('₪', '₷', None, None), # U+20AA, U+20B7
    'T': ('₸', '₮', None, None), # U+20B8, U+20AE
    'Y': ('¥', None, None, None), # U+00A5
    'W': ('₩', None, None, None), # U+20A9
}

for symbols in extended_currency_symbols.values():
    supported_chars.update(symbols)
supported_chars.remove(None)

assert '₸' in supported_chars
assert '₮' in supported_chars
assert None not in supported_chars

Signes diacritiques et leurs combinaisons

Signes diacritiques pris en charge

D’après NF Z71-300, § 6.3.1.

supported_diacritics = [
    '◌̀', # GRAVE ACCENT
    '◌́', # ACUTE ACCENT
    '◌̂', # CIRCUMFLEX ACCENT
    '◌̃', # TILDE
    '◌̄', # MACRON
    '◌̆', # BREVE
    '◌̇', # DOT ABOVE
    '◌̈', # DIAERESIS
    '◌̊', # RING ABOVE
    '◌̋', # DOUBLE ACUTE ACCENT
    '◌̌', # CARON
    '◌̦', # COMMA BELOW
    '◌̧', # CEDILLA
    '◌̨', # OGONEK
    '◌̵', # SHORT STROKE OVERLAY
    '◌̸', # LONG SOLIDUS OVERLAY
    '◌̏', # DOUBLE GRAVE ACCENT
    '◌̑', # INVERTED BREVE
    '◌̣', # DOT BELOW
    '◌̱', # MACRON BELOW
]

Combinaison de diacritiques avec des lettres de l’alphabet latin

D’après NF Z71-300, § 6.3.2, Tableau 15.

from kbdrefs.literate import parse_as_table

# TODO check Ḡḡ, ı
letters_combinations_table = parse_as_table("""
|◌ |◌̀ |◌́ |◌̂ |◌̃ |◌̄ |◌̆ |◌̇ |◌̈ |◌̊ |◌̋ |◌̌ |◌̦ |◌̧ |◌̨ |◌̵ |◌̸ |◌̏ |◌̑ |◌̣ |◌̱ |
|Aa|Àà|Áá|Ââ|Ãã|Āā|Ăă|Ȧȧ|Ää|Åå|  |Ǎǎ|  |  |Ąą|  |Ⱥ |Ȁȁ|Ȃȃ|Ạạ|  |
|Bb|  |  |  |  |  |  |Ḃḃ|  |  |  |  |  |  |  |Ƀƀ|  |  |  |Ḅḅ|Ḇḇ|
|Cc|  |Ćć|Ĉĉ|  |  |  |Ċċ|  |  |  |Čč|  |Çç|  |  |Ȼȼ|  |  |  |  |
|Dd|  |  |  |  |  |  |Ḋḋ|  |  |  |Ďď|  |Ḑḑ|  |Đđ|  |  |  |Ḍḍ|Ḏḏ|
|Ee|Èè|Éé|Êê|Ẽẽ|Ēē|Ĕĕ|Ėė|Ëë|  |  |Ěě|  |Ȩȩ|Ęę|  |Ɇɇ|Ȅȅ|Ȇȇ|Ẹẹ|  |
|Ff|  |  |  |  |  |  |Ḟḟ|  |  |  |  |  |  |  |  |  |  |  |  |  |
|Gg|  |Ǵǵ|Ĝĝ|  |Ḡḡ|Ğğ|Ġġ|  |  |  |Ǧǧ|  |Ģģ|  |Ǥǥ|  |  |  |  |  |
|Hh|  |  |Ĥĥ|  |  |  |Ḣḣ|Ḧḧ|  |  |Ȟȟ|  |Ḩḩ|  |Ħħ|  |  |  |Ḥḥ| ẖ|
|Ii|Ìì|Íí|Îî|Ĩĩ|Īī|Ĭĭ|İı|Ïï|  |  |Ǐǐ|  |  |Įį|Ɨ |  |Ȉȉ|Ȋȋ|Ịị|  |
|Jj|  |  |Ĵĵ|  |  |  |  |  |  |  | ǰ|  |  |  |Ɉɉ|  |  |  |  |  |
|Kk|  |Ḱḱ|  |  |  |  |  |  |  |  |Ǩǩ|  |Ķķ|  |  |  |  |  |Ḳḳ|Ḵḵ|
|Ll|  |Ĺĺ|  |  |  |  |Ŀŀ|  |  |  |Ľľ|  |Ļļ|  |Ƚƚ|Łł|  |  |Ḷḷ|Ḻḻ|
|Mm|  |Ḿḿ|  |  |  |  |Ṁṁ|  |  |  |  |  |  |  |  |  |  |  |Ṃṃ|  |
|Nn|Ǹǹ|Ńń|  |Ññ|  |  |Ṅṅ|  |  |  |  |  |Ņņ|  |  |  |  |Ňň|Ṇṇ|Ṉṉ|
|Oo|Òò|Óó|Ôô|Õõ|Ōō|Ŏŏ|Ȯȯ|Öö|  |Őő|Ǒǒ|  |  |Ǫǫ|  |Øø|Ȍȍ|Ȏȏ|Ọọ|  |
|Pp|  |Ṕṕ|  |  |  |  |Ṗṗ|  |  |  |  |  |  |  |  |  |  |  |  |  |
|Rr|  |Ŕŕ|  |  |  |  |Ṙṙ|  |  |  |Řř|  |Ŗŗ|  |Ɍɍ|  |Ȑȑ|Ȓȓ|Ṛṛ|Ṟṟ|
|Ss|  |Śś|Ŝŝ|  |  |  |Ṡṡ|  |  |  |Šš|Șș|Şş|  |  |  |  |  |Ṣṣ|  |
|Tt|  |  |  |  |  |  |Ṫṫ| ẗ|  |  |Ťť|Țț|Ţţ|  |Ŧŧ|Ⱦ |  |  |Ṭṭ|Ṯṯ|
|Uu|Ùù|Úú|Ûû|Ũũ|Ūū|Ŭŭ|  |Üü|Ůů|Űű|Ǔǔ|  |  |Ųų|Ʉʉ|  |Ȕȕ|Ȗȗ|Ụụ|  |
|Vv|  |  |  |Ṽṽ|  |  |  |  |  |  |  |  |  |  |  |  |  |  |Ṿṿ|  |
|Ww|Ẁẁ|Ẃẃ|Ŵŵ|  |  |  |Ẇẇ|Ẅẅ| ẘ|  |  |  |  |  |  |  |  |  |Ẉẉ|  |
|Xx|  |  |  |  |  |  |Ẋẋ|Ẍẍ|  |  |  |  |  |  |  |  |  |  |  |  |
|Yy|Ỳỳ|Ýý|Ŷŷ|Ỹỹ|Ȳȳ|  |Ẏẏ|Ÿÿ| ẙ|  |  |  |  |  |Ɏɏ|  |  |  |Ỵỵ|  |
|Zz|  |Źź|Ẑẑ|  |  |  |Żż|  |  |  |Žž|  |  |  |Ƶƶ|  |  |  |Ẓẓ|Ẕẕ|
|Ææ|  |Ǽǽ|  |  |Ǣǣ|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
|Ʒʒ|  |  |  |  |  |  |  |  |  |  |Ǯǯ|  |  |  |  |  |  |  |  |  |
| ſ|  |  |  |  |  |  | ẛ|  |  |  |  |  |  |  |  | ẜ|  |  |  |  |
""", strip='')

Note: La norme comporte une erreur, Ḟ, ḟ est indiqué à la place de Ḡ, ḡ.

# Make combinations from table
letters_combinations = {}
for line in letters_combinations_table[1:]:
    for col, dead_key in enumerate(letters_combinations_table[0][1:], 1):
        for case in (0, 1):
            letter = line[0][case]
            output = line[col][case]
            if output != ' ':
                letters_combinations[(dead_key.strip(), letter)] = output

supported_combinations.update(letters_combinations)
supported_chars.update(letters_combinations.values())

assert supported_combinations[('◌̸', 'A')] == 'Ⱥ'
assert not ('◌̸', 'a') in supported_combinations
assert 'Ⱥ' in supported_chars
assert '◌̸' not in supported_chars
import unicodedata

# Checks that combined char name contains the base letter name
for combination, output in letters_combinations.items():
    dead_key, combined_with = combination
    exceptions = ['ı']
    if not output in exceptions:
        combined_with = unicodedata.name(combined_with)
        output = unicodedata.name(output)
        assert (combined_with in output), (
            '{} is not in {}'.format(combined_with, output))

Combinaison de diacritiques avec d’autres signes

D’après NF Z71-300, § 6.3.3.1, Tableau 16.

La combinaison d’un chiffre avec l’accent circonflexe produit le chiffre en exposant :

special_chars_from_diacritics = {
    ('◌̂', '0'): '⁰', # U+2070
    ('◌̂', '1'): '¹', # U+00B9
    ('◌̂', '2'): '²', # U+00B2
    ('◌̂', '3'): '³', # U+00B3
    ('◌̂', '4'): '⁴', # U+2074
    ('◌̂', '5'): '⁵', # U+2075
    ('◌̂', '6'): '⁶', # U+2076
    ('◌̂', '7'): '⁷', # U+2077
    ('◌̂', '8'): '⁸', # U+2078
    ('◌̂', '9'): '⁹', # U+2079
}

La combinaison d’un chiffre avec le caron produit le chiffre en indice :

special_chars_from_diacritics.update({
    ('◌̌', '0'): '₀', # U+2080
    ('◌̌', '1'): '₁', # U+2081
    ('◌̌', '2'): '₂', # U+2082
    ('◌̌', '3'): '₃', # U+2083
    ('◌̌', '4'): '₄', # U+2084
    ('◌̌', '5'): '₅', # U+2085
    ('◌̌', '6'): '₆', # U+2086
    ('◌̌', '7'): '₇', # U+2087
    ('◌̌', '8'): '₈', # U+2088
    ('◌̌', '9'): '₉', # U+2089
})

D’après NF Z71-300, § 6.3.3.2, Tableau 17. Le tilde et la barre oblique permettent d’accéder aux caractères suivants :

special_chars_from_diacritics.update({
    ('◌̃', '-'): '≃', # U+2243
    ('◌̃', '='): '≈', # U+2248
    ('◌̸', '='): '≠', # U+2260
    ('◌̸', '≃'): '≄', # U+2244
})
supported_combinations.update(special_chars_from_diacritics)
assert supported_combinations[('◌̂', '7')] == '⁷'
assert supported_combinations[('◌̌', '7')] == '₇'
assert supported_combinations[('◌̸', '≃')] == '≄'

Combinaisons avec l’espace telles que décrites par ISO/CEI 9995-11 § 5.2.

from kbdrefs import iso_cei_9995_11

for space in typographical_spaces:
    for dead_key in supported_diacritics:
        clone = iso_cei_9995_11.get_spacing_clone(dead_key)
        supported_combinations[(dead_key, space)] = clone

assert supported_combinations[('◌̀', ' ')] == '`'

Disposition de clavier de type AZERTY

Note: Certains caractères définis dans cette section de la norme ne correspondent pas à ceux définis dans la première partie. Par exemple ⩽ (U+2A7D) est utilisé alors que la première partie définit ≤ (U+2264). Ce document utilise la première partie comme référence.

D’après NF Z71-300, § A.1.1.2, Tableau A.1.

Espaces identifiées par leur nom Unicode.

from kbdrefs.literate import parse_as_list
from kbdrefs.iso_cei_9995_1 import alphanumeric_keys

azerty_group1_level1 = parse_as_list("""
║ @ ║ à ║ é ║ è ║ ê ║ ( ║ ) ║ ‘ ║ ’ ║ « ║ » ║ ' ║ ◌̂ ║
      ║ a ║ z ║ e ║ r ║ t ║ y ║ u ║ i ║ o ║ p ║ - ║ + ║
       ║ q ║ s ║ d ║ f ║ g ║ h ║ j ║ k ║ l ║ m ║ / ║ * ║
     ║ < ║ w ║ x ║ c ║ v ║ b ║ n ║ . ║ , ║ : ║ ; ║
""", row_separator='║')
azerty_group1_level1.append(' ') # Espace

azerty_group1_level2 = parse_as_list("""
║ # ║ 1 ║ 2 ║ 3 ║ 4 ║ 5 ║ 6 ║ 7 ║ 8 ║ 9 ║ 0 ║ " ║ ◌̈ ║
      ║ A ║ Z ║ E ║ R ║ T ║ Y ║ U ║ I ║ O ║ P ║ – ║ ± ║
       ║ Q ║ S ║ D ║ F ║ G ║ H ║ J ║ K ║ L ║ M ║ \\║ ½ ║
     ║ > ║ W ║ X ║ C ║ V ║ B ║ N ║ ? ║ ! ║ … ║ = ║
""", row_separator='║')
azerty_group1_level2.append(' ') # Espace

azerty_group2_level1 = parse_as_list("""
║ ◌̆ ║ § ║ ◌́ ║ ◌̀ ║ & ║ [ ║ ] ║ ◌̄ ║ _ ║ “ ║ ” ║ ° ║ ◌̌ ║
      ║ æ ║ £ ║ € ║ ® ║ { ║ } ║ ù ║ ◌̇ ║ œ ║ % ║ − ║ † ║
       ║ θ ║ ß ║ $ ║◌¤ ║◌µ ║◌Eu║   ║ ◌̸ ║ | ║ ∞ ║ ÷ ║ × ║
     ║ ≤ ║ ʒ ║ © ║ ç ║ ◌̧ ║ ◌̵ ║ ◌̃ ║ ¿ ║ ¡ ║ · ║ ≃ ║
""", row_separator='║')
azerty_group2_level1.append('\u00A0') # Espace insécable

azerty_group2_level2 = parse_as_list("""
║ ◌̑ ║ À ║ É ║ È ║ Ê ║ ◌̋ ║ ◌̏ ║   ║ — ║ ‹ ║ › ║ ◌̊ ║   ║
      ║ Æ ║   ║   ║   ║ ™ ║   ║ Ù ║ ◌̣ ║ Œ ║ ‰ ║ ‑ ║ ‡ ║
       ║ ϴ ║ ẞ ║   ║   ║   ║ ◌̱ ║   ║   ║   ║   ║ √ ║ ¼ ║
     ║ ≥ ║ Ʒ ║   ║ Ç ║ ◌̨ ║   ║   ║   ║ ◌̦ ║   ║ ≠ ║
""", row_separator='║')
azerty_group2_level2.append('\u202F') # Espace fine insécable

azerty_layout = dict(zip(
    alphanumeric_keys,
    zip(azerty_group1_level1,
        azerty_group1_level2,
        azerty_group2_level1,
        azerty_group2_level2)))

assert azerty_layout['B06'] == ('n', 'N', '◌̃', None)

Touche de verrouillage des majuscules

D’après NF Z71-300, § A.1.1.3, Tableau A.2.

Lorsque le verrouillage des majuscules est activé, le caractère de niveau 2 est sélectionné pour les chiffres et les lettres. La saisie des autres caractères reste inchangée.

azerty_caps_lock = [
    # Selected in group 1
    '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
    # Selected in group 2
    'À', 'É', 'È', 'Ê', 'Æ', 'Ù', 'Œ', 'Θ', 'ẞ', 'Ʒ', 'Ç'
]

Touches mortes

azerty_combinations = {}
azerty_combinations.update(supported_combinations)

Combinaisons complémentaires

D’après NF Z71-300, § A.1.2.1, Tableau A.4.

azerty_combinations.update({
    ('◌̸', '<'): '≮', # U+226E
    ('◌̸', '>'): '≯', # U+226F
    ('◌̸', '≤'): '≰', # U+2270
    ('◌̸', '≥'): '≱', # U+2271
})

D’après NF Z71-300, § A.1.2.1, Tableau A.5.

azerty_combinations.update({
    ('◌̃', '<'): '≲', # U+2272
    ('◌̃', '>'): '≳', # U+2273
})

Lettres grecques et symboles monétaires

D’après NF Z71-300, § A.1.2.3.

for letter, symbols in extended_currency_symbols.items():
    for key_chars in azerty_layout.values():
        if key_chars[1] == letter:
            for i, symbol in enumerate(symbols):
                if symbol is not None and symbol is not '¤':
                    azerty_combinations[('◌¤', key_chars[i])] = symbol
            break

Note: D’après NF Z71-300, § 6.2.7, Tableau 14. Le symbole « ¤ » devrait être obtenu en combinant la touche morte symbole monétaire et le caractère en groupe 2, niveau 2 de la touche « E ». Cependant la disposition AZERTY ne définit pas de caractère en groupe 2, niveau 2 pour la touche « E ». Ce document remplace cette combinaison par l’espace.

azerty_combinations.update({
    ('◌¤', ' '): '¤',
    ('◌¤', "\u00A0"): '¤',
})

Caractères européens additionels

D’après NF Z71-300, § A.1.2.4.

extended_latin_letters = {
    'A': ('ª', None),
    'D': ('ð', 'Ð'),
    'E': ('ə', 'Ə'),
    'G': ('ŋ', 'Ŋ'),
    'O': ('º', None),
    'S': ('ſ', None),
    'T': ('Þ', 'þ'),
    'J': ('ij', 'IJ'),
    'I': ('ı', 'İ'),
}

extended_latin_symbols = {
    '‘': '‚', # U+201A
    '7': '›', # U+203A
    '’': '‘', # U+2018
    '8': '‹', # U+2039
    '«': '„', # U+201E
    '9': '»', # U+00BB
    '»': '“', # U+201C
    '0': '«', # U+00AB
    "'": 'ʼ', # U+02BC
}

for letter, extended in extended_latin_letters.items():
    extended_lower, extended_upper = extended
    azerty_combinations[('◌Eu', letter.lower())] = extended_lower
    if extended_upper is not None:
        azerty_combinations[('◌Eu', letter)] = extended_upper

for char, symbol in extended_latin_symbols.items():
    azerty_combinations[('◌Eu', char)] = symbol

Note: L’espace cadratin n’est pas supporté dans la disposition AZERTY normalisée, alors qu’il est définit dans NF Z71-300, § 6.2.6, Tableau 12. Ce document le rend disponible par la touche morte des caractères européens additionels, comme sur la disposition BÉPO.

azerty_combinations.update({
    ('◌Eu', ' '): "\u2003",
    ('◌Eu', "\u00A0"): "\u2003",
})

Validation

import re

# Supported chars contains only single codepoint chars

for char in supported_chars:
    assert type(char) == str, "{} is not a str".format(char)
    assert len(char) == 1, "len({}) is not 1".format(char)

# Supported combinations
# - defines dead keys combined with ◌
# - combines with supported chars
# - outputs single codepoint chars, except for some spacing clones

for combination, output in azerty_combinations.items():
    dead_key, combined_with = combination
    assert dead_key[0] == '◌', (
        "◌ not in {}".format((combination, output)))
    assert combined_with in supported_chars, "Cannot combine with {}, not in supported chars".format(combined_with)
    if not combined_with in typographical_spaces:
        assert len(output) == 1, (
            "{} unexpected len".format((combination, output)))

# Combinations are consistent with special combinations
# defined in ISO/CEI 9995-11

for combination, output in azerty_combinations.items():
    if combination in iso_cei_9995_11.special_chars_combinations:
        expected = iso_cei_9995_11.special_chars_combinations[combination]
        assert output == expected, "{} != {}".format(output, expected)

# Codepoints in comments describes char on the same line

import re
with open(MARKDOWN_FILE, 'r') as file:
    for line in file:
        for codepoint in re.findall(r'U\+[0-9a-fA-F]{4}', line):
            char = chr(int(codepoint[2:], 16))
            assert char in line , (
            "{} not in {}".format(char, line))

# AZERTY supports every chars defined in the document

supported_by_azerty = set()
for chars in azerty_layout.values():
    supported_by_azerty.update(chars)

for combination, output in azerty_combinations.items():
    dead_key, combined_with = combination
    assert dead_key in supported_by_azerty, "Dead key {} is not supported by azerty".format(dead_key)
    supported_by_azerty.add(output)

for char in supported_chars:
    assert char in supported_by_azerty, "Char {} is not supported by azerty".format(char)

Références

Fiet et al. 2018 : Anna Feit, Mathieu Nancel, Daryl Weir, Gilles Bailly, Maximilian John, et al. Élaboration de la disposition AZERTY modernisée. 2018.

Nancel 2018 : Mathieu Nancel. Historique et méthodologie de la nouvelle disposition de clavier AZERTY. 2018.

NF Z71-300 : Norme AFNOR Z71-300. Interfaces utilisateurs - Dispositions de clavier bureautique français. 2019.