Résoudre les problèmes d'affichage des polices chinoises dans iText7 : guide pratique de l'entreprise

Résoudre les problèmes d'affichage des polices chinoises dans iText7 : guide pratique de l'entreprise

【Lien de téléchargement gratuit】Projet itext7-chinese-font: https://gitcode.com/gh_mirrors/it/itext7-chinese-font

I. Problème : Analyse des difficultés d'affichage des polices chinoises dans la génération PDF

Dans les scénarios de génération de documents PDF d'entreprise, les problèmes d'affichage des polices chinoises posent défis aux développeurs, se manifestant principalement par trois points critiques :

1.1 Caractères illisibles et carrés

Lorsqu'iText7 ne parvient pas à trouver une police chinoise appropriée, le PDF affiche des symbores comme □□□ ou ���. Ceci s'explique par le fait que la norme PDF prend en charge par défaut uniquement les jeux de caractères occidentaux, nécessitant l'intégration explicite des polices chinoises pour un rendu correct.

1.2 Incohérences d'affichage multiplateforme

Le même fichier PDF présente des rendus différents sur Windows, macOS et Linux, avec même des textes manquants dans certains cas. Un projet financier a montré qu'un PDF non intégré générait correctement côté serveur, mais perdait 30% des titres de tableaux sur les appareils Mac des clients.

1.3 Défis de performance et de taille

Les fichiers de polices chinoises complètes dépassent généralement 10 Mo, leur intégration directe provoquant une explosion de la taille du PDF. Le système de factures électroniques d'une plateforme e-commerce, n'ayant pas optimisé le traitement des polices, génère des fichiers PDF de 25 Mo, augmentant les coûts de stockage de 400% et réduisant la vitesse de téléchargement de 70%.

Conclusion clé : Le problème des polices chinoises est fondamentalement une协同 entre l'encodage des caractères et le mécanisme d'intégration des polices, nécessitant une approche systémique à trois niveaux : sélection de police, stratégie d'intégration et gestion des ressources.

II. Solutions : Analyse complète des solutions de polices chinoises pour iText7

2.1 Principes de rendu des polices PDF

2.1.1 Mécanisme d'intégration des polices

Intégration des polices → Incorporer le fichier police dans le PDF pour assurer un affichage correct sur tous les appareils. iText7 réalise cette intégration via PdfFontFactory, avec un processus principal incluant :

  1. Chargement du fichier police local (format TTF/OTF)
  2. Encodage des informations de contour de police en objets PDF
  3. Établissement de la correspondance entre l'encodage des caractères et les glyphes
  4. Enregistrement de la référence de police utilisée dans le flux de contenu
2.1.2 Mappage d'encodage des caractères

PDF utilise le système d'encodage CID (Character ID) pour traiter les caractères chinois, japonais et coréens. iText7 gère automatiquement le mappage Unicode vers CID via la classe PdfFont. Lors de l'utilisation de la méthode createFont(), il est nécessaire de spécifier les paramètres d'encodage corrects :

// Spécification explicite de l'encodage UTF-8 (par défaut)
PdfFont police = PdfFontFactory.createFont(CHEMIN_POLICE, PdfEncodings.UTF_8, true);


2.2 Comparaison des trois stratégies d'intégration des polices

Type de stratégie Code d'implémentation Avantages Inconvénients Scénarios d'utilisation
EMBEDDED EmbeddingStrategy.EMBEDDED Intégration complète, meilleure compatibilité Taille de fichier la plus grande (+15 Mo) Documents officiels comme contrats, certificats
EMBEDDED_SUBSET EmbeddingStrategy.EMBEDDED_SUBSET Intégration uniquement des caractères utilisés, taille minimale Intégration répétée lors de multiples utilisations de la même police Documents personnalisés générés dynamiquement
PREFER_EMBEDDED EmbeddingStrategy.PREFER_EMBEDDED Intégration prioritaire, polices système utilisées en cas d'absence Réduction de la cohérence multiplateforme Rapports internes temporaires

Recommandation d'entreprise : Pour les documents métier critiques, adopter la stratégie EMBEDDED ; pour les documents non critiques, utiliser EMBEDDED_SUBSET pour équilibrer taille et compatibilité.

2.3 Implémentation de modèles de conception pour la gestion des polices

2.3.1 Gestionnaire de polices en mode singleton
public class GestionnairePolices {
    // Instance singleton
    private static volatile GestionnairePolices instance;
    private final Map<String, PdfFont> cachePolice = new ConcurrentHashMap<>();
    
    // Constructeur privé pour empêcher l'instanciation
    private GestionnairePolices() {}
    
    // Implémentation singleton thread-safe avec double vérification
    public static GestionnairePolices getInstance() {
        if (instance == null) {
            synchronized (GestionnairePolices.class) {
                if (instance == null) {
                    instance = new GestionnairePolices();
                }
            }
        }
        return instance;
    }
    
    // Méthode de chargement de police avec cache
    public PdfFont getPolice(String cheminPolice) throws IOException {
        return cachePolice.computeIfAbsent(cheminPolice, path -> {
            try {
                // Utilisation de la stratégie d'intégration de sous-ensemble avec activation du cache
                return PdfFontFactory.createFont(
                    path, 
                    PdfEncodings.UTF_8, 
                    true, 
                    PdfFontFactory.EmbeddingStrategy.EMBEDDED_SUBSET
                );
            } catch (IOException e) {
                throw new ExceptionChargementPolice("Échec du chargement de la police : " + path, e);
            }
        });
    }
}


2.3.2 Fournisseur de police en mode factory
public interface FournisseurPolice {
    PdfFont getPoliceReguliere();
    PdfFont getPoliceGrasse();
    PdfFont getPoliceItalique();
}

public class FournisseurPoliceAlibaba implements FournisseurPolice {
    private static final String CHEMIN_REGULIER = "fonts/AlibabaPuHuiTi-2-45-Light.ttf";
    private static final String CHE gras = "fonts/AlibabaPuHuiTi-2-85-Bold.ttf";
    
    @Override
    public PdfFont getPoliceReguliere() throws IOException {
        return GestionnairePolices.getInstance().getPolice(CHEMIN_REGULIER);
    }
    
    @Override
    public PdfFont getPoliceGrasse() throws IOException {
        return GestionnairePolices.getInstance().getPolice(CHEMIN_GRAS);
    }
    
    @Override
    public PdfFont getPoliceItalique() throws IOException {
        // Alibaba PuHuiTi n'a pas de version italique, retour de la police régulière
        return getPoliceReguliere();
    }
}


III. Pratique : Guide de mise en œuvre en entreprise

3.1 Préparation de l'environnement et construction du projet

3.1.1 Exigences de l'environnement de développement
  • JDK 11+ (JDK 17 recommandé, avec une amélioration de performance de 20%)
  • Maven 3.6+ ou Gradle 7.0+
  • Bibliothèque cœur iText7 7.1.15+
3.1.2 Initialisation du projet
# Cloner le dépôt du projet
git clone https://gitcode.com/gh_mirrors/it/itext7-chinese-font
cd itext7-chinese-font

# Construire avec Maven
mvn clean package -DskipTests


3.1.3 Configuration des dépendances Maven
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext7-core</artifactId>
    <version>7.1.15</version>
    <type>pom</type>
</dependency>
<!-- Outils de traitement de police -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-io</artifactId>
    <version>1.3.2</version>
</dependency>


3.2 Implémentation principale : Générateur PDF d'entreprise

Voici l'implémentation complète intégrant la gestion des exceptions, le cache des polices et l'adaptation à plusieurs scénarios :

import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.property.TextAlignment;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class GenerateurPdfEntreprise {
    // Cache des polices - ConcurrentHashMap thread-safe
    private static final Map<String, PdfFont> CACHE_POLICE = new ConcurrentHashMap<>(4);
    // Chemin par défaut de la police
    private static final String POLICE_DEFAUT = "src/main/resources/fonts/AlibabaPuHuiTi-2-45-Light.ttf";
    
    /**
     * Génère un document PDF avec polices chinoises
     * @param contenu Contenu du document
     * @param cheminSortie Chemin de sortie
     * @param scene Scène d'application : IMPRESSION/MOBILE/ECRAN
     * @throws IOException En cas d'échec des opérations sur fichier
     */
    public void genererPdf(String contenu, String cheminSortie, String scene) throws IOException {
        // 1. Validation du répertoire de sortie
        File fichierSortie = new File(cheminSortie);
        if (!fichierSortie.getParentFile().exists() && !fichierSortie.getParentFile().mkdirs()) {
            throw new IOException("Impossible de créer le répertoire de sortie : " + fichierSortie.getParent());
        }
        
        // 2. Obtention de la police adaptée à la scène
        PdfFont police = getPolicePourScene(scene);
        
        // 3. Création du document PDF
        try (PdfWriter writer = new PdfWriter(cheminSortie);
             PdfDocument pdf = new PdfDocument(writer);
             Document document = new Document(pdf)) {
            
            // 4. Configuration des paramètres adaptés à la scène
            configurerParametresScene(document, scene);
            
            // 5. Ajout du contenu
            Paragraph paragraphe = new Paragraph(contenu)
                .setFont(police)
                .setTaillePolice(getTaillePolicePourScene(scene))
                .setAlignementTexte(TextAlignment.LEFT);
            document.add(paragraphe);
        }
    }
    
    /**
     * Obtention de la police appropriée selon la scène d'application
     */
    private PdfFont getPolicePourScene(String scene) throws IOException {
        // Sélection de la police selon la scène, priorité au cache
        String clePolice = scene + "_" + POLICE_DEFAUT;
        return CACHE_POLICE.computeIfAbsent(clePolice, k -> {
            try {
                // Vérification de l'existence du fichier police
                Path cheminPolice = Path.of(POLICE_DEFAUT);
                if (!Files.exists(cheminPolice)) {
                    throw new ExceptionPoliceNonTrouvee("Fichier police inexistant : " + cheminPolice);
                }
                
                // Sélection de la stratégie d'intégration selon la scène
                PdfFontFactory.EmbeddingStrategy strategie = getStrategieIntegration(scene);
                
                return PdfFontFactory.createFont(
                    cheminPolice.toAbsolutePath().toString(),
                    PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED,
                    true // Activation du cache de police
                );
            } catch (IOException e) {
                // Encapsulation en exception runtime pour éviter la signature de méthode
                throw new RuntimeException("Échec du chargement de la police", e);
            }
        });
    }
    
    /**
     * Sélection de la stratégie d'intégration selon la scène
     */
    private PdfFontFactory.EmbeddingStrategy getStrategieIntegration(String scene) {
        switch (scene.toUpperCase()) {
            case "IMPRESSION":
                return PdfFontFactory.EmbeddingStrategy.EMBEDDED; // Scène d'impression : intégration complète
            case "MOBILE":
                return PdfFontFactory.EmbeddingStrategy.EMBEDDED_SUBSET; // Scène mobile : intégration de sous-ensemble
            case "ECRAN":
                return PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED; // Scène écran : intégration prioritaire
            default:
                return PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED;
        }
    }
    
    /**
     * Configuration des paramètres du document selon la scène
     */
    private void configurerParametresScene(Document document, String scene) {
        switch (scene.toUpperCase()) {
            case "IMPRESSION":
                // Scène d'impression : papier A4, marges étroites
                document.setMarges(36, 36, 36, 36); // 1 pouce = 72 points, marges de 0.5 pouce
                break;
            case "MOBILE":
                // Scène mobile : disposition étroite adaptée à la lecture mobile
                document.setMarges(72, 18, 72, 18); // Réduction des marges latérales
                break;
            case "ECRAN":
                // Scène grand écran : optimisation pour affichage large
                document.setMarges(36, 72, 36, 72);
                break;
        }
    }
    
    /**
     * Obtention de la taille de police appropriée selon la scène
     */
    private float getTaillePolicePourScene(String scene) {
        switch (scene.toUpperCase()) {
            case "IMPRESSION":
                return 12; // Taille standard pour documents imprimés
            case "MOBILE":
                return 16; // Taille augmentée pour appareils mobiles
            case "ECRAN":
                return 14; // Taille équilibrée pour affichage écran
            default:
                return 12;
        }
    }
    
    /**
     * Exception de fichier police manquant
     */
    public static class ExceptionPoliceNonTrouvee extends RuntimeException {
        public ExceptionPoliceNonTrouvee(String message) {
            super(message);
        }
    }
}


3.3 Stratégies d'adaptation multi-scénarios

3.3.1 Optimisation pour la scène d'impression
  • Utilisation de la stratégie EMBEDDED pour garantir la netteté de l'impression
  • Configuration du mode couleur CMYK (standard d'impression)
  • Activation de la technologie Hinting des polices pour améliorer la netteté des bords
// Configuration spécifique pour l'impression
pdf.getCatalog().setLang(new PdfString("zh-CN"));
pdf.getCatalog().setPreferencesVisionneur(
    new PdfViewerPreferences().setEchelleImpression(PdfViewerPreferences.EchelleImpression.AUCUNE)
);


3.3.2 Adaptation pour mobile
  • Adoption de la stratégie SUBSET pour réduire le trafic de téléchargement
  • Taille de police d'au moins 14pt, interligne 1.5x
  • Marges uniques d'au moins 18pt pour éviter que le contenu soit coupé
3.3.3 Optimisation pour grand écran
  • Utilisation du format WOFF2 pour les polices (nécessite iText7 7.2.0+)
  • Activation du lissage des polices
  • Prise en charge du mode sombre

3.4 Pratiques d'optimisation des performances

3.4.1 Compression des fichiers police

Utilisation de l'outil FontTools pour optimiser les polices TTF :

# Installation des outils d'optimisation des polices
pip install fonttools brotli

# Compression de la police chinoise (conservation des 3500 caractères courants)
pyftsubset AlibabaPuHuiTi-2-45-Light.ttf \
  --text-file=caracteres_chinois_courants.txt \
  --layout-features='*' \
  --flavor=woff2


3.4.2 Mécanisme de cache des polices

Implémentation d'un cache au niveau JVM pour éviter les chargements répétés :

// Méthode de nettoyage du cache - à appeler lors de l'arrêt de l'application Web
public static void viderCachePolice() {
    CACHE_POLICE.clear();
    log.info("Cache des polices vidé, mémoire libérée : {}MB", 
             (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/1024/1024);
}


3.4.3 Chargement asynchrone des polices

Dans les scénarios à forte concurrence, utilisation de CompletableFuture pour un chargement asynchrone :

public CompletableFuture<PdfFont> chargerPoliceAsynchrone(String cheminPolice) {
    return CompletableFuture.supplyAsync(() -> {
        try {
            return PdfFontFactory.createFont(cheminPolice);
        } catch (IOException e) {
            throw new CompletionException(e);
        }
    }, serviceExecutionPolice);
}


3.5 Guide d'évitage des pièges et meilleures pratiques

3.5.1 Notes sur les droits d'auteur des polices
  • Alibaba PuHuiTi permet une utilisation commerciale mais nécessite le maintien de la déclaration de droits d'auteur
  • Source Han Sans/Serif basé sur SIL Open Font License, utilisation libre
  • Éviter l'utilisation des polices système Windows (comme SimSun) en raison des risques de droits d'auteur
3.5.2 Meilleures pratiques de gestion des exceptions
try {
    // Tentative de chargement de la police préférée
    return PdfFontFactory.createFont(POLICE_PREFEREE);
} catch (IOException e) {
    log.warn("Échec du chargement de la police préférée, utilisation de la police de secours : {}", e.getMessage());
    try {
        // Chargement de la police de secours
        return PdfFontFactory.createFont(POLICE_SECOURS);
    } catch (IOException ex) {
        log.error("Échec du chargement de toutes les polices, utilisation de la police par défaut", ex);
        // Retour de la police intégrée iText7 (supporte uniquement les caractères occidentaux)
        return PdfFontFactory.createFont();
    }
}


3.5.3 Flux de résolution des problèmes courants
  1. Problème de caractères illisibles : Vérifier le chemin de la police → Valider l'intégrité du fichier police → Confirmer la stratégie d'intégration
  2. Taille excessive : Utiliser la stratégie SUBSET → Compresser les polices → Activer la compression PDF
  3. Problèmes de performance : Vérifier le taux de réusite du cache → Optimiser le chargement des polices → Utiliser le traitement asynchrone

Recommandation d'entreprise : En déploeiment en production, il est recommandé de placer les fichiers police sur un CDN indépendant, en utilisant un chargement par flux pour garantir la sécurité et améliorer la vitesse de chargement.

IV. Cas d'étude : Solution de polices pour le système de rapports financiers

Un système de relevés électroniques d'une banque nationale a résolu les problèmes de polices chinoises grâce à l'architecture suivante :

  1. Service de police : Mise en place d'un serveur de police dédié, fournissant une API de chargement à la demande
  2. Cache multi-niveaux : Implémentation d'une architecture à trois niveaux (CDN → application → JVM)
  3. Déploiement progressif : Validation des nouveaux polices dans l'environnement de test avant déploiement complet
  4. Surveillance et alerte : Définition de seuils d'alerte pour le taux d'échec de chargement des polices (seuil < 0,1%)

Cette solution a permis d'améliorer la vitesse de génération PDF de 40%, de réduire la taille des fichiers de 65% et d'atteindre une cohérence multiplateforme de 99,98%.

À travers le système "problème-solution-pratique" exposé dans cet article, les développeurs peuvent résoudre systématiquement les problèmes d'affichage des polices chinoises dans iText7, construisant une solution PDF d'entreprise à la fois conforme aux spécifications techniques et adaptée aux besoins métier. La clé réside dans la compréhension des principes d'intégration des polices, le choix de stratégies d'intégration appropriées, combiné à l'optimisation contextuelle et au réglage des performances, pour finalement réaliser une génération de documents PDF de haute qualité et à haute efficacité.

【Lien de téléchagrement gratuit】Projet itext7-chinese-font: https://gitcode.com/gh_mirrors/it/itext7-chinese-font

Étiquettes: itext7 PDF polices-chinoises génération-pdf Java

Publié le 4 juillet à 23h46