Files
MD-to-Prrint/md_to_print.sh
syoul 8ff8658ba7 Ajout option --convert-only pour conversion sans impression
- Ajout flag --convert-only / --no-print
- Permet conversion PDF sans impression automatique
- Utile pour intégration bureau et workflows batch
2025-12-25 16:38:41 +01:00

682 lines
18 KiB
Bash
Executable File

#!/bin/bash
# Script automatique : Markdown → PDF → Prévisualisation → Impression
# Usage: ./md_to_print.sh fichier.md [options]
set -euo pipefail
# Version
VERSION="1.0.0"
# Codes de retour
EXIT_SUCCESS=0
EXIT_GENERAL=1
EXIT_FILE_NOT_FOUND=2
EXIT_DEPS_MISSING=3
EXIT_PDF_CONVERSION=4
EXIT_PRINT_ERROR=5
EXIT_CONFIG_ERROR=6
EXIT_DOCKER_ERROR=7
EXIT_PERMISSIONS=8
EXIT_INVALID_ARG=9
EXIT_PRINTER_UNAVAILABLE=10
# Couleurs pour les messages
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Variables par défaut
PREVIEW=false
PRINTER=""
COPIES=1
PAGES=""
ORIENTATION="portrait"
SIZE="A4"
QUALITY="normal"
COLOR=false
DUPLEX="none"
INSTALL_DEPS=false
KEEP_PDF=false
CONVERT_ONLY=false
CONFIG_FILE=""
VERBOSE=false
LOG_FILE=""
LOG_LEVEL="INFO"
# Répertoires
OUTPUT_DIR="./output"
LOG_DIR="./logs"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Fonction de logging
log() {
local level="$1"
shift
local message="$*"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
local log_entry="[${timestamp}] [${level}] ${message}"
case "$level" in
DEBUG)
if [ "$VERBOSE" = true ] || [ "$LOG_LEVEL" = "DEBUG" ]; then
echo -e "${BLUE}${log_entry}${NC}" >&2
fi
;;
INFO)
echo -e "${GREEN}${log_entry}${NC}" >&2
;;
WARN)
echo -e "${YELLOW}${log_entry}${NC}" >&2
;;
ERROR)
echo -e "${RED}${log_entry}${NC}" >&2
;;
esac
if [ -n "$LOG_FILE" ] && [ -f "$LOG_FILE" ]; then
echo "${log_entry}" >> "$LOG_FILE"
fi
}
# Fonction d'aide
show_help() {
cat << EOF
MD_to_Print v${VERSION} - Conversion Markdown → PDF → Impression
Usage: $0 fichier.md [OPTIONS]
OPTIONS:
-p, --preview Prévisualiser le PDF avant impression
-P, --printer NOM Spécifier l'imprimante
-c, --copies N Nombre de copies (défaut: 1)
-r, --pages RANGE Pages à imprimer (ex: 1-3,5,7-9)
-o, --orientation DIR Orientation: portrait ou landscape (défaut: portrait)
-s, --size SIZE Taille: A4, A3, Letter, etc. (défaut: A4)
-q, --quality QUALITY Qualité: draft, normal, high (défaut: normal)
-C, --color Impression en couleur
-d, --duplex MODE Mode recto-verso: none, simplex, duplex (défaut: none)
-i, --install-deps Installer les dépendances automatiquement
-k, --keep-pdf Conserver le fichier PDF après impression
--convert-only, --no-print Conversion uniquement (sans impression)
--config FILE Fichier de configuration
-v, --verbose Mode verbeux (logging DEBUG)
-l, --log FILE Fichier de log
-h, --help Afficher cette aide
--version Afficher la version
EXEMPLES:
$0 document.md --preview
$0 document.md --printer HP_LaserJet --copies 2 --color
$0 document.md --pages 1-5 --orientation landscape
$0 document.md --duplex duplex --quality high --config myconfig.conf
CONFIGURATION:
Le fichier de configuration utilise le format INI avec les sections:
[default], [paths], [logging]
Voir config.example.conf pour un exemple.
CODES DE RETOUR:
0 : Succès
1 : Erreur générale
2 : Fichier introuvable
3 : Dépendances manquantes
4 : Erreur conversion PDF
5 : Erreur impression
6 : Erreur configuration
7 : Erreur Docker
8 : Permissions insuffisantes
9 : Argument invalide
10 : Imprimante non disponible
EOF
}
# Fonction de chargement de configuration
load_config() {
local config_path="$1"
if [ ! -f "$config_path" ]; then
log ERROR "Fichier de configuration introuvable: $config_path"
return $EXIT_CONFIG_ERROR
fi
log INFO "Chargement de la configuration depuis: $config_path"
while IFS='=' read -r key value || [ -n "$key" ]; do
# Ignorer les commentaires et lignes vides
[[ "$key" =~ ^[[:space:]]*# ]] && continue
[[ -z "${key// }" ]] && continue
# Supprimer les espaces
key=$(echo "$key" | tr -d '[:space:]')
value=$(echo "$value" | tr -d '[:space:]')
case "$key" in
printer)
[ -z "$PRINTER" ] && PRINTER="$value"
;;
copies)
[ "$COPIES" = 1 ] && COPIES="$value"
;;
orientation)
[ "$ORIENTATION" = "portrait" ] && ORIENTATION="$value"
;;
size)
[ "$SIZE" = "A4" ] && SIZE="$value"
;;
quality)
[ "$QUALITY" = "normal" ] && QUALITY="$value"
;;
color)
[ "$COLOR" = false ] && COLOR=$( [ "$value" = "true" ] && echo true || echo false )
;;
duplex)
[ "$DUPLEX" = "none" ] && DUPLEX="$value"
;;
preview)
[ "$PREVIEW" = false ] && PREVIEW=$( [ "$value" = "true" ] && echo true || echo false )
;;
keep_pdf)
[ "$KEEP_PDF" = false ] && KEEP_PDF=$( [ "$value" = "true" ] && echo true || echo false )
;;
output_dir)
OUTPUT_DIR="$value"
;;
log_dir)
LOG_DIR="$value"
;;
level)
LOG_LEVEL="$value"
;;
file)
[ -z "$LOG_FILE" ] && LOG_FILE="$LOG_DIR/$value"
;;
verbose)
[ "$VERBOSE" = false ] && VERBOSE=$( [ "$value" = "true" ] && echo true || echo false )
;;
esac
done < <(grep -v '^\[' "$config_path" | grep -v '^[[:space:]]*$')
log INFO "Configuration chargée avec succès"
return $EXIT_SUCCESS
}
# Fonction d'installation des dépendances
install_dependencies() {
log INFO "Vérification et installation des dépendances..."
if ! command -v apt-get &> /dev/null; then
log ERROR "apt-get non disponible. Installation manuelle requise."
return $EXIT_DEPS_MISSING
fi
log INFO "Mise à jour de la liste des paquets..."
if ! sudo apt-get update -qq; then
log ERROR "Échec de la mise à jour des paquets"
return $EXIT_DEPS_MISSING
fi
local deps_to_install=()
if ! command -v pandoc &> /dev/null; then
deps_to_install+=("pandoc")
fi
if ! command -v pdflatex &> /dev/null; then
deps_to_install+=("texlive-latex-base" "texlive-fonts-recommended" "texlive-latex-extra")
fi
if ! command -v lp &> /dev/null; then
deps_to_install+=("cups")
fi
if [ ${#deps_to_install[@]} -gt 0 ]; then
log INFO "Installation: ${deps_to_install[*]}"
if ! sudo apt-get install -y "${deps_to_install[@]}"; then
log ERROR "Échec de l'installation des dépendances"
return $EXIT_DEPS_MISSING
fi
log INFO "Dépendances installées avec succès"
else
log INFO "Toutes les dépendances sont déjà installées"
fi
return $EXIT_SUCCESS
}
# Fonction de vérification des dépendances
check_dependencies() {
local missing_deps=()
if ! command -v pandoc &> /dev/null; then
missing_deps+=("pandoc")
fi
if ! command -v pdflatex &> /dev/null; then
missing_deps+=("texlive-latex-base")
fi
if ! command -v lp &> /dev/null; then
missing_deps+=("cups")
fi
if [ ${#missing_deps[@]} -gt 0 ]; then
log ERROR "Dépendances manquantes: ${missing_deps[*]}"
log INFO "Utilisez l'option --install-deps pour installer automatiquement"
return $EXIT_DEPS_MISSING
fi
return $EXIT_SUCCESS
}
# Fonction de prévisualisation
preview_pdf() {
local pdf_file="$1"
if [ ! -f "$pdf_file" ]; then
log ERROR "Fichier PDF introuvable pour prévisualisation: $pdf_file"
return $EXIT_FILE_NOT_FOUND
fi
log INFO "Ouverture de la prévisualisation..."
if command -v evince &> /dev/null; then
evince "$pdf_file" 2>/dev/null &
log DEBUG "Utilisation de evince pour la prévisualisation"
elif command -v okular &> /dev/null; then
okular "$pdf_file" 2>/dev/null &
log DEBUG "Utilisation de okular pour la prévisualisation"
elif command -v xdg-open &> /dev/null; then
xdg-open "$pdf_file" 2>/dev/null &
log DEBUG "Utilisation de xdg-open pour la prévisualisation"
else
log WARN "Aucun visualiseur PDF trouvé. Installation recommandée: evince ou okular"
return $EXIT_DEPS_MISSING
fi
log INFO "Appuyez sur Entrée pour continuer avec l'impression..."
read -r
return $EXIT_SUCCESS
}
# Fonction de liste des imprimantes
list_printers() {
log INFO "Imprimantes disponibles:"
if command -v lpstat &> /dev/null; then
lpstat -p 2>/dev/null | grep -E "^printer" | awk '{print " - " $2}' || log WARN "Aucune imprimante trouvée"
else
log WARN "lpstat non disponible, impossible de lister les imprimantes"
fi
}
# Fonction de validation de l'imprimante
check_printer() {
local printer_name="$1"
if [ -z "$printer_name" ]; then
return $EXIT_SUCCESS
fi
if ! command -v lpstat &> /dev/null; then
log WARN "lpstat non disponible, impossible de vérifier l'imprimante"
return $EXIT_SUCCESS
fi
if lpstat -p "$printer_name" &>/dev/null; then
log DEBUG "Imprimante '$printer_name' disponible"
return $EXIT_SUCCESS
else
log WARN "Imprimante '$printer_name' non disponible"
list_printers
return $EXIT_PRINTER_UNAVAILABLE
fi
}
# Parsing des arguments
MD_FILE=""
ARGS=("$@")
while [[ $# -gt 0 ]]; do
case $1 in
-p|--preview)
PREVIEW=true
shift
;;
-P|--printer)
if [ -z "${2:-}" ]; then
log ERROR "Option --printer nécessite un argument"
exit $EXIT_INVALID_ARG
fi
PRINTER="$2"
shift 2
;;
-c|--copies)
if [ -z "${2:-}" ]; then
log ERROR "Option --copies nécessite un argument"
exit $EXIT_INVALID_ARG
fi
COPIES="$2"
shift 2
;;
-r|--pages)
if [ -z "${2:-}" ]; then
log ERROR "Option --pages nécessite un argument"
exit $EXIT_INVALID_ARG
fi
PAGES="$2"
shift 2
;;
-o|--orientation)
if [ -z "${2:-}" ]; then
log ERROR "Option --orientation nécessite un argument"
exit $EXIT_INVALID_ARG
fi
ORIENTATION="$2"
shift 2
;;
-s|--size)
if [ -z "${2:-}" ]; then
log ERROR "Option --size nécessite un argument"
exit $EXIT_INVALID_ARG
fi
SIZE="$2"
shift 2
;;
-q|--quality)
if [ -z "${2:-}" ]; then
log ERROR "Option --quality nécessite un argument"
exit $EXIT_INVALID_ARG
fi
QUALITY="$2"
shift 2
;;
-C|--color)
COLOR=true
shift
;;
-d|--duplex)
if [ -z "${2:-}" ]; then
log ERROR "Option --duplex nécessite un argument"
exit $EXIT_INVALID_ARG
fi
DUPLEX="$2"
shift 2
;;
-i|--install-deps)
INSTALL_DEPS=true
shift
;;
-k|--keep-pdf)
KEEP_PDF=true
shift
;;
--convert-only|--no-print)
CONVERT_ONLY=true
KEEP_PDF=true
shift
;;
--config)
if [ -z "${2:-}" ]; then
log ERROR "Option --config nécessite un argument"
exit $EXIT_INVALID_ARG
fi
CONFIG_FILE="$2"
shift 2
;;
-v|--verbose)
VERBOSE=true
LOG_LEVEL="DEBUG"
shift
;;
-l|--log)
if [ -z "${2:-}" ]; then
log ERROR "Option --log nécessite un argument"
exit $EXIT_INVALID_ARG
fi
LOG_FILE="$2"
shift 2
;;
-h|--help)
show_help
exit $EXIT_SUCCESS
;;
--version)
echo "MD_to_Print v${VERSION}"
exit $EXIT_SUCCESS
;;
-*)
log ERROR "Option inconnue: $1"
show_help
exit $EXIT_INVALID_ARG
;;
*)
if [ -z "$MD_FILE" ]; then
MD_FILE="$1"
else
log ERROR "Trop de fichiers spécifiés: $MD_FILE et $1"
exit $EXIT_INVALID_ARG
fi
shift
;;
esac
done
# Vérification du fichier markdown
if [ -z "$MD_FILE" ]; then
log ERROR "Aucun fichier markdown spécifié"
show_help
exit $EXIT_INVALID_ARG
fi
# Résolution du chemin absolu
if [[ "$MD_FILE" != /* ]]; then
MD_FILE="$(realpath "$MD_FILE" 2>/dev/null || echo "$MD_FILE")"
fi
if [ ! -f "$MD_FILE" ]; then
log ERROR "Le fichier '$MD_FILE' n'existe pas"
exit $EXIT_FILE_NOT_FOUND
fi
# Vérification des permissions de lecture
if [ ! -r "$MD_FILE" ]; then
log ERROR "Permission de lecture refusée pour '$MD_FILE'"
exit $EXIT_PERMISSIONS
fi
# Création des répertoires nécessaires
mkdir -p "$OUTPUT_DIR" "$LOG_DIR"
# Initialisation du fichier de log si spécifié
if [ -n "$LOG_FILE" ]; then
mkdir -p "$(dirname "$LOG_FILE")"
touch "$LOG_FILE"
fi
# Chargement de la configuration si spécifiée
if [ -n "$CONFIG_FILE" ]; then
if ! load_config "$CONFIG_FILE"; then
exit $EXIT_CONFIG_ERROR
fi
elif [ -f "$SCRIPT_DIR/config.conf" ]; then
log DEBUG "Chargement de la configuration par défaut: $SCRIPT_DIR/config.conf"
load_config "$SCRIPT_DIR/config.conf" || true
fi
# Installation des dépendances si demandée
if [ "$INSTALL_DEPS" = true ]; then
if ! install_dependencies; then
exit $EXIT_DEPS_MISSING
fi
fi
# Vérification des dépendances
if ! check_dependencies; then
exit $EXIT_DEPS_MISSING
fi
# Génération du nom du fichier PDF
MD_BASENAME=$(basename "$MD_FILE" .md)
PDF_FILE="$OUTPUT_DIR/${MD_BASENAME}.pdf"
log INFO "=== Conversion Markdown → PDF ==="
log INFO "Fichier source: $MD_FILE"
log INFO "Fichier PDF: $PDF_FILE"
# Conversion Markdown → PDF avec options
# Détection du moteur PDF disponible (xelatex gère mieux Unicode)
PDF_ENGINE="pdflatex"
if command -v xelatex &> /dev/null; then
PDF_ENGINE="xelatex"
log DEBUG "Utilisation de xelatex pour meilleure gestion Unicode"
fi
PANDOC_OPTS=(
"$MD_FILE"
-o "$PDF_FILE"
--pdf-engine="$PDF_ENGINE"
-V geometry:margin=2cm
-V fontsize=11pt
-V documentclass=article
-V papersize="$SIZE"
)
# Options spécifiques pour xelatex (meilleure gestion Unicode)
if [ "$PDF_ENGINE" = "xelatex" ]; then
PANDOC_OPTS+=(
-V mainfont="DejaVu Sans"
-V monofont="DejaVu Sans Mono"
)
fi
if [ "$ORIENTATION" = "landscape" ]; then
PANDOC_OPTS+=(-V geometry:landscape)
fi
log DEBUG "Exécution: pandoc ${PANDOC_OPTS[*]}"
if ! pandoc "${PANDOC_OPTS[@]}" 2>&1 | while IFS= read -r line; do log DEBUG "$line"; done; then
log ERROR "Erreur lors de la conversion en PDF"
exit $EXIT_PDF_CONVERSION
fi
if [ ! -f "$PDF_FILE" ]; then
log ERROR "Le fichier PDF n'a pas été créé"
exit $EXIT_PDF_CONVERSION
fi
log INFO "PDF créé avec succès: $PDF_FILE"
# Vérification de l'imprimante si spécifiée
if [ -n "$PRINTER" ]; then
if ! check_printer "$PRINTER"; then
log WARN "Tentative d'utilisation de l'imprimante par défaut"
PRINTER=""
fi
fi
# Prévisualisation si demandée
if [ "$PREVIEW" = true ]; then
if ! preview_pdf "$PDF_FILE"; then
log WARN "Échec de la prévisualisation, continuation avec l'impression"
fi
fi
# Préparation des options d'impression
LP_OPTS=()
if [ -n "$PRINTER" ]; then
LP_OPTS+=(-d "$PRINTER")
else
log INFO "Utilisation de l'imprimante par défaut"
list_printers
fi
LP_OPTS+=(-n "$COPIES")
if [ -n "$PAGES" ]; then
LP_OPTS+=(-o page-ranges="$PAGES")
fi
if [ "$ORIENTATION" = "landscape" ]; then
LP_OPTS+=(-o orientation-requested=4)
fi
LP_OPTS+=(-o media="$SIZE")
case "$QUALITY" in
draft)
LP_OPTS+=(-o print-quality=3)
;;
high)
LP_OPTS+=(-o print-quality=5)
;;
*)
LP_OPTS+=(-o print-quality=4)
;;
esac
if [ "$COLOR" = true ]; then
LP_OPTS+=(-o ColorMode=Color)
else
LP_OPTS+=(-o ColorMode=Monochrome)
fi
case "$DUPLEX" in
simplex)
LP_OPTS+=(-o sides=one-sided)
;;
duplex)
LP_OPTS+=(-o sides=two-sided-long-edge)
;;
*)
LP_OPTS+=(-o sides=one-sided)
;;
esac
# Impression (sauf si --convert-only)
if [ "$CONVERT_ONLY" = false ]; then
log INFO "=== Impression ==="
log INFO "Options:"
log INFO " - Copies: $COPIES"
log INFO " - Taille: $SIZE"
log INFO " - Orientation: $ORIENTATION"
log INFO " - Qualité: $QUALITY"
log INFO " - Couleur: $([ "$COLOR" = true ] && echo "Oui" || echo "Non")"
log INFO " - Recto-verso: $DUPLEX"
[ -n "$PAGES" ] && log INFO " - Pages: $PAGES"
[ -n "$PRINTER" ] && log INFO " - Imprimante: $PRINTER"
log DEBUG "Exécution: lp ${LP_OPTS[*]} $PDF_FILE"
if ! lp "${LP_OPTS[@]}" "$PDF_FILE" 2>&1 | while IFS= read -r line; do log DEBUG "$line"; done; then
log ERROR "Erreur lors de l'impression"
log INFO "Le fichier PDF a été conservé: $PDF_FILE"
exit $EXIT_PRINT_ERROR
fi
log INFO "Impression lancée avec succès!"
else
log INFO "Mode conversion uniquement (pas d'impression)"
fi
# Gestion du fichier PDF
if [ "$KEEP_PDF" = false ]; then
read -p "Supprimer le fichier PDF temporaire? (o/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Oo]$ ]]; then
rm "$PDF_FILE"
log INFO "Fichier PDF supprimé"
else
log INFO "Fichier PDF conservé: $PDF_FILE"
fi
else
log INFO "Fichier PDF conservé: $PDF_FILE"
fi
log INFO "=== Opération terminée avec succès ==="
exit $EXIT_SUCCESS