Rendre le HTML en image

167

Existe-t-il un moyen de rendre du HTML en image comme PNG? Je sais que c'est possible avec canvas mais j'aimerais rendre un élément html standard comme div par exemple.

Martin Delille
la source
Pour créer une sorte d'assistance utilisateur, par exemple. Malheureusement, ce n'est pas possible (pour des raisons de sécurité?). Vous devez demander à l'utilisateur d'appuyer sur PrintScreen pour faire quelque chose.
MaxArt
1
duplication possible de Comment convertir le HTML d'un site web en image?
Ernest Friedman-Hill
Je souhaite utiliser HTML / CSS pour concevoir un logo.
Martin Delille
4
@ ErnestFriedman-Hill pas un doublon: la question que vous avez mentionnée est spécifique à java.
Martin Delille
Voici un tutoriel étape par étape pour convertir du HTML en format image
Satinder singh

Réponses:

42

Je sais que c'est une question assez ancienne qui a déjà beaucoup de réponses, mais j'ai encore passé des heures à essayer de faire ce que je voulais:

  • étant donné un fichier html, générer une image (png) avec un fond transparent à partir de la ligne de commande

En utilisant Chrome sans tête (version 74.0.3729.157 à partir de cette réponse), c'est en fait facile:

"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --headless --screenshot --window-size=256,256 --default-background-color=0 button.html

Explication de la commande:

  • vous exécutez Chrome à partir de la ligne de commande (ici montré pour le Mac, mais en supposant similaire sur Windows ou Linux)
  • --headless exécute Chrome sans l'ouvrir et se ferme une fois la commande terminée
  • --screenshotcapturera une capture d'écran (notez qu'il génère un fichier appelé screenshot.pngdans le dossier où la commande est exécutée)
  • --window-sizepermet de ne capturer qu'une partie de l'écran (le format est --window-size=width,height)
  • --default-background-color=0est le tour de magie qui dit à Chrome d'utiliser un transparent arrière-plan , pas la couleur blanche par défaut
  • enfin vous fournissez le fichier html (sous forme d'url locale ou distante ...)
yan
la source
1
Très agréable! Cela fonctionne aussi avec SVG! Je suis finalement passé à votre solution! ;-)
Martin Delille
1
--screenshot='absolute\path\of\screenshot.png'travaillé pour moi
palash le
la ligne de virgule a fonctionné. si je veux exécuter ce programme à partir d'express js comment l'exécuter?
Recettes Squapl le
Pour info, cela fonctionne également avec le chrome même s'il ne mentionne pas l'option dans la page de manuel.
Robin Lashof-Regas le
Le svg n'est pas un svg pour moi ... c'est juste un PNG avec une extension SVG ... alors attention.
kristopolous
97

Oui. HTML2Canvas existe pour rendre le HTML <canvas>(que vous pouvez ensuite utiliser comme image).

REMARQUE: il existe un problème connu, à savoir que cela ne fonctionnera pas avec SVG

AKX
la source
Cela semble intéressant mais je n'ai pas réussi à le faire fonctionner alors j'ai choisi la solution John Fisher. Merci pour l'info, je vais regarder ce projet dans le futur!
Martin Delille
@MartinDelille: voici un article sur l'utilisation de HTML2Canvas pour créer une IMAGE et vous pouvez également le télécharger côté client codepedia.info/2016/02
Satinder singh
3
dom-to-image (voir la réponse de tsayen) fait un bien meilleur travail pour rendre une image précise.
JBE
3
Cette solution est très lente
hamboy75
3
autre problème, si l'élément est caché ou derrière un autre élément cela ne fonctionnera pas
Yousef
91

Puis-je recommander la bibliothèque dom-to-image , qui a été écrite uniquement pour résoudre ce problème (je suis le mainteneur).
Voici comment vous l'utilisez (quelques autres ici ):

var node = document.getElementById('my-node');

domtoimage.toPng(node)
    .then (function (dataUrl) {
        var img = new Image();
        img.src = dataUrl;
        document.appendChild(img);
    })
    .catch(function (error) {
        console.error('oops, something went wrong!', error);
    });
Tsayen
la source
2
C'est bien! Un moyen de doubler la taille de sortie?
cronoklee le
Merci! Mais qu'entendez-vous exactement par «doubler la taille»?
tsayen le
2
La première chose qui me vient à l'esprit - essayez de rendre votre image en SVG (en utilisant domtoimage.toSvg), puis rendez-la vous-même sur la toile et essayez de jouer avec la résolution de cette toile d'une manière ou d'une autre. Il est probablement possible d'implémenter une telle fonctionnalité comme une sorte d'option de rendu dans la bibliothèque elle-même, afin que vous puissiez passer les dimensions de l'image en pixels. Si vous en avez besoin, je vous serais reconnaissant de créer un problème sur github.
tsayen
8
C'est BEAUCOUP mieux que html2canvas. Merci.
c.hughes
1
@Subho c'est une chaîne contenant l'URL avec des données encodées en base64
tsayen
67

Il y a beaucoup d'options et elles ont toutes leurs avantages et leurs inconvénients.

Option 1: utilisez l'une des nombreuses bibliothèques disponibles

Avantages

  • La conversion est assez rapide la plupart du temps

Les inconvénients

  • Mauvais rendu
  • N'exécute pas javascript
  • Pas de prise en charge des fonctionnalités Web récentes (FlexBox, Advanced Selectors, Webfonts, Box Sizing, Media Queries, ...)
  • Parfois pas si facile à installer
  • Compliqué à l'échelle

Option 2: utilisez PhantomJs et peut-être une bibliothèque d'encapsuleurs

Avantages

  • Exécuter Javascript
  • Tres rapide

Les inconvénients

  • Mauvais rendu
  • Pas de prise en charge des fonctionnalités Web récentes (FlexBox, Advanced Selectors, Webfonts, Box Sizing, Media Queries, ...)
  • Compliqué à l'échelle
  • Pas si facile de le faire fonctionner s'il y a des images à charger ...

Option 3: utilisez Chrome Headless et peut-être une bibliothèque d'encapsuleurs

Avantages

  • Exécuter Javascript
  • Rendu presque parfait

Les inconvénients

  • Pas si facile d'avoir exactement le résultat souhaité concernant:
    • synchronisation de chargement de la page
    • dimensions de la fenêtre
  • Compliqué à l'échelle
  • Assez lent et encore plus lent si le html contient des liens externes

Option 4: utiliser une API

Avantages

  • Exécuter Javascript
  • Rendu presque parfait
  • Rapide lorsque les options de mise en cache sont correctement utilisées
  • L'échelle est gérée par les API
  • Synchronisation précise, fenêtre d'affichage, ...
  • La plupart du temps, ils proposent un plan gratuit

Les inconvénients

  • Pas gratuit si vous prévoyez de les utiliser beaucoup

Divulgation: je suis le fondateur d'ApiFlash. J'ai fait de mon mieux pour fournir une réponse honnête et utile.

Timothée Jeannin
la source
2
Merci pour une réponse détaillée avec de nombreuses solutions possibles
bszom
wkhtmltoimage / pdf prend en charge le rendu javascript. Vous pouvez définir un délai javascript ou laisser wkhtml vérifier un window.status spécifique (que vous pouvez définir avec javascript lorsque vous savez que vos tâches js sont terminées)
Daniel Z.
PhantomJS prend en charge les éléments dynamiques comme Google Maps. C'est vraiment un navigateur Web complet, juste sans écran. Cependant, vous devez attendre un peu que Google Map charge les tuiles avec la géographie (j'attends 500 ms et autorise les gens à modifier le délai - si ce n'est pas suffisant, recommencer sans augmenter le temps, c'est bien, car ces tuiles sont mises en cache).
Ladislav Zima le
50

Toutes les réponses ici utilisent des bibliothèques tierces tandis que le rendu HTML sur une image peut être relativement simple en Javascript pur. Il y a même eu un article à ce sujet dans la section canevas de MDN.

L'astuce est la suivante:

  • créer un SVG avec un nœud ForeignObject contenant votre XHTML
  • définir le src d'une image sur l'url de données de ce SVG
  • drawImage sur la toile
  • définir les données du canevas sur l'image cible.src

const {body} = document

const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.width = canvas.height = 100

const tempImg = document.createElement('img')
tempImg.addEventListener('load', onTempImageLoad)
tempImg.src = 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml"><style>em{color:red;}</style><em>I</em> lick <span>cheese</span></div></foreignObject></svg>')

const targetImg = document.createElement('img')
body.appendChild(targetImg)

function onTempImageLoad(e){
  ctx.drawImage(e.target, 0, 0)
  targetImg.src = canvas.toDataURL()
}

Quelques points à noter

  • Le HTML à l'intérieur du SVG doit être XHTML
  • Pour des raisons de sécurité, le SVG en tant qu'URL de données d'une image agit comme une portée CSS isolée pour le HTML car aucune source externe ne peut être chargée. Ainsi, une police Google, par exemple, doit être intégrée à l'aide d'un outil comme celui-ci .
  • Même lorsque le HTML à l'intérieur du SVG dépasse la taille de l'image, il sera dessiné correctement sur le canevas. Mais la hauteur réelle ne peut pas être mesurée à partir de cette image. Une solution à hauteur fixe fonctionnera très bien, mais la hauteur dynamique nécessitera un peu plus de travail. Le mieux est de rendre les données SVG dans une iframe (pour une portée CSS isolée) et d'utiliser la taille résultante pour le canevas.
Sjeiti
la source
Agréable! La suppression de la dépendance à une bibliothèque externe est en effet la bonne solution. J'ai changé ma réponse acceptée :-)
Martin Delille
Cet article n'est plus là.
MSC
2
Excellente réponse, mais commentant l'état de l'art des API HTML et Web, qui, comme d'habitude, ressemble à quelque chose qui a été retiré par quelqu'un - toutes les fonctionnalités sont techniquement là mais exposées derrière un tableau (sans jeu de mots) de chemins de code étranges qui ressemblent rien du genre de clarté que vous attendez d'une API bien conçue. En langage clair: il est très probablement trivial pour un navigateur de permettre à un Documentd'être rendu dans un raster (par exemple, a Canvas), mais parce que personne n'a pris la peine de le standardiser, nous devons recourir à la folie comme illustré ci-dessus (pas d'offense à l'auteur de ce code).
amn
1
Cette réponse stackoverflow.com/questions/12637395/... semble indiquer que vous pouvez aller assez loin. Mozilla et Chrome ont une longueur d'URI de données illimitée et même IE actuel autorise 4 Go, ce qui se résume à environ 3 Go d'images (en raison de la base64). Mais ce serait une bonne chose à tester.
Sjeiti
3
- quelques tests rapides et sales plus tard - jsfiddle.net/Sjeiti/pcwudrmc/75965 Chrome, Edge et Firefox vont jusqu'à une hauteur de largeur d'au moins 10000 pixels, ce qui (dans ce cas) est un nombre de caractères d'environ 10000000 et une taille de fichier png de 8 Mo. Je n'ai pas testé rigoureusement mais c'est suffisant pour la plupart des cas d'utilisation.
Sjeiti
13

Vous pouvez utiliser PhantomJS , qui est un webkit sans tête (le moteur de rendu dans safari et (jusqu'à récemment) le pilote chrome). Vous pouvez apprendre comment effectuer une capture d'écran de pages ici . J'espère que cela pourra aider!

TheContrarian
la source
Cette technique fonctionne bien. Cependant, 2 ans se sont écoulés depuis votre commentaire. Avez-vous rencontré quelque chose qui fonctionne plus rapidement que PhantomJS? D'après mon expérience, la génération d'images prend généralement 2 à 5 secondes dans Phantom.
Brian Webster
Chrome sans tête est désormais possible. Je ne sais pas si c'est plus rapide, mais si vous voulez des captures d'écran précises, c'est une bonne solution.
Josh Maag
11

Vous pouvez utiliser un outil HTML vers PDF tel que wkhtmltopdf. Et puis vous pouvez utiliser un outil PDF en image comme imagemagick. Certes, c'est côté serveur et un processus très compliqué ...

PinnyM
la source
12
Ou vous pouvez simplement exécuter le frère d'image de wkhtmltopdf, wkhtmltoimage.
Roman Starkov
1
Cette bibliothèque est pour Node.js. Que diriez-vous d'une interface simple?
Vlad
9

La seule bibliothèque que j'ai pu utiliser pour Chrome, Firefox et MS Edge était rasterizeHTML . Il produit une meilleure qualité que HTML2Canvas et est toujours pris en charge contrairement à HTML2Canvas.

Obtention d'un élément et téléchargement au format PNG

var node= document.getElementById("elementId");
var canvas = document.createElement("canvas");
canvas.height = node.offsetHeight;
canvas.width = node.offsetWidth;
var name = "test.png"

rasterizeHTML.drawHTML(node.outerHTML, canvas)
     .then(function (renderResult) {
            if (navigator.msSaveBlob) {
                window.navigator.msSaveBlob(canvas.msToBlob(), name);
            } else {
                const a = document.createElement("a");
                document.body.appendChild(a);
                a.style = "display: none";
                a.href = canvas.toDataURL();
                a.download = name;
                a.click();
                document.body.removeChild(a);
            }
     });
vabanagas
la source
2
mais cela ne fonctionne pas avec IE. rasterizeLimitations HTML
Boban Stojanovski
@vabanagas y a-t-il un seul fichier javascript pour cela?
Mahdi Khalili
Ce changement m'a beaucoup aidé: rasterizeHTML.drawHTML(node.innerHTML, canvas) Notez que j'appelle innerHTML sur le nœud
Sep GH
5

Utilisez html2canvas, incluez simplement le plugin et la méthode d'appel pour convertir HTML en Canvas puis téléchargez comme image PNG

        html2canvas(document.getElementById("image-wrap")).then(function(canvas) {
            var link = document.createElement("a");
            document.body.appendChild(link);
            link.download = "manpower_efficiency.jpg";
            link.href = canvas.toDataURL();
            link.target = '_blank';
            link.click();
        });

Source : http://www.freakyjolly.com/convert-html-document-into-image-jpg-png-from-canvas/

Espion de code
la source
4

Je ne m'attends pas à ce que ce soit la meilleure réponse, mais cela semblait assez intéressant pour être publié.

Écrivez une application qui ouvre votre navigateur préféré au document HTML souhaité, dimensionne correctement la fenêtre et prend une capture d'écran. Ensuite, supprimez les bordures de l'image.

John Fisher
la source
4

Utilisez ce code, cela fonctionnera sûrement:

<script type="text/javascript">
 $(document).ready(function () {
	 setTimeout(function(){
		 downloadImage();
	 },1000)
 });
 
 function downloadImage(){
	 html2canvas(document.querySelector("#dvContainer")).then(canvas => {
		a = document.createElement('a'); 
		document.body.appendChild(a); 
		a.download = "test.png"; 
		a.href =  canvas.toDataURL();
		a.click();
	});	 
 }
</script>

N'oubliez pas d'inclure le fichier Html2CanvasJS dans votre programme. https://html2canvas.hertzen.com/dist/html2canvas.js

Mohit Kulkarni
la source
2

Vous ne pouvez pas faire cela avec 100% de précision avec JavaScript seul.

Il existe un outil Qt Webkit et une version python . Si vous voulez le faire vous-même, j'ai eu du succès avec Cocoa:

[self startTraverse:pagesArray performBlock:^(int collectionIndex, int pageIndex) {

    NSString *locale = [self selectedLocale];

    NSRect offscreenRect = NSMakeRect(0.0, 0.0, webView.frame.size.width, webView.frame.size.height);
    NSBitmapImageRep* offscreenRep = nil;      

    offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
                                             pixelsWide:offscreenRect.size.width
                                             pixelsHigh:offscreenRect.size.height
                                             bitsPerSample:8
                                             samplesPerPixel:4
                                             hasAlpha:YES
                                             isPlanar:NO
                                             colorSpaceName:NSCalibratedRGBColorSpace
                                             bitmapFormat:0
                                             bytesPerRow:(4 * offscreenRect.size.width)
                                             bitsPerPixel:32];

    [NSGraphicsContext saveGraphicsState];

    NSGraphicsContext *bitmapContext = [NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep];
    [NSGraphicsContext setCurrentContext:bitmapContext];
    [webView displayRectIgnoringOpacity:offscreenRect inContext:bitmapContext];
    [NSGraphicsContext restoreGraphicsState];

    // Create a small + large thumbs
    NSImage *smallThumbImage = [[NSImage alloc] initWithSize:thumbSizeSmall];  
    NSImage *largeThumbImage = [[NSImage alloc] initWithSize:thumbSizeLarge];

    [smallThumbImage lockFocus];
    [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];  
    [offscreenRep drawInRect:CGRectMake(0, 0, thumbSizeSmall.width, thumbSizeSmall.height)];  
    NSBitmapImageRep *smallThumbOutput = [[NSBitmapImageRep alloc] initWithFocusedViewRect:CGRectMake(0, 0, thumbSizeSmall.width, thumbSizeSmall.height)];  
    [smallThumbImage unlockFocus];  

    [largeThumbImage lockFocus];  
    [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];  
    [offscreenRep drawInRect:CGRectMake(0, 0, thumbSizeLarge.width, thumbSizeLarge.height)];  
    NSBitmapImageRep *largeThumbOutput = [[NSBitmapImageRep alloc] initWithFocusedViewRect:CGRectMake(0, 0, thumbSizeLarge.width, thumbSizeLarge.height)];  
    [largeThumbImage unlockFocus];  

    // Write out small
    NSString *writePathSmall = [issueProvider.imageDestinationPath stringByAppendingPathComponent:[NSString stringWithFormat:@"/%@-collection-%03d-page-%03d_small.png", locale, collectionIndex, pageIndex]];
    NSData *dataSmall = [smallThumbOutput representationUsingType:NSPNGFileType properties: nil];
    [dataSmall writeToFile:writePathSmall atomically: NO];

    // Write out lage
    NSString *writePathLarge = [issueProvider.imageDestinationPath stringByAppendingPathComponent:[NSString stringWithFormat:@"/%@-collection-%03d-page-%03d_large.png", locale, collectionIndex, pageIndex]];
    NSData *dataLarge = [largeThumbOutput representationUsingType:NSPNGFileType properties: nil];
    [dataLarge writeToFile:writePathLarge atomically: NO];
}];

J'espère que cela t'aides!

Matt Melton
la source
Avez-vous une version rapide de ce qui précède? ou si possible pouvez-vous le réécrire?
ssh
Souhaitez-vous partager le code que vous utilisez pour charger la vue Web que vous rendez? Cela semble être une approche prometteuse pour mon moteur de rendu de vignettes. Merci!
Dave
1

Installer phantomjs

$ npm install phantomjs

Créez un fichier github.js avec le code suivant

var page = require('webpage').create();
//viewportSize being the actual size of the headless browser
page.viewportSize = { width: 1024, height: 768 };
page.open('http://github.com/', function() {
    page.render('github.png');
    phantom.exit();
});

Passez le fichier comme argument à phantomjs

$ phantomjs github.js
Saurabh Saxena
la source
-3

Vous pouvez ajouter une référence HtmlRenderer à votre projet et effectuer les opérations suivantes,

string htmlCode ="<p>This is a sample html.</p>";
Image image = HtmlRender.RenderToImage(htmlCode ,new Size(500,300));
Kuttalam
la source