Створюйте pdf з HTML у діві за допомогою 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 те, що знаходиться у діві з ідентифікатором "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
Код шпигуна

Відповіді:


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 у PopUp:

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>

3
Я буду здогадуватися, що обробником елементів може бути клас? Це було б набагато більш семантично відповідно до стандартів HTML5. Ідентифікатор не тільки має занадто велику вагу специфічності в CSS, він також повинен бути унікальним.
імператив

Ні, наскільки я знаю, класи на даний момент не є дійсними селекторами, як зазначено у їхніх прикладах: "ми підтримуємо спеціальні обробники елементів. Зареєструйте їх за допомогою селектора ідентифікаторів у стилі jQuery як для ідентифікатора, так і для імені вузла. (" #IAmID ", "div", "span" тощо) Наразі немає підтримки для будь-якого іншого вибору селекторів (класу, сполуки). " Однак імена тегів можна використовувати, якщо це відповідає вашому випадку використання та не приховує інших важливих елементів на вашій сторінці.
snrlx

На своїй сторінці parall.ax/products/jspdf вони заявляють, що вони підтримують IE6 + через shim. Я сам не пробував цього, хоча.
snrlx

2
@snrlx Я отримую pdf pdf, і ця помилка: renderer.pdf.sHashCode не є функцією
Lisa Solomon

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" - я пропустив цю частину. Як ти це робиш?
Мауг каже, що повернути Моніку

9
Це працювало для мене, для вирішення проблеми стилю CSS я створив ще один файл css під назвою printPDF.css і додав, використовуючи тег посилання так само, як у наведеному вище прикладі: printWindow.document.write ('<html> <head> <title> Зміст DIV </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; розмір шрифту: 20px; } .noPrintPdf {показ: немає; }} і використовуйте ці класи як свої потреби. 2) .live ("натиснути", ...) не працює для мене, тому я використовую замість .on ("клацання", ...).
Девідсон Ліма

1
@DavidsonLima цей код створює нове вікно, яке не побачить старий css вікна. Ось чому це "ігнорування" css, це не ігнорування, просто в новому вікні воно не завантажується. Просто завантажте його в голову, і це буде візуалізація.
Лукас Лейсіс

1
На всякий випадок, якщо хтось намагається зробити це за допомогою Angular, будь ласка, помістіть свій файл CSS у папку активів.
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
}

1
Мені цікаво, чи це коли-небудь працювало для когось, крім ОП? Зчитавши код, я, здається, зрозумів, що він буде працювати лише з елементами, які мають ідентифікатор. Це, мабуть, трохи складніше, ніж це, я все одно не маю уявлення, як зробити цю роботу.
Майкл

14
З того, що я зауважив, дуже іронічно, що зHTML працює лише у тому випадку, якщо елемент, надісланий як параметр, не містить HTML: підтримується лише звичайний текст. Ніби вбиває мету цілої справи.
Майкл

Для мене прекрасно працювали. Елемент, який ви хочете передати, не обов'язково повинен мати ідентифікатор. Ось так саме реплікант знайшов вузол, через який хотів перейти. Крім того, це рішення також працює без 'printDoc.autoPrint ()'. Якщо ви хочете залишити цей специфічний рядок у коді, вам потрібно включити модуль autoPrint-Plugin.
snrlx

13

якщо вам потрібно завантажити файл PDF на певній сторінці, просто додайте кнопку, як це

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

використовуйте window.print (), щоб надрукувати всю сторінку, а не діл


2
Просто просте доповнення до нього, якщо ви хочете створити завантажуваний PDF-файл із document.querySelector("#myIframe").contentWindow.print()
рамки

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

3
він не конвертує зображення
Probosckie

1
спасибі за відповідь, чи могли б ви підказати, як розмістити його у форматі сторінки A4?
johannesMatevosyan

3
Це насправді не робить хороший векторний PDF-файл, він створює багато растрових зображень із полотном та зберігає їх як зображення. Результат має багато недоліків - великий, низька якість, не вдається скопіювати та вставити з PDF тощо.
Roman Zenka

6

Один із способів - використовувати функцію window.print (). Яка не потребує жодної бібліотеки

Плюси

1.Не вимагають зовнішньої бібліотеки.

2.Ми також можемо друкувати лише вибрані частини тіла.

3. Немає конфліктів css та js.

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 - функція нижче дзвінка при натисканні btn

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

Примітка: Використовуйте! Важливо у кожному об'єкті css

Приклад -

.legend  {
  background: #9DD2E2 !important;
}

Виникають проблеми з функцією друку браузерів. Користувачі зазвичай вибирають параметри за замовчуванням для перегляду Друк (поля, розмір сторінки та багато іншого). Тому дуже важко створити потрібний PDF-файл із необхідним стилем без навчання користувача, що набагато складніше і приблизно неможливо ...
Рахмат Алі,

4

я використовую jspdf та html2canvas для візуалізації css та експортую вміст конкретного діва, оскільки це мій код

$(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
Phil Roggenbuck

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

2

Використовуйте pdfMake.js і цей 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 з документа про розмітку (з markdown-it ) та згодом генерувати pdf та завантажувати його двійковий вміст (який я можу отримати за допомогою функції pdfMake' getBuffer()), все з браузера. Створений pdf виявляється кращим для цього типу html, ніж для інших рішень, які я намагався.
  • Я незадоволений результатами, отриманими від jsPDF.fromHTML()запропонованих у прийнятій відповіді, оскільки це рішення легко плутається спеціальними символами в моєму HTML, які, мабуть, інтерпретуються як своєрідна розмітка і повністю псують отриманий PDF.
  • Використання рішень на основі полотна (як, наприклад, застаріла jsPDF.from_html()функція, не плутати з тією, що є прийнятою відповіддю) для мене не є варіантом, оскільки я хочу, щоб текст у створеному PDF-файлі був вставним, тоді як рішення на основі полотна генерують PDF-файли на основі бітової карти.
  • Пряма відмітка до PDF-перетворювачів, таких як md-to-pdf, є лише стороною сервера, і не працює для мене.
  • Використання функцій друку браузера не допоможе мені, оскільки я не хочу відображати згенерований PDF, але завантажувати його двійковий вміст.

Якщо я читаю код правильно, це не підтримує стилі меж CSS (наприклад, у таблицях), правильно?
ninjagecko

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>

2
Ви можете, будь ласка, публікувати приклад, як ви передаєте html
Erum

але ваш код <script src = " html2canvas.hertzen.com/build/html2canvas.js" > </… > що означає, що йому потрібен Інтернет?
Ерум

1
@Erum Ви можете завантажити файл.
Едуардо Куомо

0

Щоб захопити div як PDF, ви можете використовувати https://grabz.it рішенням . Він має API JavaScript, який є легким і гнучким і дозволить вам захоплювати вміст одного елемента 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>

Щоб зафіксувати те, що знаходиться під функцією id, вам потрібно:

//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, як у попередньому прикладі. Тепер, коли сторінка завантажена, скріншот зображення буде створений у тому самому місці, що і тег сценарію, який буде містити весь вміст функцій div та більше нічого.

Інші конфігурації та налаштування, які ви можете зробити з механізмом дівного скріншоту, перегляньте їх тут

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.