Repository with sources and generator of https://larlet.fr/david/ https://larlet.fr/david/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

typography.py 2.4KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. from dataclasses import dataclass
  2. import regex # pour le support de "\p{}"
  3. @dataclass
  4. class Caractere:
  5. unicode: str
  6. html: str
  7. ESPACE_INSECABLE = Caractere(unicode="\u00a0", html=" ")
  8. ESPACE_FINE_INSECABLE = Caractere(unicode="\u202f", html=" ")
  9. def assemble_regexes(*regexes):
  10. return "|".join(regexes)
  11. def build_regex(avant, apres):
  12. # \p{} permet de reconnaître un caractère par sa catégorie Unicode
  13. # "Zs" est la catégorie "Separator, space".
  14. return (
  15. rf"((?P<avant>{avant})"
  16. + rf"(\p{{Zs}}|{ESPACE_INSECABLE.html})"
  17. + rf"(?P<apres>{apres}))"
  18. + r"(?!(.(?!<svg))*<\/svg>)"
  19. )
  20. RE_ESPACE_FINE_INSECABLE = regex.compile(
  21. assemble_regexes(
  22. build_regex(r"\w?", r"[;\?!]"), # Ponctuations doubles.
  23. build_regex(
  24. r"\d", r"([ghj]|min|sec|images|mm|hab|kg|mg|µg|L|km|°C|GHz)(\b|$)"
  25. ), # Unités.
  26. build_regex(r"\d", r"(Mo|Ko|Go|Mb|Kb|Gb)(\b|$)"), # Tailles de fichiers.
  27. build_regex(r"\d", r"%"), # Pourcentages.
  28. build_regex(r"\d", r"€"), # Symboles monétaires.
  29. build_regex(r"\d", r"\d"), # Séparateurs de milliers.
  30. )
  31. )
  32. def insere_espaces_fines_insecables(texte):
  33. return RE_ESPACE_FINE_INSECABLE.sub(
  34. r"\g<avant>" + ESPACE_FINE_INSECABLE.unicode + r"\g<apres>", texte
  35. )
  36. RE_ESPACE_INSECABLE = regex.compile(
  37. assemble_regexes(
  38. build_regex(r"\w?", r":"), # Deux points.
  39. build_regex(r"«", r""), # Guillemets en chevrons.
  40. build_regex(r"", r"»"), # Guillemets en chevrons.
  41. build_regex(
  42. rf"\b(\d|{ESPACE_FINE_INSECABLE.html})+", r"(?!\d)\w"
  43. ), # Nombre suivi de lettres.
  44. build_regex(r"(M\.|Mme)", r"\w"), # Titres (Monsieur, Madame).
  45. )
  46. )
  47. def insere_espaces_insecables(texte):
  48. return RE_ESPACE_INSECABLE.sub(
  49. r"\g<avant>" + ESPACE_INSECABLE.unicode + r"\g<apres>", texte
  50. )
  51. def encode_espaces_insecables_en_html(texte):
  52. for caractere in (ESPACE_INSECABLE, ESPACE_FINE_INSECABLE):
  53. texte = texte.replace(caractere.unicode, caractere.html)
  54. return texte
  55. def typographie(texte, html=False):
  56. """
  57. Utilise les espaces insécables fines ou normales lorsque c’est approprié
  58. https://fr.wikipedia.org/wiki/Espace_ins%C3%A9cable#En_France
  59. """
  60. res = insere_espaces_fines_insecables(insere_espaces_insecables(texte))
  61. if html:
  62. res = encode_espaces_insecables_en_html(res)
  63. return res