Générer des PDF personnalisés / Publipostage / Widget Markdown

L’objectif

Créer un modèle de document, qu’on pourra exporter en PDF, et qui se remplit automatiquement avec les données d’une table. Par exemple, une lettre personnalisée avec la date et le nom, prénom et adresse du destinataire :

La base

Dans une première table, on crée notre modèle de document en langage html. Ce code contient notre texte modèle, un genre de phrase « avec des trous » - ou plutôt des variables de template qui seront remplacées par des valeurs au moment du rendu. Ces variables sont reconnaissables car entourées d’accolades {}.

Exemple :

Dans une seconde table, on stocke les informations à afficher via ces variables. Et on ajoute une colonne avec une formule qui permettra de trouver quelle donnée devra être affichée dans quelle variable - la correspondance se fera entre le nom de la variable et le nom d’une colonne de la table de données. Cela va permettre de « remplir les trous » de notre texte.

Cette formule s’écrit :

# Classe utilisée pour la recherche des données
class Find_Data(dict):
  def __missing__(self, key):
    return getattr(rec, key)

# 1. Récupère le modèle du document    
template = Template_Facture.lookupOne().Modele

# 2. Formate le modèle avec les champs de la table actuelle
template.format_map(Find_Data())
   

Dans notre exemple, le champ {nom} du modèle sera remplacé par les données de la colonne « nom » de la table courante.

Dans l’image ci-dessus, pour chaque ligne, on peut observer que le nom a bien été remplacé, c’est donc que notre formule fonctionne bien !

:bulb: Si nous n’avions pas de colonne appelée « nom » dans notre table courante, la formule retournerait une erreur AttributeError : Table 'Liste_de_noms' has no column 'nom'

Nous souhaitons désormais avoir une vue finale de notre document, plutôt que d’avoir le html brut affiché dans une colonne.

Nous allons pour cela utiliser le widget « Markdown », pour l’interprétation du contenu html :

  • Cliquer sur Nouveau > Ajouter une vue > Personnalisée > choisir « Markdown » en sélectionnant la table contenant les données dynamiques
  • Dans la configuration de la vue > Vue, autoriser l’accès complet au document, et dans le champ « content », choisir la colonne formule
  • Dans la configuration de la vue > Données source, lier la vue à la table en la sélectionnant dans le champ « Sélectionner par »

Et c’est tout ! Désormais, lorsque vous parcourez la table, vous verrez apparaître votre modèle personnalisé selon les données sélectionnées. :partying_face:

publip

Un modèle de lettre

Maintenant que nous avons compris le concept de base, nous allons créer un modèle plus complexe : une lettre qui contient une image, de la mise en forme et, évidemment, des variables.

Le code html est le suivant :

Je suis là
<div style="font-family:arial;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<img src="https://upload.wikimedia.org/wikipedia/fr/2/22/Republique-francaise-logo.svg" width="150" />

</div>

<div style="text-align:right;">
{prenom} {nom}

{adresse}


Paris, le {date}
</div>

<div>
Madame, Monsieur,

Vous faites partie de la fabuleuse communauté des Gristeur·euses, et nous vous en remercions. 

Nous vous prions l’assurance de nos sentiments les meilleurs. 
</div> 

<div align="right">
L'équipe Grist.Gouv
</div>

</div>

Comme précédemment, nous stockons ce code dans une table « Template_lettre », colonne « Modèle ».
Il contient les champs dynamiques suivants : {prenom}, {nom}, {adresse}, {date}. Il faudra donc que notre table de données dispose de colonnes qui portent ces mêmes noms.

Notre table de données a également une colonne « formule » contenant la formule de remplacement des variables. Il faut l’éditer pour indiquer le nom de la table dans laquelle aller chercher le modèle (ici, « Template_lettre »).

:bulb: Pour éviter d’avoir des lignes à rallonge dans la table de données - le code html est souvent très long - il peut être pratique de masquer la colonne « formule ».

Nous ajoutons ensuite la vue Markdown, et voici le résultat :

jajehe

Enregistrer le document

Cluiquer sur les ··· en haut à droite de la vue > Imprimer la vue > Enregistrer au format PDF. Il est aussi possible d’imprimer directement le document.

aaeeaee

Afficher des nombres

Si l’on souhaite afficher des nombres, le formatage que l’on choisit sur Grist (par exemple 2 chiffres après la virgule) ne s’applique pas dans la vue Markdown, qui récupère la donnée source non formatée.

Par exemple :

Dans ce cas, on peut modifier la formule pour arrondir la valeur, avec le code
round(valeur , 2)

Formule avec arrondi
import numbers

# Classe utilisée pour la recherche des données
class Find_Data(dict):
  def __missing__(self, key):
    attr = getattr(rec, key)

    # Arrondit les nombres à deux décimales 
    if isinstance(attr, numbers.Number):
      return f"{attr:.2f}"
    else:
      return getattr(rec, key)

# 1. Récupère le modèle du document    
template = Template_Facture.lookupOne().Modele

# 2. Formate le modèle avec les champs de la table actuelle
template.format_map(Find_Data())

Résultat :

Pour des entiers, pensez à bien formater votre colonne en « Entier » et non en « Numérique », pour ne pas afficher les décimales dans votre document (par exemple, afficher 6 plutôt que 6.0)

Afficher des dates au format français

Si l’on souhaite afficher des dates, le formatage que l’on choisit sur Grist (par exemple DD-MM-YYYY) ne s’applique pas dans la vue Markdown, qui récupère la donnée source non formatée.

Il faut modifier la formule pour faire ce formatage avec

if isinstance(attr, datetime.date):
      return attr.strftime(format = '%d-%m-%Y')
Formule avec formatage date et arrondi nombre
import datetime
import numbers

# Classe utilisée pour la recherche des données
class Find_Data(dict):
  def __missing__(self, key):
    attr = getattr(rec, key)
    
    # Formate la date en français
    if isinstance(attr, datetime.date):
      return attr.strftime(format = '%d-%m-%Y')
    
    # Arrondit les nombres à deux décimales 
    if isinstance(attr, numbers.Number):
      return round(attr, 2)
    else:
      return getattr(rec, key)

# 1. Récupère le modèle du document    
template = Template_Facture.lookupOne().Modele

# 2. Formate le modèle avec les champs de la table actuelle
template.format_map(Find_Data())

Ajouter le CSS au début du code HTML

Jusqu’ici nous avons intégré le style css élément par élément, à l’aide de l’attribut style. Par exemple nous avons écrit : <div style="text-align:right;">

Cette méthode peut convenir pour des documents très simples, mais devient ingérable lorsque l’on souhaite faire une mise en forme plus avancée : il faudrait répéter le style pour chaque balise de même type.

La bonne pratique est d’ajouter le css « à part », dans l’en-tête du code HTML, dans l’élément <head>. La structure sera la suivante :

<!DOCTYPE html>
<html>
<head>
<style>
h1 
{
  color:blue; 
  font-size:14px;
}
</style>
</head>
<body>
<h1>body ohdy</h1>
</body>
</html>

Le problème qui se pose, c’est qu’écrire le style css de cette manière nous fait utiliser des {}, et notre formule de génération du Markdown considère les accolades comme des variables… Or, elles ne servent ici qu’à définir le style ! Pour éviter des erreurs (par exemple ici Grist nous retournerait une erreur « AttributeError : Table 'Ma_table' has no column 'h1' ») , nous allons utiliser une astuce : « échapper » les accolades en les doublant. Par exemple :

h1 
{{
  color:blue; 
  font-size:14px;
}}

Voici le résultat d’un document au style avancé : une facture pour le bois d’affouage : Template Grist : Factures personnalisées

Happy Gristing! :hugs:

Liens utiles :

10 « J'aime »

Merci @audezu

Est-ce que tu verrais une solution pour formater la date qui s’affiche au format anglosaxon après fusion alors qu’elle est bien au format FR dans la table ?

Éric

Avec plaisir !
Oui, bien vu, il faut tester si le champ est une date, et, si oui, faire le formatage :

if isinstance(attr, datetime.date):
      return attr.strftime(format = '%d-%m-%Y')

La formule finale :

import datetime
import numbers

# Classe utilisée pour la recherche des données
class Find_Data(dict):
  def __missing__(self, key):
    attr = getattr(rec, key)
    
    # Formate la date en français
    if isinstance(attr, datetime.date):
      return attr.strftime(format = '%d-%m-%Y')
    
    # Arrondit les nombres à deux décimales 
    if isinstance(attr, numbers.Number):
      return round(attr, 2)
    else:
      return getattr(rec, key)

# 1. Récupère le modèle du document    
template = Template_Facture.lookupOne().Modele

# 2. Formate le modèle avec les champs de la table actuelle
template.format_map(Find_Data())

Je vais ajouter cela au post d’origine. Merci @ecouillard !

2 « J'aime »

Merci, y a vraiment du potentiel mais je vois quand même une limite : le non-respect de la charte graphique de l’Etat, ne serait-ce que par l’absence de la police Marianne :grimacing: :cry:
Immédiatement, je ne vois pas de solution mais si quelqu’un ou quelqu’une veut s’y lancer !
Je souhaite juste suggérer une piste d’amélioration et donner une idée de fonctionnalité à apporter.

1 « J'aime »

D’abord un grand merci, c’est clair et ça fonctionne.
Dans mon cas d’usage, je rencontre un problème, les cellules vides m’affichent un disgracieux None là où je préférerais une chaine vide.

Voyez-vous une solution ?

Bonjour @Arnault , merci de ce retour :slight_smile:

Vous pouvez ajouter ce bout de code dans la fonction « Find_Data » :

    if attr is None:
      return " "

ou si vous voulez remplacer par un tiret, return "-"

La formule finale :

import datetime
import numbers

# Classe utilisée pour la recherche des données
class Find_Data(dict):
  def __missing__(self, key):
    attr = getattr(rec, key)
    
    # Ne retourne rien si le champ est vide
    if attr is None:
      return ""
    
    # Formate la date en français
    if isinstance(attr, datetime.date):
      return attr.strftime(format = '%d-%m-%Y')
    
    # Arrondit les nombres à deux décimales 
    if isinstance(attr, numbers.Number):
      return round(attr, 2)
    else:
      return getattr(rec, key)

# 1. Récupère le modèle du document    
template = Template_lettre.lookupOne().Modele

# 2. Formate le modèle avec les champs de la table actuelle
template.format_map(Find_Data())

Est-ce que ça fonctionne pour vous ?

3 « J'aime »

Je suis persuadé que cela fonctionne et je le mettrai en œuvre dès que possible. Dans l’attente j’avais modifié toutes mes formules pour qu’elles renvoient " " au lieu de None :slight_smile:
Merci beaucoup pour cette réponse rapide.

@audezu

je le mettrai en œuvre dès que possible

Bon, j’ai testé, c’est mieux pour les lecteurs ici et ça fonctionne parfaitement.
Encore encore merci

1 « J'aime »

Je rajoute ma pierre à l’édifice pour ceux qui passeraient par là si vous cherchez à récupérer de la donnée depuis un tableau récapitulatif / summary table et modifier le html dynamiquement en fonction du nombre de ligne pour avoir des formats adaptatifs (en l’occurence ici un format de tableau), voici un exemple fonctionnel pour une fiche annuaire (toujours utile en collectivité pour savoir qui contacter)

import datetime
import numbers

# 0. Récupérer variable de la mairie
Nom_Commune = $Nom_commune
CTM = Cordonnees_Mairie.lookupOne(Nom_Commune=$Nom_commune).CTM
Adresse_Postale = Cordonnees_Mairie.lookupOne(Nom_Commune=$Nom_commune).Adresse_Mairie
Telephone_mairie = Cordonnees_Mairie.lookupOne(Nom_Commune=$Nom_commune).Telephone_Mairie
Adresse_mail = Cordonnees_Mairie.lookupOne(Nom_Commune=$Nom_commune).Mail_Maire
Site_web = Cordonnees_Mairie.lookupOne(Nom_Commune=$Nom_commune).Site_internet_Mairie
Maire = Cordonnees_Mairie.lookupOne(Nom_Commune=$Nom_commune).Maire_et_mandats
DGS = Cordonnees_Mairie.lookupOne(Nom_Commune=$Nom_commune).Mail

# 1. Connaître le nombre de lignes 
nb_lignes = rec.count

# 2. Récupère toutes les lignes du groupe
fonctions = [lignes.Fonction for lignes in $group]
prenoms_noms = [lignes.Prenom_Nom for lignes in $group]
telephone = [lignes.Telephone for lignes in $group]
mail = [lignes.Mail for lignes in $group]

# Générer les lignes du tableau HTML
lignes_html = ""
for i in range(len(fonctions)):
    lignes_html += "<tr>"
    lignes_html += "<td style='border: 1px solid #ddd; padding: 8px; font-size: 14px;'>" + str(fonctions[i]) + "</td>"
    lignes_html += "<td style='border: 1px solid #ddd; padding: 8px; font-size: 14px;'>" + str(prenoms_noms[i]) + "</td>"
    lignes_html += "<td style='border: 1px solid #ddd; padding: 8px; font-size: 14px;'>" + str(telephone[i]) + "</td>"
    lignes_html += "<td style='border: 1px solid #ddd; padding: 8px; font-size: 14px;'>" + str(mail[i]) + "</td>"
    lignes_html += "</tr>"

# Template HTML utilisant des quotes simples
template_base = "<div style='font-family: Arial, sans-serif; padding: 20px; max-width: 800px;'>"
template_base += "<div style='display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 30px;'>"
template_base += "<div>"
template_base += "<h2 style='margin: 0 0 10px 0; font-size: 20px; color: #333;'>Fiche annuaire élus - " + str(Nom_Commune) + "</h2>"
template_base += "<p style='margin: 0; font-size: 14px; color: #666;'>" + str(CTM) + "</p>"
template_base += "</div>"
template_base += "<div style='display: flex; align-items: center;'>"
template_base += "</div>"
template_base += "<img src='https://upload.wikimedia.org/wikipedia/fr/b/b8/Logo_M%C3%A9tropole_Lyon_-_2022.svg' style='height: 80px; width: auto;' />"
template_base += "</div>"
template_base += "</div>"

# Tableau d'informations de la mairie
template_base += "<table style='border-collapse: collapse; width: 100%; margin-bottom: 20px; font-size: 14px;'>"
template_base += "<tr><td style='border: 1px solid #333; padding: 8px; font-weight: bold; background-color: #f0f0f0; width: 25%;'>Adresse postale :</td><td style='border: 1px solid #333; padding: 8px;'>" + str(Adresse_Postale) + "</td></tr>"
template_base += "<tr><td style='border: 1px solid #333; padding: 8px; font-weight: bold; background-color: #f0f0f0;'>Téléphone mairie :</td><td style='border: 1px solid #333; padding: 8px;'>" + str(Telephone_mairie) + "</td></tr>"
template_base += "<tr><td style='border: 1px solid #333; padding: 8px; font-weight: bold; background-color: #f0f0f0;'>Adresse mail :</td><td style='border: 1px solid #333; padding: 8px;'>" + str(Adresse_mail) + "</td></tr>"
template_base += "<tr><td style='border: 1px solid #333; padding: 8px; font-weight: bold; background-color: #f0f0f0;'>Site web :</td><td style='border: 1px solid #333; padding: 8px;'>" + str(Site_web) + "</td></tr>"
template_base += "<tr><td style='border: 1px solid #333; padding: 8px; font-weight: bold; background-color: #f0f0f0;'>Maire :</td><td style='border: 1px solid #333; padding: 8px;'>" + str(Maire) + "</td></tr>"
template_base += "<tr><td style='border: 1px solid #333; padding: 8px; font-weight: bold; background-color: #f0f0f0;'>DGS :</td><td style='border: 1px solid #333; padding: 8px;'>" + str(DGS) + "</td></tr>"
template_base += "</table>"

# Tableau des élus
template_base += "<table style='border-collapse: collapse; width: 100%; margin: 20px 0; font-size: 14px;'>"
template_base += "<thead>"
template_base += "<tr style='background-color: #f8f9fa;'>"
template_base += "<th style='border: 1px solid #ddd; padding: 12px; text-align: left; font-weight: bold; font-size: 14px;'>Fonction</th>"
template_base += "<th style='border: 1px solid #ddd; padding: 12px; text-align: left; font-weight: bold; font-size: 14px;'>Prénom Nom</th>"
template_base += "<th style='border: 1px solid #ddd; padding: 12px; text-align: left; font-weight: bold; font-size: 14px;'>Téléphone</th>"
template_base += "<th style='border: 1px solid #ddd; padding: 12px; text-align: left; font-weight: bold; font-size: 14px;'>Mail</th>"
template_base += "</tr>"
template_base += "</thead>"
template_base += "<tbody>" + lignes_html + "</tbody>"
template_base += "</table>"
template_base += "</div>"

# Retourner le résultat
resultat_final = template_base
return resultat_final

2 « J'aime »

Pour envoyer la facture dans le corps d’un mail (et pas en PDF), et qu’elle apparaisse correctement au format A4 dans tous les clients mails, voici le code que vous pouvez utiliser :

<!DOCTYPE html>
<html>
<head>
<title>Facture affouage</title>
</head>
<body style="margin:0; padding:0; background-color:#0000;">
  <center>
    <table width="100%" border="0" cellspacing="0" cellpadding="0" style="background-color:#0000;">
      <tr>
        <td align="center">
          <table width="595" border="1px" cellspacing="0" cellpadding="0" style="background-color:#ffffff; font-family:Arial, sans-serif; font-size:12px; color:#000;">
            <tr>
              <td style="padding:20px;">
                <h1 style="text-align:center;">Facture affouage</h1>
                <table width="100%" style="margin-bottom:20px;">
                  <tr>
                    <td valign="top" style="width:50%;">
                      Mairie de La Mif<br>
                      395 rue de l'Église<br>
                      88460 La Mif Mouf<br>
                      Email : mairielamif@wanadoo.fr
                    </td>
                    <td valign="top" style="width:50%;">
                      <strong>A destination de :</strong><br>
                      {Nom_Prenom}<br>
                      {N_RUE} {RUE}<br>
                      {CP} {COMMUNE}
                    </td>
                  </tr>
                </table>
                <p><strong>Numéro de facture :</strong> {N_Facture}</p>
                <p><strong>Date d'émission :</strong> {Date_Emission}</p>
                <p><strong>Date de livraison:</strong> {Date_Livraison}</p>
                <table width="100%" border="1" cellspacing="0" cellpadding="5" style="border-collapse:collapse; font-size:11px; margin-top:10px; margin-bottom:10px;">
                  <thead style="background-color:#f0f0f0;">
                    <tr>
                      <th align="left">Description</th>
                      <th align="left">Quantité</th>
                      <th align="left">Prix Unitaire (€)</th>
                      <th align="left">Total HT (€)</th>
                    </tr>
                  </thead>
                  <tbody>
                    <tr>
                      <td>Stères bois / affouage</td>
                      <td>{Qte}</td>
                      <td>{PU_HT}</td>
                      <td>{Total_HT}</td>
                    </tr>
                  </tbody>
                  <tfoot>
                    <tr>
                      <td colspan="3" align="right"><strong>TVA 10%</strong></td>
                      <td>{TVA_10_}</td>
                    </tr>
                    <tr>
                      <td colspan="3" align="right"><strong>Total TTC</strong></td>
                      <td><strong>{Total_TTC}</strong></td>
                    </tr>
                  </tfoot>
                </table>
                <p style="margin-top:20px;"><u><b>Paiement à effectuer avant le {Date_limite}.</b></u></p>
                <ul style="padding-left:20px;">
                  <li><b>Par chèque</b> à l’ordre de la régie forêt "RR FORET LA MIF", <b>ou</b></li>
                  <li><b>Par virement</b> en rappelant votre nom, prénom et numéro de facture en référence de virement.<br>
                    (ex : Affouages 2024 N°FACT Nom Prénom). Le Relevé d'Identité Bancaire est ci-dessous :
                  </li>
                </ul>
                <table width="100%" border="1" cellspacing="0" cellpadding="5" style="border-collapse:collapse; font-size:11px; margin-top:10px;">
                  <thead style="background-color:#f0f0f0;">
                    <tr>
                      <th>Titulaire du compte</th>
                      <th>Code banque</th>
                      <th>Code guichet</th>
                      <th>N° de compte</th>
                    </tr>
                  </thead>
                  <tbody>
                    <tr>
                      <td>RR FORET LA MIF</td>
                      <td>10071</td>
                      <td>88000</td>
                      <td>00002002144</td>
                    </tr>
                  </tbody>
                </table>
                <table width="100%" border="1" cellspacing="0" cellpadding="5" style="border-collapse:collapse; font-size:11px; margin-top:10px; margin-bottom:20px;">
                  <thead style="background-color:#f0f0f0;">
                    <tr>
                      <th>Clé RIB</th>
                      <th>Domiciliation</th>
                      <th>IBAN</th>
                      <th>BIC</th>
                    </tr>
                  </thead>
                  <tbody>
                    <tr>
                      <td>36</td>
                      <td>IEUUZP</td>
                      <td>FR76 1989 4567 0000 0011 0114 226</td>
                      <td>TRPURUP1</td>
                    </tr>
                  </tbody>
                </table>
                <div style="text-align:center; font-size:12px; margin-top:30px;">
                  <u><b>La livraison ne pourra se faire qu’après règlement.</b></u><br>
                  Merci de votre compréhension.
                </div>
                <div style="text-align:right; font-size:12px; margin-top:30px;">
                  <b>Lisa LEPANARD</b><br>
                  Adjoint chargé de la forêt
                </div>
              </td>
            </tr>
          </table>
        </td>
      </tr>
    </table>
  </center>
</body>
</html>

Cela donne :

1 « J'aime »

Bonjour,

Je vous remercie pour ce travail qui est très pratique !
Je suis en train de le tester et tout fonctionne parfaitement.
J’ai néanmoins deux questions :

  • Savez vous comment faire pour que les chiffres en milliers aient un espace comme séparateur ?
  • Lorsque je l’imprime en pdf, toutes les couleurs disparaissent et je n’ai qu’un document en N&B (malgré les réglages d’impression en couleur). J’ai testé sur Firefox et Edge et le résultat est le même. Pouvez-vous m’indiquer comment enregistrer un pdf qui conserve les couleurs ?

Je vous remercie par avance.

Bonjour et bienvenue sur le forum :slight_smile:
Merci de vos retours, et avec plaisir. Pour les questions :

PDF final :

Est-ce que ça peut fonctionner pour vous ? (je n’ai pas d’imprimante donc je ne peux tester le choix de couleur lors de l’impression directe)

Bonjour,

Je vous remercie pour votre réponse, je souhaiterai justement que les chiffres en milliers s’affiche plutôt comme ça : « 8 888.89 »
Pour ce qui est de l’enregistrement en pdf voici ce qui s’affiche


Dac :slight_smile:

  • Pour ajouter un espace pour séparer les milliers, vous pouvez modifier la formule avec ceci, dans la condition if isinstance(attr, numbers.Number):
if isinstance(attr, numbers.Number):
      return str('{:,}'.format(round(attr, 2)).replace(",", " "))

explications :
'{:,}'.format(round(attr, 2) sépare les milliers avec des virgules (ex : 8,888)
str(...).replace(",", " ") transforme le nombre (ex: 8,888) en chaîne de caractères et remplace les virgules par des espaces (ex: 8 888)

  • Pour la couleur, étrange, rien dans « Paramètres supplémentaires » ? Ou si vous cliquez sur « Imprimer à l’aide de la boîte de dialogue système » ?

Je vous remercie. Pour les chiffres ça fonctionne parfaitement.
Concernant l’impression j’ai enfin trouvé la solution, il y a une option « Graphisme de l’arrière-plan » à cocher
Encore merci

1 « J'aime »

Bonjour. Merci pour ce tutoriel très utile. Lorsqu’on enregistre la vue en PDF, le nom par défaut du fichier PDF est « A cheap and cheerful Markdown viewer_editor.pdf » quelque soit le fichier. Existe-til un moyen de personnaliser ce nom de fichier PDF proposé par défaut avec des données issues de Grist ? (exemple « Facture de M. NOM PRENOM - VILLE.pdf »

1 « J'aime »

Bonjour et merci pour cette astuce indispensable !

J’avais besoin d’éditer ce type de pdf mais en un seul document.
J’ai donc ajouté une colonne « markdown concatener » avec une formule lookuprecords.

Dans cette colonne j’obtiens donc la concatenation de toutes les lignes html qui m’intéressent. Je l’affiche avec le widget Markdown.

Et plutôt que d’avoir 50 fichiers pdf je n’en ai qu’un seul.
Il ne me reste plus qu’à gérer les changements de pages.

Cela peut permettre aussi de régler le problème suivant : la vue liste de fiche est très jolie et lisible à l’écran, mais dés qu’on essaye de l’imprimer ou de l’exporter en pdf, c’est plus difficile.
Ici en gérant bien le html, on a un document a priori propre.

1 « J'aime »

Merci de votre post

mais je suis novice, et j’aimerai que vous m’expliquiez
ou si je pouvais trouver ce grist « metrople lyon » pour voir exactement comment il est programmé ça me serait utile

merci

Je relance ce sujet, qui mérite d’être + connu :).

Je ne vois plus « Paramètres avancés » en-dessous de Données source, c’est normal @audezu ?

Je rejoins la remarque de @Cedric ,
on a du texte indésirable en haut de page,
cela vient du widget Markdown lui-même, à cet endroid grist-widget/markdown/index.html at 8e1bcc5e054799e943f0044467df26c712939a79 · gristlabs/grist-widget · GitHub

Hello Quentin, bien vu ! Ces paramètres avancés donnaient la possibilité de basculer une table en mode « A la demande », ce qui était censé améliorer les performances du document, mais cette fonctionnalité a été dépréciée au mois de mai.