Renderizar HTML a una imagen

167

¿Hay alguna manera de representar html en una imagen como PNG? Sé que es posible con el lienzo, pero me gustaría representar el elemento html estándar como div, por ejemplo.

Martin Delille
fuente
Para crear algún tipo de asistencia al usuario, por ejemplo. Lamentablemente, eso no es posible (¿por razones de seguridad?). Tiene que pedirle al usuario que presione PrintScreen para hacer algo.
MaxArt
1
posible duplicado de ¿Cómo convertir HTML de un sitio web a una imagen?
Ernest Friedman-Hill
Quiero usar HTML / CSS para diseñar un logotipo.
Martin Delille
44
@ ErnestFriedman-Hill no es un duplicado: la pregunta que mencionó es específica de Java.
Martin Delille
Aquí hay un tutorial paso a paso para convertir HTML a IMAGEN en formato png o jpeg codepedia.info/2016/02/…
Satinder singh

Respuestas:

42

Sé que esta es una pregunta bastante antigua que ya tiene muchas respuestas, pero aún así pasé horas tratando de hacer lo que quería:

  • dado un archivo html, generar una imagen (png) con fondo transparente desde la línea de comando

Con Chrome sin cabeza (versión 74.0.3729.157 a partir de esta respuesta), en realidad es fácil:

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

Explicación del comando:

  • ejecuta Chrome desde la línea de comandos (aquí se muestra para Mac, pero suponiendo que sea similar en Windows o Linux)
  • --headless ejecuta Chrome sin abrirlo y sale después de completar el comando
  • --screenshotcapturará una captura de pantalla (tenga en cuenta que genera un archivo llamado screenshot.pngen la carpeta donde se ejecuta el comando)
  • --window-sizePermitir capturar solo una parte de la pantalla (el formato es --window-size=width,height)
  • --default-background-color=0es el truco de magia que le dice a Chrome que use un fondo transparente , no el color blanco predeterminado
  • finalmente proporcionas el archivo html (como una url local o remota ...)
Yan
fuente
1
¡Muy agradable! ¡Funciona también con SVG! ¡Cambié a tu solución al final! ;-)
Martin Delille
1
--screenshot='absolute\path\of\screenshot.png'funcionó para mí
palash
línea de comandante trabajado. si quiero ejecutar esto programáticamente desde express js ¿cómo ejecutarlo?
Recetas Squapl
Para su información, esto también funciona con cromo, aunque no menciona la opción en la página del manual.
Robin Lashof-Regas
El svg no es un svg para mí ... es solo un PNG con una extensión SVG ... así que ten cuidado.
Kristopolous
97

Si. HTML2Canvas existe para renderizar HTML <canvas>(que luego puede usar como imagen).

NOTA: Existe un problema conocido, que esto no funcionará con SVG

AKX
fuente
Parece interesante, pero no pude hacerlo funcionar, así que elegí la solución John Fisher. Gracias por la información, ¡veré este proyecto en el futuro!
Martin Delille
@ MartinDelille: aquí un artículo sobre el uso de HTML2Canvas para crear IMAGEN y también puede descargarlo en el lado del cliente codepedia.info/2016/02/…
Satinder singh
3
dom-to-image (ver la respuesta de tsayen) hace un trabajo mucho mejor para representar una imagen precisa.
JBE
3
Esta solución es muy lenta
hamboy75
3
otro problema, si el elemento está oculto o detrás de otro elemento, esto no funcionará
Yousef
91

¿Puedo recomendar la biblioteca dom-to-image , que fue escrita únicamente para resolver este problema (soy el responsable)
Así es como lo usa (un poco más aquí ):

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
fuente
2
¡Esto es genial! ¿Alguna forma de duplicar el tamaño de salida?
cronoklee
¡Gracias! Pero, ¿qué quieres decir exactamente con "duplicar el tamaño"?
tsayen
2
Lo primero que me viene a la mente: intenta renderizar tu imagen en SVG (usando domtoimage.toSvg), luego hazlo tú mismo en el lienzo e intenta jugar con la resolución de ese lienzo de alguna manera. Probablemente sea posible implementar una función como algún tipo de opción de representación en la propia biblioteca, por lo que puede pasar las dimensiones de la imagen en píxeles. Si lo necesita, le agradecería que creara un problema en github.
tsayen
8
Esto es MUCHO mejor que html2canvas. Gracias.
c
1
@Subho es una cadena que contiene la URL con datos codificados en base64
tsayen
66

Hay muchas opciones y todas tienen sus ventajas y desventajas.

Opción 1: use una de las muchas bibliotecas disponibles

Pros

  • La conversión es bastante rápida la mayor parte del tiempo

Contras

  • Mala representación
  • No ejecuta javascript
  • No se admiten funciones web recientes (FlexBox, Selectores avanzados, Fuentes web, Tamaño de cuadro, Consultas de medios, ...)
  • A veces no es tan fácil de instalar
  • Complicado a escala

Opción 2: usar PhantomJs y tal vez una biblioteca de contenedor

Pros

  • Ejecutar Javascript
  • Bastante rapido

Contras

  • Mala representación
  • No se admiten funciones web recientes (FlexBox, Selectores avanzados, Fuentes web, Tamaño de cuadro, Consultas de medios, ...)
  • Complicado a escala
  • No es tan fácil hacerlo funcionar si hay imágenes para cargar ...

Opción 3: use Chrome Headless y tal vez una biblioteca de contenedor

Pros

  • Ejecutar Javascript
  • Representación casi perfecta

Contras

  • No es tan fácil tener exactamente el resultado deseado con respecto a:
    • tiempo de carga de la página
    • dimensiones de la ventana gráfica
  • Complicado a escala
  • Bastante lento e incluso más lento si el html contiene enlaces externos

Opción 4: usar una API

Pros

  • Ejecutar Javascript
  • Representación casi perfecta
  • Rápido cuando las opciones de almacenamiento en caché se usan correctamente
  • La escala es manejada por las API
  • Tiempo preciso, ventana gráfica, ...
  • La mayoría de las veces ofrecen un plan gratuito.

Contras

  • No es gratis si planeas usarlos mucho

Divulgación: soy el fundador de ApiFlash. Hice todo lo posible para proporcionar una respuesta honesta y útil.

Timothée Jeannin
fuente
2
Gracias por una respuesta detallada con muchas posibles soluciones
bszom
wkhtmltoimage / pdf admite la representación de JavaScript. Se puede establecer un retardo de Javascript o dejar cheque wkhtml para un window.status specifc (que se puede establecer con javascript cuando se conoce su materia se hace js)
Daniel Z.
PhantomJS admite cosas dinámicas como Google Maps. Es realmente un navegador web completo, solo que sin una pantalla adjunta. Sin embargo, debe esperar un poco para que Google Map cargue los mosaicos con la geografía (espero 500 ms y permito que las personas cambien el retraso; si no es suficiente, volver a ejecutar sin aumentar el tiempo está bien, porque esos mosaicos están en caché).
Ladislav Zima
50

Todas las respuestas aquí usan bibliotecas de terceros, mientras que el procesamiento de HTML en una imagen puede ser relativamente simple en Javascript puro. No se fue incluso un artículo al respecto en la sección de lienzo en el MDN.

El truco es este:

  • crear un SVG con un nodo foreignObject que contenga su XHTML
  • establecer el src de una imagen a la url de datos de ese SVG
  • drawImage en el lienzo
  • establecer datos de lienzo para image.src de destino

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

Algunas cosas a tener en cuenta

  • El HTML dentro del SVG tiene que ser XHTML
  • Por razones de seguridad, el SVG como URL de datos de una imagen actúa como un alcance CSS aislado para el HTML ya que no se pueden cargar fuentes externas. Por lo tanto, una fuente de Google, por ejemplo, tiene que estar en línea usando una herramienta como esta .
  • Incluso cuando el HTML dentro del SVG excede el tamaño de la imagen, se dibujará correctamente en el lienzo. Pero la altura real no se puede medir a partir de esa imagen. Una solución de altura fija funcionará bien, pero la altura dinámica requerirá un poco más de trabajo. Lo mejor es representar los datos SVG en un iframe (para un alcance CSS aislado) y usar el tamaño resultante para el lienzo.
Sjeiti
fuente
¡Agradable! Eliminar la dependencia de la biblioteca externa es, de hecho, la mejor manera de hacerlo. Cambié mi respuesta aceptada :-)
Martin Delille
Ese artículo ya no está allí.
MSC
2
Gran respuesta, pero comentando el estado del arte del HTML y las API web, que como de costumbre parece que algo extrajo a alguien detrás: toda la funcionalidad está técnicamente allí pero expuesta detrás de una matriz (sin juego de palabras) de rutas de código extrañas que se parecen nada del tipo de claridad que esperaría de una API bien diseñada. En términos simples: lo más probable es que sea trivial para un navegador permitir que Documenta se convierta en un ráster (por ejemplo, a Canvas), pero como nadie se molestó en estandarizarlo, tenemos que recurrir a la locura como se ilustra arriba (sin ofender al autor de esto código).
amn
1
Esta respuesta stackoverflow.com/questions/12637395/… parece indicar que puedes llegar bastante lejos. Mozilla y Chrome tienen una longitud de uri de datos ilimitada e incluso el IE actual permite 4 GB, que se reduce a aproximadamente 3 GB de imágenes (debido a base64). Pero esto sería algo bueno para probar.
Sjeiti
3
- algunas pruebas rápidas y sucias más tarde - jsfiddle.net/Sjeiti/pcwudrmc/75965 Chrome, Edge y Firefox alcanzan un ancho de al menos 10,000 píxeles que (en este caso) es un recuento de caracteres de aproximadamente 10,000,000 y un tamaño de archivo png de 8MB. No lo he probado rigurosamente, pero es suficiente para la mayoría de los casos de uso.
Sjeiti
13

Podrías usar PhantomJS , que es un webkit sin cabeza (el motor de renderizado en safari y (hasta hace poco) Chrome). Puedes aprender cómo hacer capturas de pantalla de páginas aquí . ¡Espero que ayude!

TheContrarian
fuente
Esta técnica funciona bien. Sin embargo, han pasado 2 años desde su comentario. ¿Te has encontrado con algo que funcione más rápido que PhantomJS? La generación de imágenes generalmente toma 2-5 segundos en Phantom, en mi experiencia.
Brian Webster
Chrome sin cabeza ahora es posible. No sé si es más rápido, pero si quieres capturas de pantalla precisas, esta es una buena manera de hacerlo.
Josh Maag
11

Puede utilizar una herramienta HTML a PDF como wkhtmltopdf. Y luego puede usar una herramienta de PDF a imagen como imagemagick. Es cierto que esto es del lado del servidor y un proceso muy complicado ...

PinnyM
fuente
12
O simplemente puede ejecutar la imagen de wkhtmltopdf hermano, wkhtmltoimage.
Roman Starkov
1
Esta biblioteca es para Node.js. ¿Qué tal una interfaz simple?
Vlad
9

La única biblioteca que pude trabajar para Chrome, Firefox y MS Edge fue rasterizeHTML . Produce una mejor calidad que HTML2Canvas y todavía es compatible a diferencia de HTML2Canvas.

Obtener elemento y descargar como 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
fuente
2
pero no funciona con IE. Limitaciones de rasterizeHTML
Boban Stojanovski
@vabanagas, ¿hay un solo archivo javascript para esto?
Mahdi Khalili
Este cambio me ayudó mucho: rasterizeHTML.drawHTML(node.innerHTML, canvas) tenga en cuenta que estoy llamando a innerHTML en el nodo
Sep GH
5

Use html2canvas solo incluya el complemento y el método de llamada para convertir HTML a Canvas y luego descargue como imagen 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();
        });

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

Code Spy
fuente
4

No espero que esta sea la mejor respuesta, pero me pareció lo suficientemente interesante como para publicar.

Escriba una aplicación que abra su navegador favorito al documento HTML deseado, ajuste el tamaño de la ventana correctamente y tome una captura de pantalla. Luego, elimine los bordes de la imagen.

John Fisher
fuente
4

Use este código, seguramente funcionará:

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

Simplemente no olvide incluir el archivo Html2CanvasJS en su programa. https://html2canvas.hertzen.com/dist/html2canvas.js

Mohit Kulkarni
fuente
2

No puede hacer esto 100% con precisión solo con JavaScript.

Hay una herramienta Qt Webkit por ahí, y una versión de Python . Si quieres hacerlo tú mismo, he tenido éxito 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];
}];

¡Espero que esto ayude!

Matt Melton
fuente
¿Tienes una versión rápida de lo anterior? o si es posible, ¿puedes volver a escribirlo?
ssh
¿Te importaría compartir el código que usas para cargar el webView que estás renderizando? Esto parece un enfoque prometedor para mi renderizador de miniaturas. ¡Gracias!
Dave
1

Instalar phantomjs

$ npm install phantomjs

Cree un archivo github.js con el siguiente código

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

Pase el archivo como argumento a phantomjs

$ phantomjs github.js
Saurabh Saxena
fuente
-3

Puede agregar HtmlRenderer de referencia a su proyecto y hacer lo siguiente,

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