Рендеринг HTML в изображение

167

Есть ли способ визуализации HTML для изображения, как PNG? Я знаю, что это возможно с холстом, но я хотел бы сделать стандартный элемент HTML, например, div.

Мартин Делиль
источник
Чтобы создать какую-то помощь пользователю, например. К сожалению, это невозможно (по соображениям безопасности?). Вы должны попросить пользователя нажать PrintScreen, чтобы что-то сделать.
MaxArt
1
Возможный дубликат Как конвертировать HTML сайта в изображение?
Эрнест Фридман-Хилл
Я хочу использовать HTML / CSS для разработки логотипа.
Мартин Делиль
4
@ ErnestFriedman-Hill не дубликат: вопрос, который вы упомянули, относится к java.
Мартин Делиль
Вот пошаговое руководство для преобразования HTML в формат IMAGE png или jpeg codepedia.info/2016/02/…
singh

Ответы:

42

Я знаю, что это довольно старый вопрос, на который уже есть много ответов, но я все еще часами пытался сделать то, что хотел:

  • учитывая HTML-файл, сгенерируйте (png) изображение с прозрачным фоном из командной строки

Используя Chrome без головы (версия 74.0.3729.157 на этот ответ), это действительно легко:

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

Объяснение команды:

  • вы запускаете Chrome из командной строки (здесь показано для Mac, но предполагается, что аналогично для Windows или Linux)
  • --headless запускает Chrome, не открывая его, и завершает работу после завершения команды
  • --screenshotсделает снимок экрана (обратите внимание, что он генерирует файл с именем screenshot.pngв папке, в которой выполняется команда)
  • --window-sizeразрешить захватывать только часть экрана (формат есть --window-size=width,height)
  • --default-background-color=0магический трюк, который говорит Chrome использовать прозрачный фон, а не белый цвет по умолчанию
  • наконец, вы предоставляете html-файл (как локальный или удаленный URL-адрес ...)
янь
источник
1
Очень хорошо! Это работает с SVG также! Я переключился на ваше решение в конце концов! ;-)
Мартин Делиль
1
--screenshot='absolute\path\of\screenshot.png'работал для меня
Палаш
Командная линия работала. если я хочу запустить это программно из экспресс-JS, как запустить его?
Рецепты Squapl
К вашему сведению, это также работает с хромом, даже если это не упоминается в справочной странице.
Робин Лашоф-Регас
SVG для меня не SVG ... это просто PNG с расширением SVG ... так что будьте осторожны.
Кристополус
97

Да. HTML2Canvas существует для рендеринга HTML <canvas>(который затем можно использовать в качестве изображения).

ПРИМЕЧАНИЕ. Существует известная проблема, которая не будет работать с SVG.

AKX
источник
Это кажется интересным, но мне не удалось заставить его работать, поэтому я выбрал решение Джона Фишера. Спасибо за информацию, я буду смотреть этот проект в будущем!
Мартин Делиль
@MartinDelille: здесь статья об использовании HTML2Canvas для создания IMAGE, вы также можете скачать ее на стороне клиента codepedia.info/2016/02/…
singh
3
dom-to-image (см. ответ Цайена) делает намного лучшую работу для создания точной картинки.
JBE
3
Это решение очень медленное
hamboy75
3
другая проблема, если элемент скрыт или находится за другим элементом, это не сработает
Yousef
91

Могу я порекомендовать библиотеку dom-to-image , которая была написана исключительно для решения этой проблемы (я сопровождающий).
Вот как вы используете это (еще немного здесь ):

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
источник
2
Это круто! Любой способ удвоить выходной размер?
cronoklee
Спасибо! Но что именно вы подразумеваете под «удвоением размера»?
Цайен
2
Первое, что приходит мне в голову - попробуйте визуализировать ваше изображение в SVG (используя domtoimage.toSvg), затем визуализируйте его самостоятельно на холсте и попытайтесь как-то поиграть с разрешением этого холста. Вероятно, возможно реализовать такую ​​функцию, как какую-то опцию рендеринга в самой библиотеке, чтобы вы могли передавать размеры изображения в пикселях. Если вам это нужно, я буду признателен, если вы создадите проблему на github.
Цаен
8
Это НАМНОГО лучше, чем html2canvas. Спасибо.
c.hughes
1
@Subho это строка, содержащая URL с данными в кодировке base64
tsayen
67

Вариантов много, и у всех есть свои плюсы и минусы.

Вариант 1. Используйте одну из множества доступных библиотек.

Pros

  • Конверсия в большинстве случаев довольно быстрая

Cons

  • Плохой рендеринг
  • Не выполняет JavaScript
  • Отсутствует поддержка последних веб-функций (FlexBox, Расширенные селекторы, Веб-шрифты, Размеры ящиков, Медиа-запросы, ...)
  • Иногда не так просто установить
  • Сложно масштабировать

Вариант 2: используйте PhantomJs и, возможно, библиотеку-обертку

Pros

  • Выполнить Javascript
  • Довольно быстро

Cons

  • Плохой рендеринг
  • Отсутствует поддержка последних веб-функций (FlexBox, Расширенные селекторы, Веб-шрифты, Размеры ящиков, Медиа-запросы, ...)
  • Сложно масштабировать
  • Не так легко заставить его работать, если есть загружаемые изображения ...

Вариант 3: используйте Chrome Headless и, возможно, библиотеку-обертку

Pros

  • Выполнить Javascript
  • Почти идеальный рендеринг

Cons

  • Не так просто получить желаемый результат в отношении:
    • время загрузки страницы
    • размеры окна просмотра
  • Сложно масштабировать
  • Довольно медленно и даже медленнее, если HTML содержит внешние ссылки

Вариант 4: использовать API

Pros

  • Выполнить Javascript
  • Почти идеальный рендеринг
  • Быстро при правильном использовании параметров кэширования
  • Шкала обрабатывается API
  • Точное время, окно просмотра, ...
  • Большую часть времени они предлагают бесплатный план

Cons

  • Не бесплатно, если вы планируете использовать их много

Раскрытие информации: я основатель ApiFlash. Я сделал все возможное, чтобы дать честный и полезный ответ.

Тимоте Жаннин
источник
2
Спасибо за подробный ответ с множеством возможных решений
bszom
wkhtmltoimage / pdf поддерживает рендеринг JavaScript. Вы можете установить задержку javascript или позволить wkhtml проверять конкретный window.status (который можно установить с помощью javascript, когда вы знаете, что ваши js-файлы выполнены)
Даниэль З.
PhantomJS поддерживает такие динамические вещи, как Google Maps. Это действительно полноценный веб-браузер, просто без прикрепленного дисплея. Однако вам придется немного подождать, пока Google Map загрузит плитки с географией (я жду 500 мс и позволяю людям изменить задержку - если этого недостаточно, снова работает без увеличения времени, потому что эти плитки кэшируются).
Ладислав Зима
50

Все ответы здесь используют сторонние библиотеки, в то время как рендеринг HTML в изображение может быть относительно простым в чистом Javascript. Там будет даже статья об этом в разделе холст на MDN.

Хитрость заключается в следующем:

  • создайте SVG с узлом foreignObject, содержащим ваш XHTML
  • установить источник изображения на URL-адрес этого SVG
  • drawImage на холст
  • установить данные холста на целевой 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()
}

Некоторые вещи на заметку

  • HTML-код внутри SVG должен быть XHTML
  • По соображениям безопасности SVG в качестве URL-адреса данных изображения выступает в качестве изолированной области CSS для HTML, поскольку внешние источники не могут быть загружены. Так, например, шрифт Google должен быть встроен с помощью такого инструмента, как этот .
  • Даже если HTML-код внутри SVG превышает размер изображения, он будет правильно рисовать на холсте. Но фактическая высота не может быть измерена от этого изображения. Решение с фиксированной высотой будет работать очень хорошо, но динамическая высота потребует немного больше работы. Лучше всего рендерить данные SVG в iframe (для изолированной области CSS) и использовать полученный размер для холста.
Sjeiti
источник
Ницца! Удаление зависимости от внешних библиотек - действительно хороший путь. Я изменил свой принятый ответ :-)
Мартин Делиль
Этой статьи больше нет.
MSC
2
Отличный ответ, но комментируя современное состояние HTML и веб-API, которые, как обычно, выглядят как что-то, что вытянуло кого-то позади - технически все функциональные возможности присутствуют, но раскрыты за массивом (без каламбура) странных путей кода, которые напоминают ничего такого, чего бы вы не ожидали от хорошо спроектированных API. Проще говоря: браузер, скорее всего, тривиально разрешает Documentрендеринг а в растр (например, а Canvas), но поскольку никто не удосужился его стандартизировать, мы вынуждены прибегать к безумию, как показано выше (не оскорбляя автора этого код).
атп
1
Этот ответ stackoverflow.com/questions/12637395/…, кажется, указывает, что вы можете пойти довольно далеко. Mozilla и Chrome имеют неограниченное количество данных, и даже текущий IE позволяет использовать 4 ГБ, что сводится к примерно 3 ГБ изображениям (благодаря base64). Но это было бы хорошо, чтобы проверить.
Сеити
3
- некоторые быстрые и грязные тесты позже - jsfiddle.net/Sjeiti/pcwudrmc/75965 Chrome, Edge и Firefox увеличивают ширину не менее чем на 10 000 пикселей, что (в данном случае) - это количество символов около 10 000 000 и размер файла png 8 МБ. Не тестировался строго, но этого достаточно для большинства случаев использования.
Сеити
13

Вы можете использовать PhantomJS , который представляет собой безголовый веб- набор (движок рендеринга в Safari и (до недавнего времени) Chrome) драйвер. Вы можете узнать, как сделать снимок экрана здесь . Надеюсь, это поможет!

TheContrarian
источник
Эта техника хорошо работает. Однако с момента вашего комментария прошло 2 года. Вы сталкивались с чем-то, что работает быстрее, чем PhantomJS? Генерация изображения в Phantom обычно занимает 2-5 секунд.
Брайан Вебстер
Безголовый Chrome теперь возможен. Я не знаю, быстрее ли это, но если вам нужны точные скриншоты, это хороший способ.
Джош Мааг
11

Вы можете использовать инструмент HTML to PDF, например wkhtmltopdf. И тогда вы можете использовать PDF для изображения инструмента, как imagemagick. По общему признанию это - сторона сервера и очень запутанный процесс ...

PinnyM
источник
12
Или вы можете просто запустить изображение брата wkhtmltopdf, wkhtmltoimage.
Роман Старков
1
Эта библиотека для Node.js. Как насчет простого интерфейса?
Влад
9

Единственной библиотекой, которую я получил для работы с Chrome, Firefox и MS Edge, была растеризация HTML . Он выводит лучшее качество, чем HTML2Canvas, и поддерживается в отличие от HTML2Canvas.

Получение элемента и загрузка в формате 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
источник
2
но это не работает с IE. rasterizeHTML Ограничения
Бобан Стояновский
@ Vabanagas есть ли какой-нибудь один файл JavaScript для этого?
Махди Халили
Это изменение мне очень помогло: rasterizeHTML.drawHTML(node.innerHTML, canvas) обратите внимание, что я вызываю innerHTML на узле
Sep GH
5

Используйте html2canvas, просто включите плагин и метод вызова, чтобы конвертировать HTML в Canvas, а затем скачать как изображение 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();
        });

Источник : http://www.freakyjolly.com/convert-html-document-into-image-jpg-png-from-canvas/

Кодовый шпион
источник
4

Я не ожидаю, что это будет лучший ответ, но он показался достаточно интересным для публикации.

Напишите приложение, которое открывает ваш любимый браузер для нужного HTML-документа, правильно масштабирует окно и делает снимок экрана. Затем удалите границы изображения.

Джон Фишер
источник
4

Используйте этот код, он обязательно будет работать:

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

Только не забудьте включить файл Html2CanvasJS в вашу программу. https://html2canvas.hertzen.com/dist/html2canvas.js

Мохит Кулькарни
источник
2

Вы не можете сделать это на 100% точно только с помощью JavaScript.

Там есть инструмент Qt Webkit и версия на Python . Если вы хотите сделать это самостоятельно, у меня был успех с Какао:

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

Надеюсь это поможет!

Мэтт Мелтон
источник
У вас есть быстрая версия выше? или, если возможно, не могли бы вы переписать это?
SSH
Не могли бы вы поделиться кодом, который вы используете для загрузки отображаемого веб-представления? Это выглядит многообещающим подходом для моего рендера. Спасибо!
Дейв
1

Установить фантоми

$ npm install phantomjs

Создайте файл github.js со следующим кодом

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

Передайте файл в качестве аргумента phantomjs

$ phantomjs github.js
Саурабх Саксена
источник
-3

Вы можете добавить ссылку HtmlRenderer в ваш проект и сделать следующее,

string htmlCode ="<p>This is a sample html.</p>";
Image image = HtmlRender.RenderToImage(htmlCode ,new Size(500,300));
kuttalam
источник