Rendern Sie HTML in ein Bild

164

Gibt es eine Möglichkeit, HTML in ein Bild wie PNG zu rendern? Ich weiß, dass es mit Canvas möglich ist, aber ich möchte ein Standard-HTML-Element wie beispielsweise div rendern.

Martin Delille
quelle
Zum Beispiel, um eine Art Benutzerunterstützung zu erstellen. Leider ist das nicht möglich (aus Sicherheitsgründen?). Sie müssen den Benutzer bitten, PrintScreen zu drücken, um etwas zu tun.
MaxArt
1
mögliches Duplikat von Wie konvertiere ich HTML einer Website in ein Bild?
Ernest Friedman-Hill
Ich möchte HTML / CSS verwenden, um ein Logo zu entwerfen.
Martin Delille
4
@ ErnestFriedman-Hill kein Duplikat: Die Frage, die Sie erwähnt haben, ist spezifisch für Java.
Martin Delille
Hier ist eine Schritt-für-Schritt-Anleitung zum Konvertieren von HTML in das IMAGE-PNG- oder JPEG-
Satinder singh

Antworten:

41

Ich weiß, dass dies eine ziemlich alte Frage ist, die bereits viele Antworten hat, aber ich habe immer noch stundenlang versucht, das zu tun, was ich wollte:

  • Generieren Sie bei einer HTML-Datei ein (PNG-) Bild mit transparentem Hintergrund über die Befehlszeile

Mit Chrome Headless (Version 74.0.3729.157 ab dieser Antwort) ist es eigentlich ganz einfach:

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

Erläuterung des Befehls:

  • Sie führen Chrome über die Befehlszeile aus (hier für den Mac gezeigt, unter Windows oder Linux jedoch ähnlich).
  • --headless führt Chrome aus, ohne es zu öffnen, und wird nach Abschluss des Befehls beendet
  • --screenshotwird einen Screenshot aufnehmen (beachten Sie, dass eine Datei generiert wird, die screenshot.pngin dem Ordner aufgerufen wird, in dem der Befehl ausgeführt wird)
  • --window-sizeErlaube nur einen Teil des Bildschirms zu erfassen (Format ist --window-size=width,height)
  • --default-background-color=0ist der Zaubertrick, der Chrome anweist, einen transparenten Hintergrund zu verwenden, nicht die standardmäßige weiße Farbe
  • Schließlich geben Sie die HTML-Datei an (als URL entweder lokal oder remote ...)
Yan
quelle
1
Sehr schön! Es funktioniert auch mit SVG! Ich habe am Ende zu Ihrer Lösung gewechselt! ;-)
Martin Delille
1
--screenshot='absolute\path\of\screenshot.png'arbeitete für mich
Palash
Kommanzeile funktioniert. Wenn ich dies programmgesteuert von Express Js ausführen möchte, wie wird es ausgeführt?
Squapl Rezepte
Zu Ihrer Information, dies funktioniert auch mit Chrom, obwohl die Option in der Manpage nicht erwähnt wird.
Robin Lashof-Regas
Das SVG ist für mich kein SVG ... es ist nur ein PNG mit einer SVG-Erweiterung ... also pass auf.
kristopolous
97

Ja. HTML2Canvas ist vorhanden, um HTML zu rendern <canvas>(das Sie dann als Bild verwenden können).

HINWEIS: Es ist ein Problem bekannt, dass dies mit SVG nicht funktioniert

AKX
quelle
Es scheint interessant zu sein, aber ich habe es nicht geschafft, es zum Laufen zu bringen, also habe ich mich für die John Fisher-Lösung entschieden. Danke für die Info, ich werde dieses Projekt in Zukunft sehen!
Martin Delille
@ MartinDelille: hier ein Artikel über die Verwendung von HTML2Canvas zum Erstellen von BILD und Sie können es auch auf clientseitiger codepedia.info/2016/02/… herunterladen
Satinder singh
3
Dom-to-Image (siehe Tsayens Antwort) macht es viel besser, ein genaues Bild zu rendern.
JBE
3
Diese Lösung ist sehr langsam
hamboy75
3
Ein weiteres Problem, wenn das Element versteckt ist oder sich hinter einem anderen Element befindet, funktioniert dies nicht
Yousef
91

Darf ich eine Dom-to-Image- Bibliothek empfehlen , die ausschließlich zur Behebung dieses Problems geschrieben wurde (ich bin der Betreuer).
So verwenden Sie es (weitere hier ):

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
quelle
2
Das ist toll! Gibt es eine Möglichkeit, die Ausgabegröße zu verdoppeln?
Cronoklee
Vielen Dank! Aber was genau meinst du mit "Verdoppelung der Größe"?
Tsayen
2
Das erste, was mir in den Sinn kommt - versuchen Sie, Ihr Bild in SVG zu rendern (mit domtoimage.toSvg), dann rendern Sie es selbst auf Leinwand und versuchen Sie, irgendwie mit der Auflösung dieser Leinwand zu spielen. Es ist wahrscheinlich möglich, eine solche Funktion als eine Art Rendering-Option in der Bibliothek selbst zu implementieren, sodass Sie Bildabmessungen in Pixel übergeben können. Wenn Sie es brauchen, würde ich mich freuen, wenn Sie ein Problem mit Github erstellen.
Tsayen
8
Dies ist viel besser als html2canvas. Vielen Dank.
c.hughes
1
@ Subho ist ein String, der die URL mit base64-codierten Daten enthält
tsayen
65

Es gibt viele Möglichkeiten und alle haben ihre Vor- und Nachteile.

Option 1: Verwenden Sie eine der vielen verfügbaren Bibliotheken

Vorteile

  • Die Konvertierung ist die meiste Zeit ziemlich schnell

Nachteile

  • Schlechtes Rendering
  • Führt kein Javascript aus
  • Keine Unterstützung für aktuelle Webfunktionen (FlexBox, Advanced Selectors, Webfonts, Box Sizing, Medienabfragen, ...)
  • Manchmal nicht so einfach zu installieren
  • Kompliziert maßstabsgetreu

Option 2: Verwenden Sie PhantomJs und möglicherweise eine Wrapper-Bibliothek

Vorteile

  • Führen Sie Javascript aus
  • Ziemlich schnell

Nachteile

  • Schlechtes Rendering
  • Keine Unterstützung für aktuelle Webfunktionen (FlexBox, Advanced Selectors, Webfonts, Box Sizing, Medienabfragen, ...)
  • Kompliziert maßstabsgetreu
  • Es ist nicht so einfach, es zum Laufen zu bringen, wenn Bilder geladen werden müssen ...

Option 3: Verwenden Sie Chrome Headless und möglicherweise eine Wrapper-Bibliothek

Vorteile

  • Führen Sie Javascript aus
  • Nahezu perfektes Rendering

Nachteile

  • Es ist nicht so einfach, genau das gewünschte Ergebnis zu erzielen:
    • Timing des Seitenladens
    • Abmessungen des Ansichtsfensters
  • Kompliziert maßstabsgetreu
  • Ganz langsam und noch langsamer, wenn das HTML externe Links enthält

Option 4: Verwenden Sie eine API

Vorteile

  • Führen Sie Javascript aus
  • Nahezu perfektes Rendering
  • Schnell, wenn die Caching-Optionen korrekt verwendet werden
  • Die Skalierung wird von den APIs übernommen
  • Präzises Timing, Ansichtsfenster, ...
  • Meistens bieten sie einen kostenlosen Plan an

Nachteile

  • Nicht kostenlos, wenn Sie vorhaben, sie häufig zu verwenden

Offenlegung: Ich bin der Gründer von ApiFlash. Ich habe mein Bestes getan, um eine ehrliche und nützliche Antwort zu geben.

Timothée Jeannin
quelle
2
Vielen Dank für eine detaillierte Antwort mit vielen möglichen Lösungen
bszom
wkhtmltoimage / pdf unterstützt das Rendern von Javascript. Sie können eine Javascript-Verzögerung festlegen oder wkhtml nach einem bestimmten window.status suchen lassen (den Sie mit Javascript festlegen können, wenn Sie wissen, dass Ihre js-Aufgaben erledigt sind)
Daniel Z.
PhantomJS unterstützt dynamische Inhalte wie Google Maps. Es ist wirklich ein vollständiger Webbrowser, nur ohne angeschlossenes Display. Sie müssen jedoch etwas warten, bis Google Map die Kacheln mit der Geografie geladen hat (ich warte 500 ms und erlaube den Leuten, die Verzögerung zu ändern - wenn es nicht ausreicht, ist es in Ordnung, erneut zu laufen, ohne die Zeit zu verlängern, da diese Kacheln zwischengespeichert sind).
Ladislav Zima
50

Alle Antworten hier verwenden Bibliotheken von Drittanbietern, während das Rendern von HTML in ein Bild in reinem Javascript relativ einfach sein kann. Es ist sogar ein Artikel über sie auf der Leinwand Abschnitt auf MDN.

Der Trick ist folgender:

  • Erstellen Sie eine SVG mit einem ForeignObject-Knoten, der Ihre XHTML enthält
  • Setzen Sie den Quellcode eines Bildes auf die Daten-URL dieser SVG
  • drawImage auf die Leinwand
  • Setzen Sie die Canvas-Daten auf 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()
}

Einige Dinge zu beachten

  • Der HTML-Code in der SVG muss XHTML sein
  • Aus Sicherheitsgründen fungiert die SVG als Daten-URL eines Bildes als isolierter CSS-Bereich für den HTML-Code, da keine externen Quellen geladen werden können. So hat eine Google Schrift zum Beispiel inlined werden , um ein Werkzeug wie mit diesem .
  • Selbst wenn der HTML-Code in der SVG die Größe des Bildes überschreitet, wird er korrekt auf die Leinwand gezeichnet. Die tatsächliche Höhe kann jedoch nicht anhand dieses Bildes gemessen werden. Eine Lösung mit fester Höhe funktioniert einwandfrei, aber die dynamische Höhe erfordert etwas mehr Arbeit. Am besten rendern Sie die SVG-Daten in einen Iframe (für isolierten CSS-Bereich) und verwenden die resultierende Größe für die Zeichenfläche.
Sjeiti
quelle
Nett! Das Entfernen der Abhängigkeit von externen Bibliotheken ist in der Tat der richtige Weg. Ich habe meine akzeptierte Antwort geändert :-)
Martin Delille
Dieser Artikel ist nicht mehr da.
MSC
2
Tolle Antwort, aber Kommentare zum Stand der Technik von HTML- und Web-APIs, die wie üblich wie etwas aussehen, das jemand hinter sich gelassen hat - die gesamte Funktionalität ist technisch vorhanden, aber hinter einem Array (kein Wortspiel beabsichtigt) seltsamer Codepfade, die ähneln Nichts von der Klarheit, die Sie von gut gestalteten APIs erwarten würden. Im Klartext: Es ist höchstwahrscheinlich trivial Document, wenn ein Browser zulässt , dass a in ein Raster gerendert wird (z. B. a Canvas), aber da sich niemand die Mühe gemacht hat, es zu standardisieren, müssen wir auf den oben dargestellten Wahnsinn zurückgreifen (keine Beleidigung beim Autor Code).
Am
1
Diese Antwort stackoverflow.com/questions/12637395/… scheint darauf hinzudeuten, dass Sie ziemlich weit gehen können. Mozilla und Chrome haben eine unbegrenzte Datenlänge und sogar der aktuelle IE erlaubt 4 GB, was auf etwa 3 GB Bilder (aufgrund von base64) hinausläuft. Aber das wäre eine gute Sache zu testen.
Sjeiti
3
- einige schnelle und schmutzige Tests später - jsfiddle.net/Sjeiti/pcwudrmc/75965 Chrome, Edge und Firefox erreichen eine Breite von mindestens 10.000 Pixel, was (in diesem Fall) einer Zeichenanzahl von etwa 10.000.000 und einer PNG-Dateigröße entspricht von 8 MB. Ich habe nicht rigoros getestet, aber es reicht für die meisten Anwendungsfälle.
Sjeiti
13

Sie können PhantomJS verwenden , ein Headless-Webkit (die Rendering-Engine in Safari und (bis vor kurzem) Chrome) -Treiber. Sie können lernen , wie Screen - Capture - Seiten zu tun hier . Hoffentlich hilft das!

TheContrarian
quelle
Diese Technik funktioniert gut. Seit Ihrem Kommentar sind jedoch 2 Jahre vergangen. Sind Sie auf etwas gestoßen, das schneller als PhantomJS funktioniert? Nach meiner Erfahrung dauert die Bilderzeugung in Phantom normalerweise 2-5 Sekunden.
Brian Webster
Headless Chrome ist jetzt möglich. Ich weiß nicht, ob es schneller ist, aber wenn Sie genaue Screenshots wünschen, ist dies ein guter Weg.
Josh Maag
11

Sie können ein HTML-zu-PDF-Tool wie wkhtmltopdf verwenden. Und dann können Sie ein PDF-to-Image-Tool wie imagemagick verwenden. Zugegeben, dies ist serverseitig und ein sehr komplizierter Prozess ...

PinnyM
quelle
12
Oder Sie könnten einfach wkhtmltopdfs Image-Bruder wkhtmltoimage ausführen.
Roman Starkov
1
Diese Bibliothek ist für Node.js. Wie wäre es mit einem einfachen Frontend?
Vlad
9

Die einzige Bibliothek, die ich für Chrome, Firefox und MS Edge arbeiten konnte, war rasterizeHTML . Es gibt eine bessere Qualität als HTML2Canvas aus und wird im Gegensatz zu HTML2Canvas weiterhin unterstützt.

Element abrufen und als PNG herunterladen

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
quelle
2
aber es funktioniert nicht mit IE. rasterizeHTML Einschränkungen
Boban Stojanovski
@vabanagas gibt es dafür eine einzelne Datei Javascript?
Mahdi Khalili
Diese Änderung hat mir sehr geholfen: rasterizeHTML.drawHTML(node.innerHTML, canvas) Beachten Sie, dass ich innerHTML auf dem Knoten aufrufe
Sep GH
5

Verwenden Sie html2canvas, fügen Sie einfach das Plugin und die Aufrufmethode hinzu , um HTML in Canvas zu konvertieren, und laden Sie es dann als Bild-PNG herunter

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

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

Code Spy
quelle
4

Ich erwarte nicht, dass dies die beste Antwort ist, aber es schien interessant genug zu sein, um etwas zu posten.

Schreiben Sie eine App, die Ihren Lieblingsbrowser für das gewünschte HTML-Dokument öffnet, das Fenster richtig dimensioniert und einen Screenshot macht. Entfernen Sie dann die Bildränder.

John Fisher
quelle
4

Verwenden Sie diesen Code, es wird sicherlich funktionieren:

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

Vergessen Sie nicht, die Datei Html2CanvasJS in Ihr Programm aufzunehmen. https://html2canvas.hertzen.com/dist/html2canvas.js

Mohit Kulkarni
quelle
2

Mit JavaScript allein können Sie dies nicht 100% genau tun.

Es gibt ein Qt-Webkit- Tool und eine Python-Version . Wenn Sie es selbst machen wollen, habe ich Erfolg mit Cocoa gehabt:

[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];
}];

Hoffe das hilft!

Matt Melton
quelle
Haben Sie eine schnelle Version der oben genannten? oder wenn möglich kannst du es bitte neu schreiben?
SSH
Würde es Ihnen etwas ausmachen, den Code zu teilen, den Sie zum Laden der von Ihnen gerenderten WebView verwenden? Dies scheint ein vielversprechender Ansatz für meinen Miniaturbild-Renderer zu sein. Vielen Dank!
Dave
1

Installieren Sie Phantomjs

$ npm install phantomjs

Erstellen Sie eine Datei github.js mit folgendem Code

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

Übergeben Sie die Datei als Argument an phantomjs

$ phantomjs github.js
Saurabh Saxena
quelle
1

Das kannst du sicher. Mit der JavaScript-API von GrabzIt können Sie ein Div von einer Webseite wie der folgenden erfassen:

<script type="text/javascript" src="grabzit.min.js"></script>
<script type="text/javascript">
GrabzIt("Your Application Key").ConvertURL("http://www.example.com/my-page.html",
{"target": "#features", "bheight": -1, "height": -1, "width": -1}).Create();
</script>

Wobei #features die ID des zu erfassenden Div ist. Wenn Sie HTML in ein Bild konvertieren möchten. Sie könnten diese Technik verwenden:

GrabzIt("Your Application Key").ConvertHTML(
"<html><body><h1>Hello World!</h1></body></html>").Create();

Haftungsausschluss Ich habe diese API erstellt!

GrabzIt
quelle
-3

Sie können Ihrem Projekt den Referenz- HTML-Renderer hinzufügen und Folgendes tun:

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