Генерация PDF из HTML в div с использованием Javascript

232

У меня есть следующий HTML-код:

<!DOCTYPE html>
<html>
    <body>
        <p>don't print this to pdf</p>
        <div id="pdf">
            <p><font size="3" color="red">print this to pdf</font></p>
        </div>
    </body>
</html>

Все, что я хочу сделать, это напечатать в pdf все, что найдено в div с идентификатором «pdf». Это должно быть сделано с использованием JavaScript. Документ «pdf» должен автоматически загружаться с именем файла «foobar.pdf»

Я использовал jspdf для этого, но единственная функция, которую он имеет, это «текст», который принимает только строковые значения. Я хочу отправить HTML в jspdf, а не в текст.

Джон Кроуфорд
источник
1
Как уже упоминалось выше, я не хочу использовать функцию «текст». Я хочу дать ему HTML. Ваша ссылка относится только к простому тексту, а не к HTML
Джон Кроуфорд,
2
jsPDF делает имеет fromHTMLфункцию; см. пример «HTML Renderer» по адресу: mrrio.github.io/jsPDF
mg1075
Попробуйте руководство jsPDF здесь freakyjolly.com/create-multipage-html-pdf-jspdf-html2canvas
Code Spy

Ответы:

228

jsPDF умеет использовать плагины. Для того, чтобы он мог печатать HTML, вы должны включить некоторые плагины и, следовательно, должны сделать следующее:

  1. Перейдите на https://github.com/MrRio/jsPDF и загрузите последнюю версию.
  2. Включите следующие сценарии в ваш проект:
    • jspdf.js
    • jspdf.plugin.from_html.js
    • jspdf.plugin.split_text_to_size.js
    • jspdf.plugin.standard_fonts_metrics.js

Если вы хотите игнорировать определенные элементы, вы должны пометить их идентификатором, который затем можно игнорировать в специальном обработчике элемента jsPDF. Поэтому ваш HTML должен выглядеть так:

<!DOCTYPE html>
<html>
  <body>
    <p id="ignorePDF">don't print this to pdf</p>
    <div>
      <p><font size="3" color="red">print this to pdf</font></p>
    </div>
  </body>
</html>

Затем вы используете следующий код JavaScript, чтобы открыть созданный PDF-файл во всплывающем окне:

var doc = new jsPDF();          
var elementHandler = {
  '#ignorePDF': function (element, renderer) {
    return true;
  }
};
var source = window.document.getElementsByTagName("body")[0];
doc.fromHTML(
    source,
    15,
    15,
    {
      'width': 180,'elementHandlers': elementHandler
    });

doc.output("dataurlnewwindow");

Для меня это создало красивый и аккуратный PDF, который включал только строку «напечатать это в pdf».

Обратите внимание, что обработчики специальных элементов имеют дело только с идентификаторами в текущей версии, что также указано в выпуске GitHub . Здесь утверждается:

Поскольку сопоставление выполняется для каждого элемента в дереве узлов, я хотел сделать это как можно быстрее. В этом случае это означало «Совпадают только идентификаторы элементов». Идентификаторы элементов по-прежнему выполняются в стиле jQuery «#id», но это не означает, что поддерживаются все селекторы jQuery.

Поэтому замена «#ignorePDF» на селекторы классов, такие как «.ignorePDF», не работала для меня. Вместо этого вам придется добавить один и тот же обработчик для каждого элемента, который вы хотите игнорировать, например:

var elementHandler = {
  '#ignoreElement': function (element, renderer) {
    return true;
  },
  '#anotherIdToBeIgnored': function (element, renderer) {
    return true;
  }
};

В примерах также указано, что можно выбрать теги типа «a» или «li». Это может быть немного не ограничивающим для большинства случаев использования:

Мы поддерживаем специальные обработчики элементов. Зарегистрируйте их с помощью селектора идентификаторов в стиле jQuery для идентификатора или имени узла. ("#iAmID", "div", "span" и т. д.) В настоящее время нет поддержки для любого другого типа селекторов (класса, составного).

Очень важно добавить, что вы теряете всю информацию о стиле (CSS). К счастью, jsPDF умеет красиво форматировать h1, h2, h3 и т. Д., Что было достаточно для моих целей. Кроме того, он будет печатать текст только в текстовых узлах, что означает, что он не будет печатать значения текстовых полей и т.п. Пример:

<body>
  <ul>
    <!-- This is printed as the element contains a textnode -->        
    <li>Print me!</li>
  </ul>
  <div>
    <!-- This is not printed because jsPDF doesn't deal with the value attribute -->
    <input type="textarea" value="Please print me, too!">
  </div>
</body>
snrlx
источник
3
Я собираюсь угадать, что обработчик элемента может быть классом? Это было бы намного более семантически в соответствии со стандартами HTML5. Идентификатор не только имеет слишком большой вес в CSS, он также должен быть уникальным.
Императив
Нет, насколько я знаю, классы на данный момент не являются допустимыми селекторами, как указано в их примерах: «мы поддерживаем специальные обработчики элементов. Зарегистрируйте их с помощью селектора идентификаторов в стиле jQuery для идентификатора или имени узла. (« #IAmID », "div", "span" и т. д.) В настоящее время не поддерживается никакой другой тип селекторов (класса, составного). " Тем не менее, можно использовать имена тегов, если они соответствуют вашему варианту использования и не скрывают другие важные элементы на вашей странице.
snrlx
На своей странице parall.ax/products/jspdf они заявляют, что они поддерживают IE6 + через прокладку. Хотя сам не пробовал.
snrlx
2
@snrlx Я получаю пустой PDF, и эта ошибка: renderer.pdf.sHashCode не является функцией
Лиза Соломон
5
«Если вы хотите игнорировать определенные элементы, вы должны пометить их идентификатором» - замечательная библиотека, разрушенная этим перевернутым требованием. ОП хотел напечатать один <div>, который может быть одним из сотен - поэтому он должен отметить все нежелательные элементы DOM ??
Мауг говорит восстановить Монику
53

Это простое решение. Это работает для меня. Вы можете использовать концепцию печати javascript и просто сохранить ее в формате PDF.

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <script type="text/javascript">
        $("#btnPrint").live("click", function () {
            var divContents = $("#dvContainer").html();
            var printWindow = window.open('', '', 'height=400,width=800');
            printWindow.document.write('<html><head><title>DIV Contents</title>');
            printWindow.document.write('</head><body >');
            printWindow.document.write(divContents);
            printWindow.document.write('</body></html>');
            printWindow.document.close();
            printWindow.print();
        });
    </script>
</head>
<body>
    <form id="form1">
    <div id="dvContainer">
        This content needs to be printed.
    </div>
    <input type="button" value="Print Div Contents" id="btnPrint" />
    </form>
</body>
</html>
вайбхав кулкарни
источник
27
«и просто сохранить это как PDF» - я пропустил эту часть. Как ты это делаешь?
Mawg говорит восстановить Monica
9
Это сработало для меня, для решения проблемы стилевого оформления CSS я создал еще один CSS-файл с именем printPDF.css и добавил, используя тег ссылки, как показано в примере выше: printWindow.document.write ('<html> <head> <title> DIV Contents </ title> '); printWindow.document.write ('<link rel = "stylesheet" href = "../ css / printPDF.css" />'); printWindow.document.write ('</ head> <body>');
Prime_Coder
2
Некоторые комментарии: 1) Вам не нужна специальная таблица стилей для печати. В вашей текущей таблице стилей сделайте что-то вроде: @media print {.pageBreak {page-break-before: always; } .labelPdf {font-weight: bold; размер шрифта: 20 пикселей; } .noPrintPdf {display: none; }} и используйте эти классы как свои потребности. 2) .live ("click", ...) мне не подходит, поэтому вместо него я использую .on ("click", ...).
Дэвидсон Лима
1
@DavidsonLima этот код создает новое окно, которое не будет видеть старое окно css. Вот почему он "игнорирует" CSS, он не игнорирует, просто в новом окне он не загружается. Просто загрузите это в голову, и это будет рендеринг.
Лукас Лиезис
1
На случай, если кто-то попытается сделать это с Angular, пожалуйста, поместите ваш CSS-файл в папку assets.
rgantla
16

Вы можете использовать autoPrint () и установить для вывода значение dataurlnewwindow следующим образом:

function printPDF() {
    var printDoc = new jsPDF();
    printDoc.fromHTML($('#pdf').get(0), 10, 10, {'width': 180});
    printDoc.autoPrint();
    printDoc.output("dataurlnewwindow"); // this opens a new popup,  after this the PDF opens the print window view but there are browser inconsistencies with how this is handled
}
Marco
источник
1
Мне любопытно, это когда-либо работало для кого-то кроме OP? Читая код, я понимаю, что он будет работать только с элементами, имеющими идентификатор. Возможно, это немного сложнее, во всяком случае, я понятия не имею, как заставить это работать.
Майкл
14
Из того, что я заметил, как ни странно, fromHTML работает только в том случае, если элемент, отправляемый в качестве параметра, не содержит HTML: поддерживается только простой текст. Kinda убивает цель всего этого imo.
Майкл
Работал отлично для меня. Элемент, который вы хотите передать, не обязательно должен иметь идентификатор. Именно таким образом репликант нашел узел, который он хотел передать. Кроме того, это решение также работает без функции printDoc.autoPrint (). Если вы хотите оставить эту конкретную строку в коде, то вам необходимо включить плагин autoPrint.
snrlx
13

если вам нужно загрузить PDF с определенной страницы, просто добавьте кнопку, подобную этой

<h4 onclick="window.print();"> Print </h4>

используйте window.print (), чтобы напечатать всю страницу, а не просто div

Ваэль Махмуд
источник
2
Просто добавление к нему, если вы хотите создать загружаемый PDF-файл iframe, а затем использовать консоль разработчика: document.querySelector("#myIframe").contentWindow.print()
ioCron
11

Как уже упоминалось, вы должны использовать jsPDF и html2canvas . Я также нашел функцию внутри вопросов jsPDF, которая автоматически разбивает ваш PDF на несколько страниц ( источники )

function makePDF() {

    var quotes = document.getElementById('container-fluid');

    html2canvas(quotes, {
        onrendered: function(canvas) {

        //! MAKE YOUR PDF
        var pdf = new jsPDF('p', 'pt', 'letter');

        for (var i = 0; i <= quotes.clientHeight/980; i++) {
            //! This is all just html2canvas stuff
            var srcImg  = canvas;
            var sX      = 0;
            var sY      = 980*i; // start 980 pixels down for every new page
            var sWidth  = 900;
            var sHeight = 980;
            var dX      = 0;
            var dY      = 0;
            var dWidth  = 900;
            var dHeight = 980;

            window.onePageCanvas = document.createElement("canvas");
            onePageCanvas.setAttribute('width', 900);
            onePageCanvas.setAttribute('height', 980);
            var ctx = onePageCanvas.getContext('2d');
            // details on this usage of this function: 
            // https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Using_images#Slicing
            ctx.drawImage(srcImg,sX,sY,sWidth,sHeight,dX,dY,dWidth,dHeight);

            // document.body.appendChild(canvas);
            var canvasDataURL = onePageCanvas.toDataURL("image/png", 1.0);

            var width         = onePageCanvas.width;
            var height        = onePageCanvas.clientHeight;

            //! If we're on anything other than the first page,
            // add another page
            if (i > 0) {
                pdf.addPage(612, 791); //8.5" x 11" in pts (in*72)
            }
            //! now we declare that we're working on that page
            pdf.setPage(i+1);
            //! now we add content to that page!
            pdf.addImage(canvasDataURL, 'PNG', 20, 40, (width*.62), (height*.62));

        }
        //! after the for loop is finished running, we save the pdf.
        pdf.save('test.pdf');
    }
  });
}
BaptWaels
источник
3
он не конвертирует изображения
Хобоски
1
спасибо за ответ, не могли бы вы дать намек о том, как перевести его на формат страницы А4?
Иоганн Матевосян
3
Это не очень хороший векторный PDF, он создает много растровых изображений с холстом и складывает их как изображения. В результате имеет много недостатков - большой, низкое качество, не может копировать и вставлять текст из PDF, и т.д ...
Roman Zenka
6

Одним из способов является использование функции window.print (). Который не требует никакой библиотеки

Pros

1. Никакой внешней библиотеки не требуется.

2. Мы можем печатать только отдельные части тела.

3. Никаких конфликтов и проблем.

4.Core html / js функциональность

--- Просто добавьте ниже код

CSS для

@media print {
        body * {
            visibility: hidden; // part to hide at the time of print
            -webkit-print-color-adjust: exact !important; // not necessary use         
               if colors not visible
        }

        #printBtn {
            visibility: hidden !important; // To hide 
        }

        #page-wrapper * {
            visibility: visible; // Print only required part
            text-align: left;
            -webkit-print-color-adjust: exact !important;
        }
    }

JS-код - вызов функции bewlow при нажатии btn

$scope.printWindow = function () {
  window.print()
}

Примечание: используйте! Важное в каждом объекте CSS

Пример -

.legend  {
  background: #9DD2E2 !important;
}
Рохит Парте
источник
Есть проблемы с функцией печати браузеров. Пользователи обычно имеют параметры по умолчанию, выбранные для представления «Печать» (поля, размер страницы и т. Д.). Поэтому очень сложно создать необходимый PDF-файл с требуемым стилем без обучения пользователя, что намного сложнее и
Рахмат Али
4

я использую jspdf и html2canvas для рендеринга CSS и экспортирую содержимое определенного div, так как это мой код

$(document).ready(function () {
    let btn=$('#c-oreder-preview');
    btn.text('download');
    btn.on('click',()=> {

        $('#c-invoice').modal('show');
        setTimeout(function () {
            html2canvas(document.querySelector("#c-print")).then(canvas => {
                //$("#previewBeforeDownload").html(canvas);
                var imgData = canvas.toDataURL("image/jpeg",1);
                var pdf = new jsPDF("p", "mm", "a4");
                var pageWidth = pdf.internal.pageSize.getWidth();
                var pageHeight = pdf.internal.pageSize.getHeight();
                var imageWidth = canvas.width;
                var imageHeight = canvas.height;

                var ratio = imageWidth/imageHeight >= pageWidth/pageHeight ? pageWidth/imageWidth : pageHeight/imageHeight;
                //pdf = new jsPDF(this.state.orientation, undefined, format);
                pdf.addImage(imgData, 'JPEG', 0, 0, imageWidth * ratio, imageHeight * ratio);
                pdf.save("invoice.pdf");
                //$("#previewBeforeDownload").hide();
                $('#c-invoice').modal('hide');
            });
        },500);

        });
});
Газалех Джавахери
источник
это работает, но оно конвертирует контент в изображение
Самад
Кроме того, как установить разрыв страницы, чтобы содержимое / изображение печаталось на новой странице, если оно не помещается на текущей странице?
SHEKHAR SHETE
4
  • Нет зависимостей, чистый JS
  • Чтобы добавить CSS или изображения - не используйте относительные URL, используйте полные URL http://...domain.../path.cssили около того. Он создает отдельный HTML-документ и не имеет контекста главного.
  • Вы также можете вставлять изображения как base64

Это служило мне в течение многих лет:

export default function printDiv({divId, title}) {
  let mywindow = window.open('', 'PRINT', 'height=650,width=900,top=100,left=150');

  mywindow.document.write(`<html><head><title>${title}</title>`);
  mywindow.document.write('</head><body >');
  mywindow.document.write(document.getElementById(divId).innerHTML);
  mywindow.document.write('</body></html>');

  mywindow.document.close(); // necessary for IE >= 10
  mywindow.focus(); // necessary for IE >= 10*/

  mywindow.print();
  mywindow.close();

  return true;
}
Лукас Лиесис
источник
1
Проблема в том, что PDF не будет иметь никаких эффектов CSS.
Шадаб Файз
@ShadabFaiz Будет, но может не совпадать с главным окном. Вы все еще можете добавить пользовательские CSS к этому HTML тоже.
Лукас Лиезис
Да. Я согласен, вы можете добавить CSS в окне, но как распечатать результат в файле PDF?
Мирко Чианфарани
1
Не рендерит изображения, хотя.
Ян Пи
1
Мне это нравится! Некоторые хитрости здесь и там, и это выглядит хорошо. И одна маленькая вещь, не удаляющая лишний интервал в <body >этом, нуждается в этом: P
Фил Роггенбак
2

Если вы хотите экспортировать таблицу, вы можете взглянуть на этот пример экспорта, предоставляемый виджетом Shield UI Grid.

Это делается путем расширения конфигурации следующим образом:

...
exportOptions: {
    proxy: "/filesaver/save",
    pdf: {
        fileName: "shieldui-export",
        author: "John Smith",
        dataSource: {
            data: gridData
        },
        readDataSource: true,
        header: {
            cells: [
                { field: "id", title: "ID", width: 50 },
                { field: "name", title: "Person Name", width: 100 },
                { field: "company", title: "Company Name", width: 100 },
                { field: "email", title: "Email Address" }
            ]
        }
    }
}
...
Владимир Георгиев
источник
Функциональность «Экспорт в PDF» привела к пустому документу PDF.
Ричард Налезински
Возможно, неправильная конфигурация на вашем конце ... Если хотите, опубликуйте вопрос с тегом shieldui
Владимир Георгиев
2

Используйте pdfMake.js и этот Gist .

(Я нашел здесь Gist вместе со ссылкой на пакет html-to-pdfmake , который я пока не использовал.)

После npm install pdfmakeи сохранения Gist htmlToPdf.jsя использую это так:

const pdfMakeX = require('pdfmake/build/pdfmake.js');
const pdfFontsX = require('pdfmake-unicode/dist/pdfmake-unicode.js');
pdfMakeX.vfs = pdfFontsX.pdfMake.vfs;
import * as pdfMake from 'pdfmake/build/pdfmake';
import htmlToPdf from './htmlToPdf.js';

var docDef = htmlToPdf(`<b>Sample</b>`);
pdfMake.createPdf({content:docDef}).download('sample.pdf');

Примечания:

  • Мой случай использования , чтобы создать соответствующий HTML из уценки документа (с уценкой-It ) и затем генерации PDF, и загрузить его бинарное содержимое (которое можно получить с pdfMake«s getBuffer()функции), все из браузера. Сгенерированный PDF оказывается лучше для этого вида HTML, чем с другими решениями, которые я пробовал.
  • Я не удовлетворен результатами, которые я получил из jsPDF.fromHTML()предложенного в принятом ответе, поскольку это решение легко спутать со специальными символами в моем HTML, которые, очевидно, интерпретируются как своего рода разметка и полностью портят полученный PDF.
  • Использование решений на основе холста (например, устаревшей jsPDF.from_html()функции, которую не следует путать с функцией из принятого ответа) не подходит для меня, поскольку я хочу, чтобы текст в сгенерированном PDF-файле можно было вставлять, в то время как решения на основе холста генерируют растровые PDF-файлы.
  • Прямая разметка в pdf-конвертеры, такие как md-to-pdf, относится только к серверу и не будет работать для меня.
  • Использование функции печати браузера не будет работать для меня, так как я не хочу отображать сгенерированный PDF, но загружать его двоичный контент.
ilmiacs
источник
Если я правильно прочитал код, это не поддерживает стили границ CSS (например, для таблиц), правильно?
Ниндзягецко
1

Мне удалось заставить jsPDF печатать динамически созданные таблицы из div.

$(document).ready(function() {

        $("#pdfDiv").click(function() {

    var pdf = new jsPDF('p','pt','letter');
    var specialElementHandlers = {
    '#rentalListCan': function (element, renderer) {
        return true;
        }
    };

    pdf.addHTML($('#rentalListCan').first(), function() {
        pdf.save("caravan.pdf");
    });
    });
});

Отлично работает с Chrome и Firefox ... форматирование все взорвано в IE.

Я также включил эти:

<script src="js/jspdf.js"></script>
    <script src="js/jspdf.plugin.from_html.js"></script>
    <script src="js/jspdf.plugin.addhtml.js"></script>
    <script src="//mrrio.github.io/jsPDF/dist/jspdf.debug.js"></script>
    <script src="http://html2canvas.hertzen.com/build/html2canvas.js"></script>
    <script type="text/javascript" src="./libs/FileSaver.js/FileSaver.js"></script>
    <script type="text/javascript" src="./libs/Blob.js/Blob.js"></script>
    <script type="text/javascript" src="./libs/deflate.js"></script>
    <script type="text/javascript" src="./libs/adler32cs.js/adler32cs.js"></script>

    <script type="text/javascript" src="js/jspdf.plugin.addimage.js"></script>
    <script type="text/javascript" src="js/jspdf.plugin.sillysvgrenderer.js"></script>
    <script type="text/javascript" src="js/jspdf.plugin.split_text_to_size.js"></script>
    <script type="text/javascript" src="js/jspdf.plugin.standard_fonts_metrics.js"></script>
Wasskinny
источник
2
Можете ли
но ваш код <script src = " html2canvas.hertzen.com/build/html2canvas.js" > </ > что означает, что ему нужен интернет?
Erum
1
@Erum вы можете скачать файл.
Эдуардо Куомо
0

Для захвата div в PDF вы можете использовать решение https://grabz.it . У него есть JavaScript API, который прост и гибок и позволит вам захватывать содержимое одного HTML-элемента, такого как div или span

Для того , чтобы реализовать его вам нужно будет сначала получить ключ приложения и секрет и скачать в (бесплатно) SDK.

А теперь пример.

Допустим, у вас есть HTML:

<div id="features">
    <h4>Acme Camera</h4>
    <label>Price</label>$399<br />
    <label>Rating</label>4.5 out of 5
</div>
<p>Cras ut velit sed purus porttitor aliquam. Nulla tristique magna ac libero tempor, ac vestibulum felisvulput ate. Nam ut velit eget
risus porttitor tristique at ac diam. Sed nisi risus, rutrum a metus suscipit, euismod tristique nulla. Etiam venenatis rutrum risus at
blandit. In hac habitasse platea dictumst. Suspendisse potenti. Phasellus eget vehicula felis.</p>

Чтобы захватить то, что находится под идентификатором функций, вам нужно:

//add the sdk
<script type="text/javascript" src="grabzit.min.js"></script>
<script type="text/javascript">
//login with your key and secret. 
GrabzIt("KEY", "SECRET").ConvertURL("http://www.example.com/my-page.html",
{"target": "#features", "format": "pdf"}).Create();
</script>

Пожалуйста, обратите внимание target: #feature. #featureэто ваш селектор CSS, как в предыдущем примере. Теперь, когда страница загружена, будет создан снимок экрана в том же месте, что и тег script, который будет содержать все содержимое div возможностей и ничего больше.

Другие конфигурации и настройки, которые вы можете сделать с механизмом div-screenshot, пожалуйста, проверьте их здесь

Джонни
источник