2010-05-06 16 views
1

J'essaie de rendre le contenu d'un Java/Swing Cobra HTML renderer à un hors-écran BufferedImage, pour une utilisation ailleurs dans mon application :Java/Swing hors écran de rendu (Cobra HTMLPanel -> BufferedImage) Problème: Le composant ne finit pas de redessiner d'abord

slideViewPanel.setDocument(document, rendererContext); 
BufferedImage test = new BufferedImage(300,300,BufferedImage.TYPE_INT_RGB); 
Graphics g = test.getGraphics(); 
slideViewPanel.paint(g); 

l'image obtenue en g montre une page partiellement rendu - parfois le contenu du HTMLFrame avant que le nouveau document a été établi; parfois une version à moitié rendue du nouveau document. Je suppose que c'est parce que la méthode setDocument de Cobra planifie simplement le document pour le re-rendu, mais je suis en train de faire un pas dans le débogueur et je ne vois pas de second thread à faire pour le re-rendu. Quelqu'un a-t-il un aperçu de ce qui pourrait se passer ici?

Répondre

1

Lorsque la page a été analysé, vous devez attendre jusqu'à ce que toutes les images sont chargées avant la pose sur le composant. Intercepter toutes les images chargées pour s'assurer que toutes les images sont complètement chargées avant de disposer le composant. Je l'ai fait en surchargeant HtmlDocumentImpl.loadImage. (Doit sous-classer DocumentBuilderImpl et surcharger createDocument pour le faire fonctionner.) J'ai utilisé un sémaphore pour attendre jusqu'à ce que le résultat d'un image.getWItdh soit disponible.

J'ai dû mettre en place une minuterie autour de l'analyse, car certains scripts peuvent boucler et ne jamais revenir. Je n'ai aucune idée s'il y a une meilleure façon de résoudre cela. En ce qui concerne la mise en page, il y a beaucoup de culte de la cargaison et d'expérimentation qui mènent à la coupure ci-dessous, alors peut-être que vous pouvez essayer d'enlever certaines choses.

  htmlPanel.setVisible(true); 
    htmlPanel.setPreferredWidth(DEFAULT_PAGE_WIDTH); 
    logger.info("Calculating preferred size"); 

    // Get the preferred heigth for the current width. 
    Dimension psvz = htmlPanel.getPreferredSize(); 
    Dimension min = htmlPanel.getMinimumSize(); 

    logger.info("prf :" + psvz); 
    logger.info("min :" + min); 

    // Enlarge to the minimum width (with a limit) 
    int width = Math.min(MAX_PAGE_WIDTH, Math.max(DEFAULT_PAGE_WIDTH, 
      psvz.width)); 
    int height = psvz.height; 

    logger.info("width :" + width); 
    logger.info("heigth :" + height); 

    htmlPanel.setSize(width, height); 

      // actually, htmlPanel is a subclass, and this method exposes validateTree. It may work without it. 
    htmlPanel.forceValidateTree(); 

    htmlPanel.doLayout(); 

    setImageSize(width); 
    logger.info("actual size:" + htmlPanel.getSize()); 

Je ne pouvais pas comprendre ce qu'est un JFrame fait avec le HtmlPanel afin qu'il peint ses enfants correctement. J'ai dû peindre en utilisant le composant pour enfants; C'est à dire le htmlPanel.getBlockRenderable() ou le frameset.

Les images sont peintes de manière asynchrone (et les peintures risquent d'être interrompues). Ainsi, avant que l'image tamponnée puisse être utilisée, toutes les peintures d'image doivent être complétées.

J'ai utilisé un objet graphics2d de délégation en surchargeant toutes les méthodes de peinture d'image pour attendre que la peinture soit terminée. Après cela, j'ai trouvé que le html pourrait bénéficier d'une re-mise en page car toutes les tailles d'images sont connues, donc j'invalide le composant et refait la mise en page et la peinture (ou en fait, j'appelle simplement le code deux fois

Ensuite, l'image tamponnée peut être utilisée. Peut-être qu'il y a un moyen plus simple.

0

Essayez d'appeler g.dispose()

+0

g.disposer() n'aide pas à rendre le rendu incomplet, mais merci de le signaler - l'API donne l'impression d'être quelque chose que je devrais utiliser avec des contextes graphiques. En regardant les documents Cobra, il semble que l'appel "setDocument" planifie la page pour le rendu, plutôt que de le rendre sur place. Il ne semble pas y avoir un notificateur de fonction/événement correspondant pour "rendu complet", bien que .. – Alterscape

+0

Ensuite, je suppose que vous devez obtenir le code source et creuser dedans ... – Guillaume

0

Vous devrez attendre jusqu'à ce que le composant est visible pour obtenir l'image. Essayez avec cette:

htmlPanel.setDocument(document, rendererContext); 
    EventQueue.invokeLater(new Runnable() { 
    public void run() { 
    if (!captureImage(htmlPanel, "test.jpg")){ 
    EventQueue.invokeLater(this); 
    System.out.println("Encolado"); 
    }else { 
    System.out.println("capturado"); 
    } 
    } 
    }); 

Ayant captureImage comme:

public static boolean captureImage(Component component, String fileName) { 
    boolean captured = false; 
    if (component.isVisible()) { 
    Dimension size = component.getSize(); 
    BufferedImage image = new BufferedImage(size.width, size.height, 
    BufferedImage.TYPE_INT_RGB); 

    component.paint(image.getGraphics()); 
    captured = true; 
    try { 
    ImageIO.write(image, "JPEG", new File(fileName)); 
    } catch (IOException e) { 
    e.printStackTrace(); 
    } 
    } 
    return captured; 
} 
0

En fait, entourez votre 'component.paint (image.getGraphics());' avec un 'SwingUtilities.invokeAndWait()' comme dans:

try { 
    SwingUtilities.invokeAndWait(new Runnable() { 
     public void run() { 
      component.paint(image.getGraphics()); 
     } 
    }); 
    ImageIO.write(image, "png", file); 
} catch (Exception e) { 
    } finally { 
    }