Ce projet est né de l’envie de créer mon propre journal guidé autour du Yams. Je voulais garder une trace de mes réflexions comme dans un journal personnel mais rattachée à un lieu et à une activité précise. Je voulais aussi faire ma propre grille de score.

Est venu ensuite le journal guidé d’aventure né de l’envie de créer un carnet de notes pour mes aventures. Je l’ai construit à partir de mes préparations et de mes retours d’expérience de voyage à vélo.

1. Les carnets

1.1 Le Yatzy Journal

Le premier carnet propose trois versions du Yams. Chacune a sa grille de score ses règles et sa façon de compter les points. Le tout est décliné en trois langues.

Après les grilles de score vient un journal guidé de cinq pages thématiques qui se répètent à chaque nouvelle partie. On y note le contexte du moment (lieu, météo, ce qu’on boit et mange), les faits marquants du jeu, ses sensations et ses réflexions. La dernière page est un espace libre de création.

1.2 L’Adventure Journal

Le second carnet contient un journal guidé de six pages dédié à une aventure. Il suit le fil du voyage : le départ et le retour, les lieux et les paysages traversés, la vie sur la route (compagnons, campement, spécialités culinaires), puis les rencontres, les mésaventures, les conseils pour la prochaine fois et un espace libre de création. Ces six pages se répètent pour pouvoir noter plusieurs aventures.

En complément le carnet contient en dernière partie une checklist du matériel et de l’équipement à ne pas oublier avant le départ.

2. Les besoins

Mes besoins de départ tenaient en trois points : le format, la séparation de la forme et du fond, et l’automatisation.

Le format : Je voulais un carnet de poche assez compact pour se glisser dans un sac ou une sacoche de vélo.

Séparer la forme du fond : Je voulais pouvoir modifier un texte sans toucher à la mise en page.

L’automatisation : Je voulais produire plusieurs carnets, en plusieurs langues, avec des sections répétées, des variantes de couleurs et une mise en page cohérente. Il fallait aussi pouvoir régénérer le document complet après une modification, travailler sur chaque partie séparément et obtenir des PDF prêts à envoyer à l’imprimeur.

3. Les contraintes

J’ai choisi le format A6 (108 × 175 mm). Toutes les plateformes d’impression à la demande ne proposent pas ce format. Ce critère a guidé mon choix vers Lulu.

Ce choix a fixé une partie des contraintes techniques : dimensions exactes des pages, marges de sécurité, zones de rognage, caractéristiques des fichiers à fournir. Elles ont influencé la conception des carnets dès le départ.

4. Les choix techniques

4.1 Les logiciels de mise en page

Dans le monde du logiciel libre des applications comme Scribus ou Inkscape permettent de créer des documents destinés à l’impression. Elles conviennent très bien pour un carnet unique.

Mais dans le cadre de mon projet j’ai estimé qu’elles n’étaient pas adaptées car il cumule plusieurs langues, des sections répétées, des variantes de couleurs et une mise en page à garder identique. Refaire tout cela à la main devient vite fastidieux. J’ai donc décidé de construire un programme sur mesure.

4.2 Séparer la forme du fond

Je choisis de gérer la position des traits, les espacements, les marges, les tailles de texte et les polices par un programme Python. C’est le langage que je connais le mieux il est très répandu et on trouve beaucoup d’aide sur internet.

Les textes sont stockés à part au format JSON. C’est un format simple à lire et à modifier à la main. Je peux ainsi modifier un texte sans toucher à la mise en page. Il me suffit ensuite de lancer une ligne de commande pour générer le document complet.

Les carnets guidés contiennent des sections qui se répètent : une page de journal, une grille, une checklist. Je choisis donc de découper chaque partie du carnet en composants réutilisables que j’assemble ensuite pour former le carnet complet. Le but est de créer chaque composant une seule fois et de le réutiliser y compris d’un carnet à l’autre.

5. La conception

Chaque partie du carnet a son propre sous-répertoire. Il contient :

  • un fichier JSON avec le contenu
  • un script Python qui met ce contenu en forme

Un programme global build.py appelle tous les sous-programmes et gère l’assemblage final.

build/
├── preface/
│   ├── generate.py
│   └── content.json
├── how-to/
│   ├── generate.py
│   └── content.json
├── guided-journal/
│   ├── generate.py
│   └── content.json
├── checklist/
│   ├── generate.py
│   └── content.json
├── cover/
│   ├── generate.py
│   └── colors.json
└── build.py      ← assemblage final

5.1 Le contenu : les textes en JSON

Voici un exemple très simplifié de content.json pour une page de journal :

{
  "fr": {
    "pages": [
      {
        "id": 5,
        "fields": [
          { "label": "RENCONTRES", "lines": 5 },
          { "label": "MOMENT OU DÉCOUVERTE INATTENDUE", "lines": 5 },
          { "label": "MÉSAVENTURE", "lines": 5 },
          { "label": "CONSEILS POUR LA PROCHAINE FOIS", "lines": 4 }
        ]
      }
    ]
  }
}

Le fichier contient les textes et le nombre de lignes d’écriture sous chaque texte. La mise en page reste du ressort du programme Python.

5.2 La mise en page : le script Python

Voici une version simplifiée d’un generate.py.

Le script lit le fichier content.json puis construit une page SVG au format A6. Il place ensuite les titres les uns sous les autres, chacun suivi de son nombre de lignes d’écriture et résultat est converti en PDF.

import argparse
import json
import os
import subprocess
import xml.etree.ElementTree as ET

# ==================================================
# CONFIG A6
# ==================================================
A6 = {"width": 108, "height": 175, "margin": 12.7}
TEXT = {"font": "Inter", "size": 3}

# ==================================================
# GÉNÉRATEUR SVG
# ==================================================
def generate_page(config):
    """Génère une page SVG à partir du JSON"""
    svg = ET.Element("svg", {
        "xmlns": "http://www.w3.org/2000/svg",
        "width": f"{A6['width']}mm",
        "height": f"{A6['height']}mm",
        "viewBox": f"0 0 {A6['width']} {A6['height']}"
    })

    # Style
    style = ET.SubElement(svg, "style")
    style.text = f"""
    .label {{ font-size: 4px; font-weight: 600; font-family: '{TEXT['font']}'; }}
    .line {{ stroke: black; stroke-width: 0.2px; }}
    """

    margin = A6["margin"]
    y = margin
    line_height = 6  # mm

    # Récupère la première page
    page = config["fr"]["pages"][0]

    # Pour chaque champ
    for field in page["fields"]:
        # Label
        ET.SubElement(svg, "text", {"x": str(margin), "y": str(y), "class": "label"}).text = field["label"]
        y += 5

        # Lignes
        for _ in range(field["lines"]):
            ET.SubElement(svg, "line", {"x1": str(margin), "y1": str(y), "x2": str(A6["width"] - margin), "y2": str(y), "class": "line"})
            y += line_height

        y += 3  # Espace entre les champs

    return svg

# ==================================================
# EXPORT PDF
# ==================================================
def svg_to_pdf(svg_file, pdf_file):
    """Convertit SVG en PDF avec inkscape"""
    subprocess.run(["inkscape", "--export-type=pdf", f"--export-filename={pdf_file}", svg_file], check=True, capture_output=True)

# ==================================================
# CLI
# ==================================================
if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--output", "-o", default="page.pdf")
    args = parser.parse_args()

    # Charge le JSON
    with open("content.json", encoding="utf-8") as f:
        config = json.load(f)

    # Génère le SVG, le convertit en PDF, puis supprime le SVG
    svg_file = args.output.replace(".pdf", ".svg")
    ET.ElementTree(generate_page(config)).write(svg_file, encoding="utf-8", xml_declaration=True)
    svg_to_pdf(svg_file, args.output)
    os.remove(svg_file)

Dans la version réelle les scripts gèrent davantage d’éléments : les retours à la ligne, les blocs colorés, les différents types de pages et le choix de la langue.

6. La réalisation

6.1 Travailler partie par partie

Chaque composant sait générer sa propre partie du carnet. Je peux générer uniquement la préface, une page de journal ou la checklist puis produire un PDF pour vérifier le résultat. J’avance pas à pas sans régénérer tout le carnet à chaque modification.

6.2 L’assemblage final

Quand chaque partie me convient build.py appelle les sous-programmes et produit le document final. Il gère :

  • l’ordre des sections
  • l’insertion de pages blanches
  • la répétition de certaines parties
  • la sélection de la langue
  • l’assemblage des fichiers PDF

6.3 Une base commune

Les titres, pieds de page, marges, espacements et styles suivent la même logique dans tous les carnets. Le contenu change mais la composition reste similaire. C’est ce qui donne à l’ensemble l’allure d’une collection cohérente.

7. Où acheter mes carnets

Les deux carnets sont disponibles sur la boutique Lulu :

  • Adventure Journal : carnet guidé pour les voyages à vélo ou autrement, 193 pages, couverture rigide, format de poche.
  • Yatzy Journal : trois versions du jeu avec leur grille de score, accompagnées d’un journal guidé, en trois langues, 193 pages, couverture rigide, format de poche.

Conclusion

Le système fonctionne bien. Une fois la logique en place pour le premier carnet le deuxième a été beaucoup plus rapide à produire.

Comme les deux carnets reposent sur le même programme ils partagent le même format et une composition proche : position des titres, pieds de page, marges, organisation générale.

J’ai maintenant l’outil et la méthode pour continuer à faire grandir ma collection. Quel sera le prochain carnet ? À suivre :)