- PDF: police Cochineal, interligne/alinéa, marges KDP (0.4in), exposants de note à 7.5pt, correctif centrage \newline dans titlesec (bug reproductible), contraste surtitre/titre pour Introduction/Conclusion/ Préface via \eyebrowtitle - EPUB: epub.css conforme aux règles KDP (police sur body, pas de couleur forcée, marges en %), structure-epub.lua reconstruit les titres LaTeX bruts, liens de retour bidirectionnels sur les notes (méthode KDP recommandée), séparateur deux-points harmonisé dans la TOC (Introduction/Conclusion/Parties) - Nouveaux filtres: typographie-francaise.lua (espaces insécables), structure-epub.lua - compiler-livre.sh: ajustements marges et appel des filtres
103 lines
3.8 KiB
Lua
103 lines
3.8 KiB
Lua
--[[
|
|
typographie-francaise.lua — filtre Pandoc
|
|
|
|
Applique les règles d'espacement de la typographie française au moment
|
|
de la compilation, pour le PDF comme pour l'EPUB. Le manuscrit reste
|
|
écrit normalement (espace ordinaire) : c'est ce filtre qui convertit
|
|
ces espaces en espaces insécables au bon endroit.
|
|
|
|
Pourquoi un filtre et non babel :
|
|
Pandoc charge babel avec l'option « shorthands=off » (codée en dur dans
|
|
son gabarit LaTeX), ce qui désactive définitivement les raccourcis de
|
|
babel-french chargés de l'espace fine avant les ponctuations doubles.
|
|
Aucune réactivation depuis le préambule n'est possible : les raccourcis
|
|
ne sont même pas déclarés (vérifié par sonde sur le catcode de « : »,
|
|
qui reste à 12). Le filtre traite donc le problème en amont, une fois
|
|
pour les deux formats de sortie.
|
|
|
|
Principe directeur : NORMALISER, JAMAIS INVENTER.
|
|
Le filtre ne transforme que les espaces déjà présents dans le
|
|
manuscrit. Un titre anglais cité en note (« ...Teen's Suicide? »), qui
|
|
n'a pas d'espace avant son point d'interrogation, reste donc intact —
|
|
ce qui est la règle : on ne francise pas la ponctuation d'un titre
|
|
étranger.
|
|
|
|
Transformations appliquées :
|
|
1. Espace fine insécable (U+202F) avant : ; ! ? et avant »
|
|
2. Espace fine insécable après «
|
|
3. Espace insécable (U+00A0) avant %
|
|
4. Espace fine insécable dans les groupes de milliers (150 000)
|
|
|
|
Rendu LaTeX : U+202F et U+00A0 sont déclarés dans preambule.tex
|
|
(\DeclareUnicodeCharacter) pour produire un crénage rigide, qui ne
|
|
s'étire pas à la justification.
|
|
]]
|
|
|
|
local FINE = "\u{202F}" -- espace fine insécable
|
|
local INSEC = "\u{00A0}" -- espace insécable de largeur normale
|
|
|
|
-- Ponctuations qui réclament une espace fine insécable AVANT elles.
|
|
-- Le guillemet fermant est testé à part, et non dans la classe
|
|
-- [:;!?] : les classes de caractères de Lua travaillent sur les OCTETS,
|
|
-- si bien que « » » (0xC2 0xBB) placé dans une classe y ferait aussi
|
|
-- entrer « « » (0xC2 0xAB), qui partage son premier octet — bug
|
|
-- constaté, le guillemet ouvrant recevait l'espace du mauvais côté.
|
|
local function needs_fine_before(text)
|
|
return text:match("^[:;!?]") ~= nil or text:match("^»") ~= nil
|
|
end
|
|
|
|
local function ends_with_opening_quote(text)
|
|
return text:match("«$") ~= nil
|
|
end
|
|
|
|
local function ends_with_digit(text)
|
|
return text:match("%d$") ~= nil
|
|
end
|
|
|
|
-- Groupe de milliers : exactement trois chiffres, éventuellement suivis
|
|
-- d'une ponctuation, mais jamais d'un quatrième chiffre.
|
|
local function is_thousands_group(text)
|
|
return text:match("^%d%d%d$") ~= nil or text:match("^%d%d%d[^%d]") ~= nil
|
|
end
|
|
|
|
function Inlines(inlines)
|
|
local out = pandoc.List()
|
|
local i = 1
|
|
while i <= #inlines do
|
|
local cur = inlines[i]
|
|
local nxt = inlines[i + 1]
|
|
|
|
-- « ... » : espace fine collée au guillemet ouvrant
|
|
if cur.t == "Str" and ends_with_opening_quote(cur.text)
|
|
and nxt and nxt.t == "Space" then
|
|
out:insert(pandoc.Str(cur.text .. FINE))
|
|
i = i + 2
|
|
|
|
elseif cur.t == "Space" and nxt and nxt.t == "Str" then
|
|
if needs_fine_before(nxt.text) then
|
|
-- espace fine avant : ; ! ? »
|
|
out:insert(pandoc.Str(FINE .. nxt.text))
|
|
i = i + 2
|
|
elseif nxt.text:match("^%%") then
|
|
-- espace insécable avant le signe pour cent
|
|
out:insert(pandoc.Str(INSEC .. nxt.text))
|
|
i = i + 2
|
|
elseif is_thousands_group(nxt.text) and i > 1
|
|
and inlines[i - 1].t == "Str"
|
|
and ends_with_digit(inlines[i - 1].text) then
|
|
-- séparateur de milliers insécable : 150 000, 32 millions
|
|
out:insert(pandoc.Str(FINE .. nxt.text))
|
|
i = i + 2
|
|
else
|
|
out:insert(cur)
|
|
i = i + 1
|
|
end
|
|
|
|
else
|
|
out:insert(cur)
|
|
i = i + 1
|
|
end
|
|
end
|
|
return out
|
|
end
|