Rendering HTML su un'immagine


167

C'è un modo per eseguire il rendering html di immagini come PNG? So che è possibile con canvas ma vorrei renderizzare l'elemento html standard come div per esempio.


Per creare una sorta di assistenza all'utente, ad esempio. Purtroppo, ciò non è possibile (per motivi di sicurezza?). Devi chiedere all'utente di premere PrintScreen per fare qualcosa.
MaxArt,


Voglio usare HTML / CSS per progettare un logo.
Martin Delille,

4
@ ErnestFriedman-Hill non è un duplicato: la domanda che hai menzionato è specifica di Java.
Martin Delille,

Ecco un tutorial passo per passo per convertire HTML in IMMAGINE png o formato jpeg codepedia.info/2016/02/…
Satinder singh

Risposte:


42

So che questa è una domanda piuttosto vecchia che ha già molte risposte, eppure ho ancora passato ore a cercare di fare davvero quello che volevo:

  • dato un file html, genera un'immagine (png) con sfondo trasparente dalla riga di comando

Utilizzando Chrome senza testa (versione 74.0.3729.157 a partire da questa risposta), in realtà è facile:

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

Spiegazione del comando:

  • esegui Chrome dalla riga di comando (qui mostrato per Mac, ma presupponendo simile su Windows o Linux)
  • --headless esegue Chrome senza aprirlo ed esce al termine del comando
  • --screenshotacquisirà uno screenshot (nota che genera un file chiamato screenshot.pngnella cartella in cui viene eseguito il comando)
  • --window-sizeconsentire di catturare solo una parte dello schermo (il formato è --window-size=width,height)
  • --default-background-color=0è il trucco magico che dice a Chrome di utilizzare uno sfondo trasparente , non il colore bianco predefinito
  • infine fornisci il file html (come url locale o remoto ...)

1
Molto bella! Funziona anche con SVG! Alla fine sono passato alla tua soluzione! ;-)
Martin Delille,

1
--screenshot='absolute\path\of\screenshot.png'ha funzionato per me
palash il

linea comune ha funzionato. se voglio eseguirlo a livello di codice da Express js come eseguirlo?
Ricette Squapl l'

Cordiali saluti, questo funziona anche con il cromo, anche se non menziona l'opzione nella pagina man.
Robin Lashof-Regas,

Lo svg non è uno svg per me ... è solo un PNG con estensione SVG ... quindi fai attenzione.
kristopolous

97

Sì. HTML2Canvas esiste per eseguire il rendering HTML <canvas>(che è quindi possibile utilizzare come immagine).

NOTA: esiste un problema noto che non funziona con SVG


Sembra interessante ma non sono riuscito a farlo funzionare, quindi ho scelto la soluzione John Fisher. Grazie per le informazioni, guarderò questo progetto in futuro!
Martin Delille,

@MartinDelille: qui un articolo sull'uso di HTML2Canvas per creare IMMAGINE e puoi anche scaricarlo sul lato client codepedia.info/2016/02/…
Satinder singh,

3
dom-to-image (vedi la risposta di tsayen) fa un lavoro molto migliore per rendere un'immagine accurata.
JBE

3
Questa soluzione è molto lenta
hamboy75,

3
un altro problema, se l'elemento è nascosto o dietro un altro elemento questo non funzionerà
Yousef

91

Posso raccomandare una libreria dom-to-image , che è stata scritta esclusivamente per risolvere questo problema (sono il manutentore).
Ecco come lo usi (alcuni altri qui ):

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);
    });

2
Questo è fantastico! Un modo per raddoppiare le dimensioni dell'output?
cronoklee,

Grazie! Ma cosa intendi esattamente per "raddoppiare le dimensioni"?
Tsayen,

2
Prima cosa che mi viene in mente: prova a rendere l'immagine in SVG (usando domtoimage.toSvg), quindi esegui il rendering da solo su tela e prova a giocare con la risoluzione di quella tela in qualche modo. Probabilmente è possibile implementare tale funzionalità come una sorta di opzione di rendering nella libreria stessa, in modo da poter passare le dimensioni dell'immagine in pixel. Se ne hai bisogno, ti sarei grato per aver creato un problema su Github.
Tsayen,

8
Questo è MOLTO meglio di html2canvas. Grazie.
circa

1
@Subho è una stringa contenente l'URL con dati codificati in base64
tsayen,

67

Ci sono molte opzioni e tutte hanno i loro pro e contro.

Opzione 1: utilizzare una delle molte librerie disponibili

Professionisti

  • La conversione è abbastanza veloce per la maggior parte del tempo

Contro

  • Cattivo rendering
  • Non esegue JavaScript
  • Nessun supporto per le funzionalità Web recenti (FlexBox, Selettori avanzati, Font Web, Dimensionamento box, Media query, ...)
  • A volte non è così facile da installare
  • Complicato per ridimensionare

Opzione 2: utilizzare PhantomJs e forse una libreria wrapper

Professionisti

  • Esegui Javascript
  • Abbastanza veloce

Contro

  • Cattivo rendering
  • Nessun supporto per le funzionalità Web recenti (FlexBox, Selettori avanzati, Font Web, Dimensionamento box, Media query, ...)
  • Complicato per ridimensionare
  • Non è così facile farlo funzionare se ci sono immagini da caricare ...

Opzione 3: utilizzare Chrome Headless e forse una libreria wrapper

Professionisti

  • Esegui Javascript
  • Rendering quasi perfetto

Contro

  • Non è così facile avere esattamente il risultato desiderato per quanto riguarda:
    • tempismo di caricamento della pagina
    • dimensioni della finestra
  • Complicato per ridimensionare
  • Abbastanza lento e persino più lento se l'html contiene collegamenti esterni

Opzione 4: utilizzare un'API

Professionisti

  • Esegui Javascript
  • Rendering quasi perfetto
  • Veloce quando le opzioni di memorizzazione nella cache sono utilizzate correttamente
  • La scala è gestita dalle API
  • Tempi precisi, finestra, ...
  • Il più delle volte offrono un piano gratuito

Contro

  • Non gratuito se prevedi di usarli molto

Disclosure: sono il fondatore di ApiFlash. Ho fatto del mio meglio per fornire una risposta onesta e utile.


2
Grazie per una risposta dettagliata con molte possibili soluzioni
bszom,

wkhtmltoimage / pdf supporta il rendering javascript. Puoi impostare un ritardo javascript o lasciare che wkhtml controlli una specifica window.status (che puoi impostare con javascript quando sai che la tua roba js è terminata)
Daniel Z.

PhantomJS supporta elementi dinamici come Google Maps. È davvero un browser web completo, solo senza un display collegato. Tuttavia devi aspettare un po 'che Google Map carichi le tessere con la geografia (aspetto 500 ms e consento alle persone di modificare il ritardo - se non è abbastanza, correre di nuovo senza aumentare il tempo va bene, perché quelle tessere sono memorizzate nella cache).
Ladislav Zima,

50

Tutte le risposte qui usano librerie di terze parti mentre il rendering HTML su un'immagine può essere relativamente semplice in Javascript puro. C'è stato anche un articolo su di esso nella sezione tela su MDN.

Il trucco è questo:

  • crea un SVG con un nodo foreignObject contenente il tuo XHTML
  • imposta lo src di un'immagine sull'URL dei dati di quel file SVG
  • drawImage sulla tela
  • imposta i dati del canvas su target image.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()
}

Alcune cose da notare

  • L'HTML all'interno di SVG deve essere XHTML
  • Per motivi di sicurezza, l'SVG come url di dati di un'immagine funge da ambito CSS isolato per l'HTML poiché non è possibile caricare origini esterne. Quindi, ad esempio, un carattere Google deve essere integrato utilizzando uno strumento come questo .
  • Anche quando l'HTML all'interno dell'SVG supera la dimensione dell'immagine, verrà disegnato correttamente sulla tela. Ma l'altezza effettiva non può essere misurata da quell'immagine. Una soluzione ad altezza fissa funzionerà perfettamente, ma l'altezza dinamica richiederà un po 'più di lavoro. La cosa migliore è rendere i dati SVG in un iframe (per ambito CSS isolato) e usare la dimensione risultante per il canvas.

Bello! Rimuovere la dipendenza da una libreria esterna è davvero il modo migliore di procedere. Ho cambiato la mia risposta accettata :-)
Martin Delille,

Quell'articolo non è più lì.
MSC

2
Ottima risposta, ma commentando lo stato dell'arte delle API HTML e Web, che come al solito sembra qualcosa tirato fuori da qualcuno - tutta la funzionalità è tecnicamente lì ma esposta dietro un array (nessun gioco di parole inteso) di percorsi di codice strani che assomigliano niente del tipo di chiarezza che ti aspetteresti da un'API ben progettata. In parole povere: è molto banale che un browser consenta di renderlo Documentun raster (ad esempio a Canvas), ma poiché nessuno si è preso la briga di standardizzarlo, dobbiamo ricorrere alla follia come illustrato sopra (nessuna offesa per l'autore di questo codice).
AMN

1
Questa risposta stackoverflow.com/questions/12637395/… sembra indicare che puoi andare abbastanza lontano. Mozilla e Chrome hanno una lunghezza dei dati illimitata e persino IE corrente consente 4 GB, che si riducono a circa 3 GB di immagini (a causa di base64). Ma questa sarebbe una buona cosa da testare.
Sjeiti

3
- alcuni test rapidi e sporchi in seguito - jsfiddle.net/Sjeiti/pcwudrmc/75965 Chrome, Edge e Firefox arrivano fino a un'altezza di almeno 10.000 pixel che (in questo caso) ha un conteggio dei caratteri di circa 10.000.000 e una dimensione del file png di 8 MB. Non sono stati testati rigorosamente ma è sufficiente per la maggior parte dei casi d'uso.
Sjeiti,

13

È possibile utilizzare PhantomJS , che è un driver webkit senza testa (il motore di rendering in Safari e (fino a poco tempo fa) Chrome) driver. Puoi imparare come fare la cattura dello schermo delle pagine qui . Spero che aiuti!


Questa tecnica funziona bene. Tuttavia, sono trascorsi 2 anni dal tuo commento. Hai mai trovato qualcosa che funziona più velocemente di PhantomJS? Generazione di immagini richiede in genere 2-5 secondi in Phantom, secondo la mia esperienza.
Brian Webster,

Chrome senza testa è ora possibile. Non so se sia più veloce, ma se vuoi schermate precise, questo è un buon modo per andare.
Josh Maag,

11

Puoi usare uno strumento da HTML a PDF come wkhtmltopdf. E quindi è possibile utilizzare un PDF per lo strumento di immagine come imagemagick. Certamente questo è un lato server e un processo molto complicato ...


12
Oppure potresti semplicemente eseguire l'immagine fratello di wkhtmltopdf, wkhtmltoimage.
Roman Starkov,

1
Questa libreria è per Node.js. Che ne dici di un semplice frontend?
Vlad

9

L'unica libreria che ho potuto lavorare per Chrome, Firefox e MS Edge era rasterizeHTML . Produce una migliore qualità di HTML2Canvas ed è ancora supportato a differenza di HTML2Canvas.

Ottenere Element e scaricarlo come 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);
            }
     });

2
ma non funziona con IE. Limitazioni HTML
Boban Stojanovski,

@vabanagas esiste un singolo file javascript per questo?
Mahdi Khalili il

Questa modifica mi ha aiutato molto: rasterizeHTML.drawHTML(node.innerHTML, canvas) nota che sto chiamando innerHTML sul nodo
settembre GH

5

Usa html2canvas includi solo il plug-in e chiama il metodo per convertire HTML in Canvas, quindi scarica come immagine 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();
        });

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


4

Non mi aspetto che questa sia la risposta migliore, ma mi è sembrato abbastanza interessante postare.

Scrivi un'app che apre il tuo browser preferito al documento HTML desiderato, ridimensiona correttamente la finestra e scatta una schermata. Quindi, rimuovere i bordi dell'immagine.


4

Usa questo codice, funzionerà sicuramente:

<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>

Non dimenticare di includere il file Html2CanvasJS nel tuo programma. https://html2canvas.hertzen.com/dist/html2canvas.js


2

Non puoi farlo con precisione al 100% solo con JavaScript.

C'è uno strumento Qt Webkit là fuori e una versione di Python . Se vuoi farlo da solo, ho avuto successo con 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];
}];

Spero che questo ti aiuti!


Hai una versione rapida di quanto sopra? o se possibile, per favore, puoi riscriverlo?
ssh,

Ti dispiacerebbe condividere il codice che usi per caricare il webView che stai visualizzando? Questo sembra un approccio promettente per il mio renderer di miniature. Grazie!
Dave,

1

Installa phantomjs

$ npm install phantomjs

Crea un file github.js con il seguente codice

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();
});

Passa il file come argomento a phantomjs

$ phantomjs github.js


-3

Puoi aggiungere HtmlRenderer di riferimento al tuo progetto ed eseguire le seguenti operazioni,

string htmlCode ="<p>This is a sample html.</p>";
Image image = HtmlRender.RenderToImage(htmlCode ,new Size(500,300));
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.