2017-02-03 3 views
1

J'essaie de créer un en-tête de page dynamique avec itext7 en utilisant EventHandlers. J'utilise le code ci-dessous pour cela:Problème d'en-tête de page dynamique iText 7

public static void main(String[] args) throws Exception { 
    File file = new File("C:\\Test\\variable_header.pdf"); 
    file.getParentFile().mkdirs(); 
    new VariableHeader().manipulatePdf(DEST); 
} 

protected void manipulatePdf(String dest) throws Exception { 
    PdfDocument pdfDoc = new PdfDocument(new PdfWriter(DEST)); 
    Document doc = new Document(pdfDoc); 
    VariableHeaderEventHandler handler = new VariableHeaderEventHandler(); 
    pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, handler); 
    handler.setHeader("First Header"); 
    for (int i = 1; i <= 50; i++) { 
     doc.add(new Paragraph("Added First content")); 
    } 
    doc.add(new AreaBreak()); 
    handler.setHeader("Second Header"); 
    for (int i = 1; i <= 15; i++) { 
     doc.add(new Paragraph("Added Second content")); 
    } 
    doc.close(); 
} 

protected class VariableHeaderEventHandler implements IEventHandler { 
    protected String header; 

    public void setHeader(String header) { 
     this.header = header; 
    } 

    @Override 
    public void handleEvent(Event event) { 
     PdfDocumentEvent documentEvent = (PdfDocumentEvent) event; 
     try { 
      new PdfCanvas(documentEvent.getPage()) 
        .beginText() 
        .setFontAndSize(PdfFontFactory.createFont(FontConstants.HELVETICA), 12) 
        .moveText(450, 806) 
        .showText(header) 
        .endText() 
        .stroke(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

} 

Ici, je crée « VariableHeaderEventHandler » qui rend l'en-tête de page dynamique pour chaque page du document.

Initialement, j'ai défini l'en-tête de la page comme "Premier en-tête" et ajouté 50 fois le texte "Premier contenu ajouté" au document. Puis j'ai ajouté le saut de secteur qui laisse l'autre partie de la page vide après avoir rendu le contenu précédent et tout autre contenu sera rendu dans une nouvelle page. Ensuite, j'ai changé l'en-tête de la page en tant que "Deuxième en-tête" et ajouté le texte "Deuxième contenu ajouté" au document pour 15 fois.

Ainsi, selon le code ci-dessus, l'en-tête de la page "Deuxième en-tête" doit être rendu UNIQUEMENT à partir de la page où le texte "Deuxième contenu ajouté" est affiché. Mais la « deuxième tête » est rendu dans la page précédente où le texte « Ajout de contenu d'abord » est affiché comme indiqué ci-dessous:

iText7 question d'en-tête dynamique:

Screenshot

Il semble qu'il y ait un certain problème avec le mécanisme de gestion des événements dans iText 7.0.1.

Existe-t-il un moyen de résoudre ce problème? Toute aide serait appréciée.

Répondre

1

Vous utilisez des événements de niveau inférieur PdfDocument ainsi qu'une mise en forme de haut niveau via Document. END_PAGE événement est déclenché lorsqu'une page est vidée dans le flux de sortie. Bien que PdfDocument de bas niveau et Document de haut niveau soient liés, il n'existe aucun contrat qui vide Document la page immédiatement après qu'il va à un nouveau. En fait, Document vider les pages avec un certain retard par défaut qui est requis par des capacités de mise en page plus fines. Le fait que les pages soient vidées avec un certain retard entraîne l'apparition d'un en-tête nouvellement défini sur les pages "plus anciennes" avec le premier type de contenu. Donc, c'est un comportement attendu, au moins pour l'instant.

Il existe plusieurs façons de contourner ce comportement et d'obtenir ce que vous voulez. Je vais parler de l'un d'entre eux.

Après avoir ajouté le AreaBreak au Document, vous pouvez demander au DocumentRenderer pour la zone actuelle disponible pour rendre le reste du contenu et cette zone contiendra le numéro de page:

doc.add(new AreaBreak()); 
int secondContentStartPageNumber = doc.getRenderer().getCurrentArea().getPageNumber(); 

Vous pouvez puis passer cette information à votre gestionnaire d'événements:

handler.setSecondAreaStartPage(secondContentStartPageNumber); 

et utiliser cette information pour décider quel en-tête à la sortie d'une page:

int pageNumber = documentEvent.getDocument().getPageNumber(documentEvent.getPage()); 
String textToShow = pageNumber < secondAreaStartPage ? firstHeader : secondHeader; 

La classe VariableHeaderEventHandler complète ressemblerait à ceci:

protected class VariableHeaderEventHandler implements IEventHandler { 
    private String firstHeader = "First Header"; 
    private String secondHeader = "Second Header"; 
    private int secondAreaStartPage = Integer.MAX_VALUE; 

    public void setSecondAreaStartPage(int newValue) { 
     secondAreaStartPage = newValue; 
    } 

    @Override 
    public void handleEvent(Event event) { 
     PdfDocumentEvent documentEvent = (PdfDocumentEvent) event; 
     int pageNumber = documentEvent.getDocument().getPageNumber(documentEvent.getPage()); 
     try { 
      new PdfCanvas(documentEvent.getPage()) 
        .beginText() 
        .setFontAndSize(PdfFontFactory.createFont(FontConstants.HELVETICA), 12) 
        .moveText(450, 806) 
        .showText(pageNumber < secondAreaStartPage ? firstHeader : secondHeader) 
        .endText() 
        .stroke(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

Bien sûr, pour le cas de plus de deux zones le code deviendra plus compliqué, mais cela est plus d'un exercice de programmation qu'une question à propos d'iText.

+0

Cela semble plus compliqué que l'implémentation de itext 5 de la même tâche. En particulier si vous voulez le chapitre et la section en cours dans l'en-tête ou le pied de page ou quelque chose de similaire dynamique ... – mkl