From 41778d59185ef4173f514587df1612cc12eb4193 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Thu, 24 Mar 2022 09:59:44 +0100 Subject: [PATCH] PDF: affiche liste polices dispo, meilleurs messages d'erreur --- app/but/bulletin_but_pdf.py | 8 +-- app/scodoc/sco_bulletins_legacy.py | 18 +++--- app/scodoc/sco_bulletins_ucac.py | 12 ++-- app/scodoc/sco_pdf.py | 93 +++++++++++++++++------------- app/scodoc/sco_preferences.py | 9 ++- app/scodoc/sco_pvpdf.py | 15 ++--- app/scodoc/sco_trombino_tours.py | 44 +++++++------- 7 files changed, 104 insertions(+), 95 deletions(-) diff --git a/app/but/bulletin_but_pdf.py b/app/but/bulletin_but_pdf.py index 8a87f9005..29b9d6749 100644 --- a/app/but/bulletin_but_pdf.py +++ b/app/but/bulletin_but_pdf.py @@ -6,15 +6,13 @@ """Génération bulletin BUT au format PDF standard """ +from reportlab.lib.colors import blue +from reportlab.lib.units import cm, mm from reportlab.platypus import Paragraph, Spacer -from app.scodoc.sco_pdf import blue, cm, mm - +from app.scodoc.sco_bulletins_standard import BulletinGeneratorStandard from app.scodoc import gen_tables from app.scodoc.sco_codes_parcours import UE_SPORT -from app.scodoc.sco_utils import fmt_note - -from app.scodoc.sco_bulletins_standard import BulletinGeneratorStandard class BulletinGeneratorStandardBUT(BulletinGeneratorStandard): diff --git a/app/scodoc/sco_bulletins_legacy.py b/app/scodoc/sco_bulletins_legacy.py index 7d570d07d..314abb0db 100644 --- a/app/scodoc/sco_bulletins_legacy.py +++ b/app/scodoc/sco_bulletins_legacy.py @@ -34,17 +34,19 @@ CE FORMAT N'EVOLUERA PLUS ET EST CONSIDERE COMME OBSOLETE. """ +from reportlab.lib.colors import Color, blue +from reportlab.lib.units import cm, mm +from reportlab.platypus import Paragraph, Spacer, Table -import app.scodoc.sco_utils as scu -from app.scodoc.sco_permissions import Permission -from app.scodoc import sco_formsemestre -from app.scodoc import sco_pdf -from app.scodoc.sco_pdf import Color, Paragraph, Spacer, Table -from app.scodoc.sco_pdf import blue, cm, mm -from app.scodoc.sco_pdf import SU -from app.scodoc import sco_preferences from app.scodoc import sco_bulletins_generator from app.scodoc import sco_bulletins_pdf +from app.scodoc import sco_formsemestre +from app.scodoc.sco_permissions import Permission +from app.scodoc import sco_pdf +from app.scodoc.sco_pdf import SU +from app.scodoc import sco_preferences +import app.scodoc.sco_utils as scu + # Important: Le nom de la classe ne doit pas changer (bien le choisir), car il sera stocké en base de données (dans les préférences) class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator): diff --git a/app/scodoc/sco_bulletins_ucac.py b/app/scodoc/sco_bulletins_ucac.py index 2114c60ad..fc49ffd9c 100644 --- a/app/scodoc/sco_bulletins_ucac.py +++ b/app/scodoc/sco_bulletins_ucac.py @@ -32,16 +32,12 @@ On redéfini la table centrale du bulletin de note et hérite de tout le reste d E. Viennet, juillet 2011 """ +from reportlab.lib.colors import Color +from reportlab.lib.units import mm -import app.scodoc.sco_utils as scu -from app.scodoc.sco_pdf import blue, cm, mm -from app.scodoc.sco_pdf import Color, Paragraph, Spacer, Table - -from app.scodoc import sco_preferences - -from app.scodoc import sco_bulletins_generator from app.scodoc import sco_bulletins_standard -from app.scodoc import gen_tables +from app.scodoc import sco_preferences +import app.scodoc.sco_utils as scu class BulletinGeneratorUCAC(sco_bulletins_standard.BulletinGeneratorStandard): diff --git a/app/scodoc/sco_pdf.py b/app/scodoc/sco_pdf.py index 19a778144..2f468ccfc 100755 --- a/app/scodoc/sco_pdf.py +++ b/app/scodoc/sco_pdf.py @@ -44,22 +44,17 @@ import traceback import unicodedata import reportlab -from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Frame, PageBreak -from reportlab.platypus import Table, TableStyle, Image, KeepInFrame +from reportlab.pdfgen import canvas +from reportlab.platypus import Paragraph, Frame from reportlab.platypus.flowables import Flowable from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate -from reportlab.lib.styles import getSampleStyleSheet from reportlab.rl_config import defaultPageSize # pylint: disable=no-name-in-module from reportlab.lib.units import inch, cm, mm -from reportlab.lib.colors import pink, black, red, blue, green, magenta, red -from reportlab.lib.colors import Color -from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY from reportlab.lib import styles -from reportlab.lib.pagesizes import letter, A4, landscape + from flask import g -import app.scodoc.sco_utils as scu from app.scodoc.sco_utils import CONFIG from app import log from app.scodoc.sco_exceptions import ScoGenError, ScoValueError @@ -89,6 +84,12 @@ def SU(s): return s +def get_available_font_names() -> list[str]: + """List installed font names""" + can = canvas.Canvas(io.StringIO()) + return can.getAvailableFonts() + + def _splitPara(txt): "split a string, returns a list of ... " L = [] @@ -147,12 +148,26 @@ def makeParas(txt, style, suppress_empty=False): except Exception as e: log(traceback.format_exc()) log("Invalid pdf para format: %s" % txt) - result = [ - Paragraph( - SU('Erreur: format invalide'), - style, - ) - ] + try: + result = [ + Paragraph( + SU('Erreur: format invalide'), + style, + ) + ] + except ValueError as e: # probleme font ? essaye sans style + # recupere font en cause ? + m = re.match(r".*family/bold/italic for (.*)", e.args[0], re.DOTALL) + if m: + message = f"police non disponible: {m[1]}" + else: + message = "format invalide" + return [ + Paragraph( + SU(f'Erreur: {message}'), + reportlab.lib.styles.ParagraphStyle({}), + ) + ] return result @@ -166,7 +181,6 @@ def bold_paras(L, tag="b", close=None): if hasattr(L, "keys"): # L is a dict for k in L: - x = L[k] if k[0] != "_": L[k] = b + L[k] or "" + close return L @@ -256,7 +270,7 @@ class ScoDocPageTemplate(PageTemplate): if logo is not None: self.background_image_filename = logo.filepath - def beforeDrawPage(self, canvas, doc): + def beforeDrawPage(self, canv, doc): """Draws (optional) background, logo and contribution message on each page. day : Day of the month as a decimal number [01,31] @@ -271,10 +285,10 @@ class ScoDocPageTemplate(PageTemplate): """ if not self.preferences: return - canvas.saveState() + canv.saveState() # ---- Background image if self.background_image_filename and self.with_page_background: - canvas.drawImage( + canv.drawImage( self.background_image_filename, 0, 0, doc.pagesize[0], doc.pagesize[1] ) @@ -285,32 +299,32 @@ class ScoDocPageTemplate(PageTemplate): (width, height), image, ) = self.logo - canvas.drawImage(image, inch, doc.pagesize[1] - inch, width, height) + canv.drawImage(image, inch, doc.pagesize[1] - inch, width, height) # ---- Add some meta data and bookmarks if self.pdfmeta_author: - canvas.setAuthor(SU(self.pdfmeta_author)) + canv.setAuthor(SU(self.pdfmeta_author)) if self.pdfmeta_title: - canvas.setTitle(SU(self.pdfmeta_title)) + canv.setTitle(SU(self.pdfmeta_title)) if self.pdfmeta_subject: - canvas.setSubject(SU(self.pdfmeta_subject)) + canv.setSubject(SU(self.pdfmeta_subject)) bookmark = self.pagesbookmarks.get(doc.page, None) if bookmark: - canvas.bookmarkPage(bookmark) - canvas.addOutlineEntry(SU(bookmark), bookmark) + canv.bookmarkPage(bookmark) + canv.addOutlineEntry(SU(bookmark), bookmark) - def draw_footer(self, canvas, content): + def draw_footer(self, canv, content): """Print the footer""" - canvas.setFont( + canv.setFont( self.preferences["SCOLAR_FONT"], self.preferences["SCOLAR_FONT_SIZE_FOOT"] ) - canvas.drawString( + canv.drawString( self.preferences["pdf_footer_x"] * mm, self.preferences["pdf_footer_y"] * mm, content, ) - canvas.restoreState() + canv.restoreState() def footer_string(self) -> str: """String contenu du pied de page""" @@ -319,14 +333,14 @@ class ScoDocPageTemplate(PageTemplate): d["server_url"] = self.server_name return SU(self.footer_template % d) - def afterDrawPage(self, canvas, doc): + def afterDrawPage(self, canv, doc): if not self.preferences: return # ---- Footer foot_content = None if hasattr(doc, "current_footer"): foot_content = doc.current_footer - self.draw_footer(canvas, foot_content or self.footer_string()) + self.draw_footer(canv, foot_content or self.footer_string()) # ---- Filigranne (texte en diagonal en haut a gauche de chaque page) filigranne = None if hasattr(doc, "filigranne"): @@ -338,13 +352,13 @@ class ScoDocPageTemplate(PageTemplate): else: filigranne = self.filigranne.get(doc.page, None) if filigranne: - canvas.saveState() - canvas.translate(9 * cm, 27.6 * cm) - canvas.rotate(30) - canvas.scale(4.5, 4.5) - canvas.setFillColorRGB(1.0, 0.65, 0.65, alpha=0.6) - canvas.drawRightString(0, 0, SU(filigranne)) - canvas.restoreState() + canv.saveState() + canv.translate(9 * cm, 27.6 * cm) + canv.rotate(30) + canv.scale(4.5, 4.5) + canv.setFillColorRGB(1.0, 0.65, 0.65, alpha=0.6) + canv.drawRightString(0, 0, SU(filigranne)) + canv.restoreState() doc.filigranne = None def afterPage(self): @@ -443,8 +457,8 @@ class PDFLock(object): return # deja lock pour ce thread try: self.Q.put(1, True, self.timeout) - except queue.Full: - raise ScoGenError(msg="Traitement PDF occupé: ré-essayez") + except queue.Full as e: + raise ScoGenError(msg="Traitement PDF occupé: ré-essayez") from e self.current_thread = threading.get_ident() self.nref = 1 log("PDFLock: granted to %s" % self.current_thread) @@ -471,7 +485,6 @@ class WatchLock: def release(self): t = threading.current_thread() assert (self.native_id == t.native_id) and (self.ident == t.ident) - pass class FakeLock: diff --git a/app/scodoc/sco_preferences.py b/app/scodoc/sco_preferences.py index 5d019f7d2..7f1d8cc41 100644 --- a/app/scodoc/sco_preferences.py +++ b/app/scodoc/sco_preferences.py @@ -121,6 +121,7 @@ from app import log from app.scodoc.sco_exceptions import ScoValueError, ScoException from app.scodoc.TrivialFormulator import TrivialFormulator import app.scodoc.notesdb as ndb +from app.scodoc import sco_pdf import app.scodoc.sco_utils as scu @@ -194,6 +195,8 @@ def _get_pref_default_value_from_config(name, pref_spec): return value +_INSTALLED_FONTS = ", ".join(sco_pdf.get_available_font_names()) + PREF_CATEGORIES = ( # sur page "Paramètres" ("general", {}), @@ -777,7 +780,7 @@ class BasePreferences(object): { "initvalue": "Helvetica", "title": "Police de caractère principale", - "explanation": "pour les pdf (Helvetica est recommandée)", + "explanation": f"pour les pdf (Helvetica est recommandée, parmi {_INSTALLED_FONTS})", "size": 25, "category": "pdf", }, @@ -1140,7 +1143,7 @@ class BasePreferences(object): { "initvalue": "Times-Roman", "title": "Police de caractère pour les PV", - "explanation": "pour les pdf", + "explanation": f"pour les pdf ({_INSTALLED_FONTS})", "size": 25, "category": "pvpdf", }, @@ -1542,7 +1545,7 @@ class BasePreferences(object): { "initvalue": "Times-Roman", "title": "Police titres bulletins", - "explanation": "pour les pdf", + "explanation": f"pour les pdf ({_INSTALLED_FONTS})", "size": 25, "category": "bul", }, diff --git a/app/scodoc/sco_pvpdf.py b/app/scodoc/sco_pvpdf.py index 2e8cdaf66..3ac0096d6 100644 --- a/app/scodoc/sco_pvpdf.py +++ b/app/scodoc/sco_pvpdf.py @@ -28,15 +28,13 @@ """Edition des PV de jury """ import io -import os import re import reportlab from reportlab.lib.units import cm, mm -from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY -from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Frame, PageBreak -from reportlab.platypus import Table, TableStyle, Image, KeepInFrame -from reportlab.platypus.flowables import Flowable +from reportlab.lib.enums import TA_RIGHT, TA_JUSTIFY +from reportlab.platypus import Paragraph, Spacer, Frame, PageBreak +from reportlab.platypus import Table, TableStyle, Image from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate from reportlab.lib.pagesizes import A4, landscape from reportlab.lib import styles @@ -53,7 +51,6 @@ from app.scodoc import sco_preferences from app.scodoc import sco_etud import sco_version from app.scodoc.sco_logos import find_logo -from app.scodoc.sco_pdf import PDFLOCK from app.scodoc.sco_pdf import SU LOGO_FOOTER_ASPECT = scu.CONFIG.LOGO_FOOTER_ASPECT # XXX A AUTOMATISER @@ -317,14 +314,14 @@ class PVTemplate(CourrierIndividuelTemplate): self.with_footer = self.preferences["PV_WITH_FOOTER"] self.with_page_background = self.preferences["PV_WITH_BACKGROUND"] - def afterDrawPage(self, canvas, doc): + def afterDrawPage(self, canv, doc): """Called after all flowables have been drawn on a page""" pass - def beforeDrawPage(self, canvas, doc): + def beforeDrawPage(self, canv, doc): """Called before any flowables are drawn on a page""" # If the page number is even, force a page break - CourrierIndividuelTemplate.beforeDrawPage(self, canvas, doc) + CourrierIndividuelTemplate.beforeDrawPage(self, canv, doc) # Note: on cherche un moyen de generer un saut de page double # (redémarrer sur page impaire, nouvelle feuille en recto/verso). Pas trouvé en Platypus. # diff --git a/app/scodoc/sco_trombino_tours.py b/app/scodoc/sco_trombino_tours.py index 61d80de2e..56261eb9f 100644 --- a/app/scodoc/sco_trombino_tours.py +++ b/app/scodoc/sco_trombino_tours.py @@ -33,20 +33,23 @@ import io from reportlab.lib import colors -from reportlab.lib import pagesizes +from reportlab.lib.colors import black from reportlab.lib.pagesizes import A4, A3 +from reportlab.lib import styles +from reportlab.lib.pagesizes import landscape +from reportlab.lib.units import cm +from reportlab.platypus import KeepInFrame, Paragraph, Table, TableStyle +from reportlab.platypus.doctemplate import BaseDocTemplate -import app.scodoc.sco_utils as scu -from app import log from app.scodoc import sco_abs +from app.scodoc import sco_etud +from app.scodoc.sco_exceptions import ScoPDFFormatError from app.scodoc import sco_groups from app.scodoc import sco_groups_view from app.scodoc import sco_preferences from app.scodoc import sco_trombino -from app.scodoc import sco_etud -from app.scodoc.sco_exceptions import ScoPDFFormatError -from app.scodoc.sco_pdf import * - +import app.scodoc.sco_utils as scu +from app.scodoc.sco_pdf import SU, ScoDocPageTemplate # Paramétrage de l'aspect graphique: PHOTOWIDTH = 2.8 * cm @@ -55,7 +58,7 @@ N_PER_ROW = 5 def pdf_trombino_tours( - group_ids=[], # liste des groupes à afficher + group_ids=(), # liste des groupes à afficher formsemestre_id=None, # utilisé si pas de groupes selectionné ): """Generation du trombinoscope en fichier PDF""" @@ -66,7 +69,6 @@ def pdf_trombino_tours( DeptName = sco_preferences.get_preference("DeptName") DeptFullName = sco_preferences.get_preference("DeptFullName") - UnivName = sco_preferences.get_preference("UnivName") InstituteName = sco_preferences.get_preference("InstituteName") # Generate PDF page StyleSheet = styles.getSampleStyleSheet() @@ -143,9 +145,7 @@ def pdf_trombino_tours( for group_id in groups_infos.group_ids: if group_id != "None": - members, group, group_tit, sem, nbdem = sco_groups.get_group_infos( - group_id, "I" - ) + members, _, group_tit, sem, _ = sco_groups.get_group_infos(group_id, "I") groups += " %s" % group_tit L = [] currow = [] @@ -286,14 +286,14 @@ def pdf_trombino_tours( def pdf_feuille_releve_absences( - group_ids=[], # liste des groupes à afficher + group_ids=(), # liste des groupes à afficher formsemestre_id=None, # utilisé si pas de groupes selectionné ): """Generation de la feuille d'absence en fichier PDF, avec photos""" NB_CELL_AM = sco_preferences.get_preference("feuille_releve_abs_AM") NB_CELL_PM = sco_preferences.get_preference("feuille_releve_abs_PM") - COLWIDTH = 0.85 * cm + col_width = 0.85 * cm if sco_preferences.get_preference("feuille_releve_abs_samedi"): days = sco_abs.DAYNAMES[:6] # Lundi, ..., Samedi else: @@ -307,7 +307,6 @@ def pdf_feuille_releve_absences( DeptName = sco_preferences.get_preference("DeptName") DeptFullName = sco_preferences.get_preference("DeptFullName") - UnivName = sco_preferences.get_preference("UnivName") InstituteName = sco_preferences.get_preference("InstituteName") # Generate PDF page StyleSheet = styles.getSampleStyleSheet() @@ -340,7 +339,7 @@ def pdf_feuille_releve_absences( currow = [""] * (NB_CELL_AM + 1 + NB_CELL_PM + 1) elem_day = Table( [currow], - colWidths=([COLWIDTH] * (NB_CELL_AM + 1 + NB_CELL_PM + 1)), + colWidths=([col_width] * (NB_CELL_AM + 1 + NB_CELL_PM + 1)), style=TableStyle( [ ("GRID", (0, 0), (NB_CELL_AM - 1, 0), 0.25, black), @@ -362,7 +361,7 @@ def pdf_feuille_releve_absences( elem_week = Table( W, - colWidths=([COLWIDTH * (NB_CELL_AM + 1 + NB_CELL_PM + 1)] * nb_days), + colWidths=([col_width * (NB_CELL_AM + 1 + NB_CELL_PM + 1)] * nb_days), style=TableStyle( [ ("LEFTPADDING", (0, 0), (-1, -1), 0), @@ -378,7 +377,7 @@ def pdf_feuille_releve_absences( elem_day_name = Table( [currow], - colWidths=([COLWIDTH * (NB_CELL_AM + 1 + NB_CELL_PM + 1)] * nb_days), + colWidths=([col_width * (NB_CELL_AM + 1 + NB_CELL_PM + 1)] * nb_days), style=TableStyle( [ ("LEFTPADDING", (0, 0), (-1, -1), 0), @@ -390,9 +389,7 @@ def pdf_feuille_releve_absences( ) for group_id in groups_infos.group_ids: - members, group, group_tit, sem, nbdem = sco_groups.get_group_infos( - group_id, "I" - ) + members, _, group_tit, _, _ = sco_groups.get_group_infos(group_id, "I") L = [] currow = [ @@ -429,7 +426,10 @@ def pdf_feuille_releve_absences( T = Table( L, colWidths=( - [5.0 * cm, (COLWIDTH * (NB_CELL_AM + 1 + NB_CELL_PM + 1) * nb_days)] + [ + 5.0 * cm, + (col_width * (NB_CELL_AM + 1 + NB_CELL_PM + 1) * nb_days), + ] ), style=TableStyle( [