Génération de rapports PDF professionnels en chinois
🚨 Leçons douloureuses de deux corrections (2026-08-20, priorité absolue, ne plus recommencer)
- Mise en page compacte : Interdiction de mettre une page entière pour un tout petit contenu. N'ajoutez pas de
PageBreakà chaque chapitre pour gonfler le nombre de pages — un seulPageBreakaprès la couverture, le corps du texte doit s'écouler naturellement. Les 6 pages précédentes doivent être compressées en 4. - Police large (lisible sur mobile, le client regarde sur son téléphone) : Corps du texte ≥16pt, tableaux ≥15pt, H2 ≥17pt, H1 ≥21pt, titre ≥30pt. Un corps à 14pt et des tableaux à 12pt sont trop petits pour un mobile — le client ne peut pas lire, et l'utilisateur le critiquera immédiatement.
- Ces deux règles sont également écrites dans les normes de mise en page en mémoire, mais lors de l'exécution, on est encore tenté d'utiliser 14pt/12pt + saut de page par chapitre — avant d'écrire le script de génération, vérifiez impérativement les constantes de police et le nombre de
PageBreak.
⭐ Normes de mise en page générales (décidées par l'utilisateur le 2026-08-18, communes à PDF et Word)
Cette logique de mise en page est la norme — tous les futurs documents PDF et Word doivent s'y conformer :
- A4 portrait (210×297 mm) — pas de paysage ni d'autres formats
- Police large, lisible sur mobile : Corps ≥14pt, tableaux ≥12.5pt, H1 ≥20pt, titre ≥32pt
- Habillage intelligent des tableaux, pas de débordement : cellules avec Paragraph + wordWrap="CJK"
- Équilibrage du contenu des pages : pas de saut de page forcé,
KeepTogetherpour des blocs intelligents, vérifier le nombre de caractères par page après génération (sauf couverture/dernière page, les autres pages doivent être équilibrées) - Mise en page compacte : marges 18-20 mm, padding des tableaux 3-4, interlignage serré, prêt à imprimer
Même logique pour les documents Word : A4 portrait, corps ≥ Petit 4 / Taille 4 (12-14 pt), largeur des colonnes de tableau adaptative sans débordement, pages équilibrées.
Conditions de déclenchement
- L'utilisateur/client demande "faire les conclusions en PDF", "envoyer le PDF", "donne-moi l'étude de faisabilité", "fais les conclusions et envoie-les"
- Nécessité de livrer un rapport d'analyse professionnellement mis en page (couverture + tableaux + pied de page + encadré des conclusions clés)
- Exemple de référence : Rapport de due diligence de Guoxun Robot (5 pages, 02-Project Results/Rapport de faisabilité et d'analyse de due diligence du projet Guoxun Robot.pdf)
Environnement et installation
- Utilisez l'interpréteur hermes venv :
python - Première installation :
$V -m pip install reportlab(déjà installé en version 5.0.0) - Police : Microsoft YaHei intégrée au système Windows, pas besoin de télécharger de fichier de police
C:/Windows/Fonts/msyh.ttc(normale, subfontIndex=0)C:/Windows/Fonts/msyhbd.ttc(gras)C:/Windows/Fonts/msyhl.ttc(fin)
Flux de travail standard
- ⚠️ Règle de livraison absolue (2026-09-02, spécifiée par l'utilisateur, priorité absolue) : Après la génération du PDF, il doit être envoyé dans le chat Feishu via
MEDIA:, jamais seulement stocké dans la base de connaissances / donné en chemin / résumé en points clés. Paroles de l'utilisateur : "Le document PDF, tu dois l'envoyer ici dans la boîte de chat Feishu, sinon comment pourrais-je le voir ? Il faut que cela devienne une règle absolue."- L'utilisateur regarde sur son mobile — il ne voit le fichier qu'en cliquant sur le message Feishu ; le sauvegarder dans
02-Project Results/n'est qu'une sauvegarde, pas une livraison. - Format de livraison :
<votre répertoire de sortie>/<nom du fichier>.pdf(chemin absolu). - Après l'envoi du PDF, joignez les points clés des conclusions (résumé 30 secondes). Envoyez d'abord sur Feishu, puis stockez dans la base de connaissances pour révision.
- L'utilisateur regarde sur son mobile — il ne voit le fichier qu'en cliquant sur le message Feishu ; le sauvegarder dans
- Récupérer le contenu :
session_searchpour rechercher les analyses historiques — l'utilisateur répond souvent "faire les conclusions en PDF" à une pièce jointe, donc retrouvez d'abord les analyses/jugements précédents, ne réécrivez pas. - Écrire le script : Copiez
templates/reportlab_chinese_template.py, remplacez uniquement la zone de contenu (texte de couverture, chapitres, données de tableaux). - Générer : Exécutez avec python → sortie vers Obsidian
Hermes工作成果/02-项目成果/(nom : Rapport de faisabilité et d'analyse de due diligence du projet.pdf) - Vérifier : Ouvrez avec pymupdf, lisez avec
get_text, confirmez le nombre de pages / le rendu du chinois / la présence des conclusions en dernière page. - Livrer : Répondez avec le chemin
MEDIA:D:\...pdf+ les points clés des conclusions (résumé 30 secondes), sans bavardage.
⭐ Règles de couleurs absolues (2026-09-02, décidées par l'utilisateur, couleurs porte-bonheur Or/Terre — priorité absolue)
Le thème astrologique de l'utilisateur aime l'Or et la Terre, déteste l'Eau (bleu/noir) et le Feu (rouge/violet). Tous les PDF doivent utiliser des couleurs de la famille Or/Terre, interdiction d'utiliser le bleu. Les variables de couleur sont dans la palette ci-dessous.
- Titre de chapitre H1 : Fond Or métallique + texte blanc (
backColor=#C9A227,textColor=white,borderPadding=(6,8,6,8)) — fond or, texte blanc, pas de texte bleu. - En-tête de tableau : Fond Or métallique + texte blanc (
header_bg=#C9A227,FONTNAME=MSYHBD) — fond or, texte blanc. - Corps du texte : Noir pur
#1f1f1f. - Sous-titres / lignes de séparation : Or/Terre foncé
#B8860B(sans rouge). - Blocs de code : Or/Terre
#8a6d1f(sans bleu). - Lignes alternées : Beige/Or clair
#FBF3DE(tons chauds, pas gris/blanc). - Lignes de grille : Or/Terre clair
#D9C9A3.
Palette Or (pour reportlab)
C_DARK=colors.HexColor("#1f1f1f") # Corps presque noir
C_RED=colors.HexColor("#B8860B") # Accent/sous-titre - Or/Terre foncé
C_BLUE=colors.HexColor("#C9A227") # Fond d'en-tête/titre - Or métallique
C_LGRAY=colors.HexColor("#FBF3DE") # Lignes alternées - Beige/Or clair
C_MGRAY=colors.HexColor("#D9C9A3") # Lignes de grille - Or/Terre clair
C_WHITE=colors.white
Accentuation en ligne
S_CODE=st(..., textColor=colors.HexColor("#8a6d1f")); en ligne<span color='#B8860B'>(pas de#c0392brouge).
Points clés de mise en page (déjà encapsulés dans le template)
- Page de couverture : Grand titre + sous-titre Or + date/source + encadré des conclusions clés (fond Or/Terre foncé, texte blanc)
- Corps du texte : H1 Chapitres (fond Or métallique, texte blanc, MSYHBD 21pt) + H2 Sous-sections (17pt) + Corps 15pt (2026-08-18, instruction utilisateur : lisible sur mobile, la police doit être grande)
- Tableaux :
make_table()en-tête fond Or métallique texte blanc + lignes alternées (ROWBACKGROUNDS) + grid + repeatRows=1, taille de police des tableaux 13pt - Pied de page : Callback onPage pour dessiner le nom du rapport + numéro de page
- Accentuation : Paragraph supporte
<b>/<span color='#B8860B'>pour les marques en ligne - Rapports longs divisés en chapitres : PageBreak pour séparer la couverture du corps
⚠️ Règle de taille de police absolue (2026-08-18, soulignée par l'utilisateur) : Le PDF est destiné à être lu sur mobile, corps ≥15pt, tableaux ≥13pt, H1 ≥21pt, titre ≥34pt. L'ancien template à 10.5pt/9.3pt est trop petit pour un mobile — tous les nouveaux rapports doivent utiliser de grandes polices. Une police plus grande peut augmenter le nombre de pages (ex. 5 pages → 8 pages).
⭐ Règles de mise en page intelligente absolues (2026-08-18, soulignées par l'utilisateur, adaptées à l'impression) Le contenu des pages doit être équilibré — pas de page avec seulement 200 caractères et une autre avec 800. Méthode :
- Utiliser peu de PageBreak : Un seul saut de page forcé après la couverture, le corps s'écoule naturellement
- Utiliser KeepTogether pour encapsuler "titre + contenu" :
keep(titre H2, tableau/paragraphe)pour éviter qu'un tableau ne soit coupé sur deux pages, ou qu'un titre ne reste isolé en bas de page - La couverture est une page indépendante : Éléments de couverture + un PageBreak
- Chaque chapitre est naturellement séparé par des sous-sections : Titre H2 + tableau/points clés, pas de saut de page forcé par chapitre
- Vérifier l'équilibrage : Après la génération, utilisez pymupdf pour vérifier le nombre de caractères par page — dans un rapport normal, sauf la couverture/dernière page, le nombre de caractères par page doit être similaire (par ex. 600-900 caractères). Si trop différent, ajustez.
- Mise en page compacte : Marges 20mm/16mm (pas trop grandes), padding des tableaux 4-5, interlignage leading=22 (pour 14pt), réduire le gaspillage d'espace blanc.
Pièges (PITFALLS)
- ⛔ Interdiction de la justification (TA_JUSTIFY) — l'espacement entre les caractères est trop large (2026-09-03, problème de lisibilité mobile le plus important pour l'utilisateur) :
TA_JUSTIFYde reportlab force l'étirement de chaque ligne jusqu'à la marge droite pour les paragraphes chinois, et quand une ligne a peu de caractères, l'espacement devient extrêmement large et laid. Tout le corps / listes / paragraphes doivent utiliserTA_LEFT(alignement à gauche) .- Erreur :
b=dict(..., alignment=TA_JUSTIFY, ...) - Correct :
b=dict(..., alignment=TA_LEFT, ...) - Vérification : Si les styles de corps/liste héritent de
st()avecalignment=TA_JUSTIFY, il faut impérativement passer à gauche.
- Erreur :
- ⛔ Le contenu de la couverture déborde sur une page blanche isolée (2026-09-03) : Tous les éléments de la couverture (titre principal + sous-titre + explication + phrase clé) doivent être contenus dans la page 1. Il est strictement interdit que le contenu de la couverture soit repoussé sur la page 2 (un petit morceau de texte + une grande zone blanche). Si le contenu de la couverture est trop long, supprimez les longs paragraphes d'introduction, compressez les listes pour que tout tienne en 1 page. Ne conservez qu'un seul PageBreak après la couverture, le corps s'écoule naturellement.
- ⛔ La taille de police et les marges doivent être ajustées simultanément : Après avoir augmenté la police, réduisez les marges en conséquence (dans cette session on est passé de 22mm à 17mm) pour que chaque page contienne plus de contenu et éviter les pages isolées. Police à 17pt + marge à 17mm est le point d'équilibre testé ; police des tableaux à 14.5pt.
- ⛔ KeepTogether encapsulant un "petit tableau d'alignement personnalisé" peut entraîner une mauvaise estimation de hauteur de page → page isolée (2026-09-06, testé) : En utilisant KeepTogether pour encapsuler "titre + corps + tableau d'alignement deux colonnes personnalisé (field_table) + prix", reportlab estime mal la hauteur de ce bloc, le pousse souvent à la page suivante, laissant sur la page précédente seulement une "phrase d'info / une ligne de fin de tableau" qui devient une page isolée de quelques dizaines de caractères (dans cette session, page 2 avec seulement 76 caractères). Correction : ① Pour la première page, utiliser KeepTogether pour encapsuler tout le bloc "tableau des services + introduction" et compresser la police du tableau / l'interlignage pour que tout tienne en page 1 ; ② Après le rendu des blocs, utiliser pymupdf pour vérifier page par page le nombre de caractères, si <120 caractères, c'est une page isolée — compressez ou supprimez immédiatement les phrases d'info en trop.
- Distinction (2026-09-10, testé) : Encapsuler uniquement "le tableau lui-même" est précis et doit être fait ; encapsuler un gros bloc "titre + corps + tableau + prix" peut facilement causer une erreur d'estimation. Faites en sorte que la fonction usine de tableau retourne
return KeepTogether(t)(le tableau entier ne se divise pas sur plusieurs pages), et en ne conservant qu'un seul PageBreak après la couverture, testé en passant de 7 pages (page 5 à seulement 77 caractères) à 6 pages, avec 301/451/548/407/551/240 caractères par page. La division d'un tableau entre deux pages, avec 1-2 lignes se retrouvant seules sur une page, est la première cause de pages isolées ; si vous trouvez une page avec seulement un en-tête de tableau / une ligne de fin, vérifiez d'abord ce point.
- Distinction (2026-09-10, testé) : Encapsuler uniquement "le tableau lui-même" est précis et doit être fait ; encapsuler un gros bloc "titre + corps + tableau + prix" peut facilement causer une erreur d'estimation. Faites en sorte que la fonction usine de tableau retourne
- ⛔ La colonne d'étiquette d'un tableau d'alignement à deux colonnes "étiquette + contenu" ne doit pas être trop étroite — les étiquettes chinoises peuvent passer à la ligne avec des caractères isolés (2026-09-06) : Lors de l'utilisation d'un tableau d'alignement à deux colonnes pour "ce qui peut être fait / jusqu'où on va / ce que vous obtenez", si la colonne d'étiquette est trop étroite (par ex. 32mm), "ce que vous obtenez" peut passer à la ligne en "ce que vous obt" + "enez", créant des caractères isolés très laids. Correction : La largeur de la colonne d'étiquette doit être > la longueur de l'étiquette la plus longue sur une seule ligne (environ 40mm), ou raccourcir l'étiquette ("ce que vous obtenez" → "bénéfice").
- ⛔ Le point final " 。" en fin de phrase longue peut passer seul à la ligne suivante, devenant un point-virgule orphelin (2026-09-06) : Pour des champs longs comme "jusqu'où on va / bénéfice", le point final en fin de phrase peut être repoussé au début de la ligne suivante. Correction : Si la valeur du champ est longue et risque de passer à la ligne, supprimez le point final (ou utilisez des phrases courtes qui l'évitent). Mieux vaut pas de point qu'un point isolé.
- ⛔ "Compétences personnalisées" doivent être formulées correctement (2026-09-06, corrigé par l'utilisateur) : Ne pas écrire "compétences personnalisées/tâches personnalisées" par erreur comme "tâches programmées". La version client utilise uniformément "Personnalisation de skills exclusifs" avec une explication simple "un skill = une capacité concrète que l'IA peut exécuter selon vos instructions" (voir enterprise-ai-service-blueprint).
- Les polices
.ttcdoivent utilisersubfontIndex=0, sinon erreur ou caractères illisibles. - Le gras doit être enregistré sous MSYHBD et le fontName défini — la balise
<b>ne fonctionne pas silencieusement sans enregistrement de police grasse. - L'échappement des
<>doit être traité en deux cas (2026-09-10, testé) : Les chevrons nus dans le corps du texte (par ex. "précision <10mm") doivent être échappés en</>; alors que<b>,<span color=...>sont des balises en ligne, elles doivent être passées littéralement à Paragraph pour fonctionner.- ⛔ Erreur fatale :
.replace('<','<')appliqué à tout le texte des cellules → le gras dans les tableaux s'affiche littéralement comme<b>gras(dans cette session, toute une colonne de la page 2 a imprimé<b>Prise en charge nominale…— découvert juste avant la livraison). - ✅ Correction : Dans les cellules de tableau, n'échapper que les
&nus (v.replace('&','&')), conserver les balises en ligne ; les chevrons nus dans le contenu doivent être écrits par l'appelant lui-même comme<. - Auto-vérification : Avant la livraison, capturez tout le texte ; si
'<b>' in textou'</b>' in textest vrai, cela signifie que les balises sont rendues comme du texte bruti, il faut revenir à l'échappement dansmake_table.
- ⛔ Erreur fatale :
- La somme des largeurs de colonnes d'un tableau doit être ≤ la largeur disponible A4 (210 mm - marges gauche et droite), sinon le débordement force un retour à la ligne disgracieux.
- Débordement du texte chinois dans les tableaux (2026-08-18, correction importante) : La classe Table de reportlab, avec du texte chinois long directement placé, ne passe pas automatiquement à la ligne — il déborde sur la droite ! Il est impératif d'utiliser Paragraph pour encapsuler les cellules +
wordWrap="CJK". Déjà intégré dansmake_tablevia_cell_style()— toutes les cellules passent automatiquement à la ligne. Vérification : Utilisez pymupdf pour vérifier si lesx2deget_text('words')dépassent la marge droite de la zone de contenu.- Le seuil doit être calculé selon les marges réelles, pas codé en dur à 540pt (2026-09-10, testé) : Bordure droite =
A4[0] - rightMargin(largeur A4 = 595.3pt ; marge 16mm → 549.9pt, 17mm → 547pt). Un seuil fixe de 540 déclenchera de fausses alertes sur les retours à la ligne normaux. - Exclure impérativement le pied de page : Le "Page N" dessiné par onPage dépasse toujours la zone de contenu. Lors de l'évaluation, filtrez le texte (par ex.
w[4] != '页'), sinon il y aura une fausse alarme à chaque fois. - Exemple d'évaluation :
[w for w in page.get_text('words') if w[2] > page.rect.width - rightMargin + 1 and w[4] != '页']
- Le seuil doit être calculé selon les marges réelles, pas codé en dur à 540pt (2026-09-10, testé) : Bordure droite =
- Règle A4 portrait absolue (insistée par l'utilisateur) : Tous les PDF doivent être en A4 portrait (210×297 mm).
pagesize=A4est le portrait par défaut, ne pas le modifier en paysage. - Avant la livraison, vérifiez impérativement avec pymupdf (nombre de pages / chinois / caractères illisibles / pas de débordement) — n'envoyez qu'une fois la vérification réussie.
- ⛔ Lors de l'écriture du script de génération, les chaînes Python doivent utiliser des guillemets simples comme délimiteurs (2026-09-10, testé) : La chaîne d'outils peut normaliser les guillemets chinois “ ” en guillemets demi-largeur
". Si la chaîne externe utilise des guillemets doubles comme délimiteurs, les guillemets chinois dans le corps du texte fermeront prématurément le littéral →SyntaxError: invalid character '+' (U+FF0B)ou similaire (l'erreur peut sembler due à un caractère pleine largeur, mais la vraie cause est la troncature par les guillemets, ce qui induit en erreur). Les guillemets chinois dans le corps du texte doivent soit utiliser「」, soit encapsuler toute la chaîne avec'...'. - ⛔ Ne jamais lire et écrire le même fichier en même temps :
open(f,'w').write(open(f,encoding='utf-8').read() + add)va d'abord tronquer le fichier à 0 octet avant de le lire, ce qui efface tout le contenu original et ne conserve que le segment ajouté (dans cette session, la note de base de connaissances est passée de 13 Ko à 3.8 Ko, presque livrée en pièce jointe incomplète). Pour ajouter du contenu, lisez d'abordtxt = open(f).read()dans une variable, puisopen(f,'w').write(txt + add); ou utilisez directement un outil de patch. - Les fichiers >100 Ko sont généralement normaux (sous-ensembles de polices intégrées).
Commande de vérification
$V -c "import pymupdf; d=pymupdf.open(r'D:\...\Rapport.pdf'); print(len(d)); print(d[0].get_text()[:120]); print('Dernière page contient conclusion:', 'conclusion' in d[-1].get_text())"
Connexions
- Cadre de contenu :
feasibility-study-writing,dual-lens-business-analysis(à charger avant l'analyse de due diligence) - Règles strictes de révision de rapport : exécutez
report-reflection-reviewavant la sortie - Cas PPT :
ultimate-ppt-master - Template :
templates/reportlab_chinese_template.py(ancien) ;templates/reportlab_gold_template.py(recommandé) : Palette Or + alignement gauche + police 17pt + marges compactes 17mm + pas de pages isolées. Correction de la gestion des balises en ligne dansmake_table()(les<b>/<span>dans les cellules de tableau fonctionnent réellement maintenant, plus échappés en texte littéral).