Czy można zapisać stronę HTML jako PDF za pomocą JavaScript lub jQuery?

83

Czy można zapisać stronę HTML jako PDF za pomocą JavaScript lub jQuery?

Szczegółowo:

Wygenerowałem jedną stronę HTML zawierającą tabelę. Posiada jeden przycisk „zapisz jako PDF”. Jeśli użytkownik kliknie ten przycisk, ta strona HTML musi zostać przekonwertowana na plik PDF.

Czy jest to możliwe przy użyciu JavaScript lub jQuery?

Baran
źródło
3
O ile wiem, Javascript nie może tworzyć dokumentów PDF.
Khoa Le
1
Obecnie wiele osób może drukować w plikach PDF. Więc ta funkcjonalność prawdopodobnie nie jest wymagana.
Eric
4
PDF to język tekstowy. Trudno byłoby znaleźć język programowania, z którego nie mógłbyś go wygenerować.
Quentin
1
Możliwa odpowiedź tutaj: stackoverflow.com/questions/12108806/ ...
colin

Odpowiedzi:

28

Tak , użyj jspdf, aby utworzyć plik pdf.

Następnie możesz przekształcić go w identyfikator URI danych i wstawić łącze pobierania do DOM

Będziesz jednak musiał samodzielnie napisać konwersję HTML do PDF.

Po prostu użyj wersji strony do druku i pozwól użytkownikowi wybrać, w jaki sposób chce wydrukować stronę.

Edycja: Najwyraźniej ma minimalne wsparcie

Zatem odpowiedzią jest napisanie własnego programu do zapisywania plików PDF lub skorzystanie z istniejącego programu do zapisywania plików PDF, który zrobi to za Ciebie (na serwerze).

Raynos
źródło
jspdf wygląda interesująco, ale wersje demonstracyjne nie działają w przeglądarce Firefox 5.0 ani IE!
Tim Büthe
jspdf ma minimalne wsparcie dla funkcji, nie obsługuje nawet grafiki. Ale jeśli nie ma systemu stylów i rzeczy do renderowania - jest to po prostu przydatne do małych notatek. W przeciwnym razie będziesz musiał napisać cały renderer HTML również w JS (lub oszukać i użyć płótna z drawElement, który jest właśnie obsługiwany w Firefoksie, o którym pamiętam). Javascript naprawdę nie nadaje się do tej linii kodowania, jeśli o mnie chodzi. Być może najłatwiej jest zadzwonić do zewnętrznej usługi internetowej.
Jon Lennart Aasenden
JavaScript (jako język) zależy wyłącznie od tego. JavaScript działający z mniejszymi ograniczeniami modeli bezpieczeństwa przeglądarek i interfejsów API.
Quentin
4
Jestem pewien, że ktoś mógłby zbudować dom z zapałek lub wykałaczek, ale ostatecznie - w pełni rozwinięty kompilator pdf z obsługą zszywania, grafiki, osadzania czcionek, stylów i w pełni funkcjonalnych tabel wyszukiwania nie wchodzi w rachubę. Spójrz na źródło jspdf - obsługuje tylko najprostsze tagi i nie zawiera słowników wyszukiwania. W pełni rozwinięty kompilator pdf jest trudny do zrobienia nawet w C ++ czy Delphi, czysta implementacja JS byłaby samobójcza. Są firmy, które od lat sprzedają tylko swoje kompilatory pdf (zobacz na przykład gnostice). To nie jest „jeden liniowiec”.
Jon Lennart Aasenden
@JonLennartAasenden Tak, ma minimalne wsparcie. Jeśli chcesz, nadal możesz napisać program do zapisywania plików PDF w js. Nie jest to jednak łatwe zadanie. Czysta implementacja JS jest tak samo samobójcza jak C ++ czy Delphi. Nie udawaj, że JS jest obywatelem drugiej kategorii.
Raynos
15

Tak, bardzo łatwo to zrobić z javascriptem. Mam nadzieję, że ten kod jest dla Ciebie przydatny.

Będziesz potrzebować biblioteki JSpdf .

<div id="content">
     <h3>Hello, this is a H3 tag</h3>

    <p>a pararaph</p>
</div>
<div id="editor"></div>
<button id="cmd">Generate PDF</button>

<script>
    var doc = new jsPDF();
    var specialElementHandlers = {
        '#editor': function (element, renderer) {
            return true;
        }
    };

    $('#cmd').click(function () {
        doc.fromHTML($('#content').html(), 15, 15, {
            'width': 170,
                'elementHandlers': specialElementHandlers
        });
        doc.save('sample-file.pdf');
    });

    // This code is collected but useful, click below to jsfiddle link.
</script>

jsfiddle link tutaj

Blackjoker
źródło
1
obraz nie jest drukowany w formacie pdf :( masz rozwiązanie ??
Maestro Vladimir
2
Nie obsługuje poprawnie wielu stron ani stylów css.
Dynamiczny
1
Nie obsługuje tabel :( Podobało mi się podejście
FutoRicky
3
Link JSFiddle prowadzi do strony 404
Oscar Chambers
13

To może być późna odpowiedź, ale tak jest najlepiej: https://github.com/eKoopmans/html2pdf

Czysta implementacja javascript. Pozwala określić tylko jeden element za pomocą identyfikatora i przekonwertować go.

Billcountry
źródło
1
Żadna odpowiedź nie jest spóźniona :). Dzięki
Kalyan Chavali
7

Możesz użyć Phantomjs. Pobierz tutaj i użyj następującego przykładu, aby przetestować funkcję konwersji html-> pdf https://github.com/ariya/phantomjs/blob/master/examples/rasterize.js

Przykładowy kod:

phantomjs.exe examples/rasterize.js http://www.w3.org/Style/CSS/Test/CSS3/Selectors/current/xhtml/index.html sample.pdf
singh1469
źródło
Czy możesz go używać z javascriptem zamiast z narzędziem wiersza poleceń?
Dynamiczny
@Dynamic nie, to bezgłowa przeglądarka, którą możesz sterować za pomocą javascript. Możesz nakazać mu wydrukowanie strony internetowej w formacie PDF za pomocą javascript, ale implementacja nie jest javascript. Jednak użyłem go w tym konkretnym scenariuszu, pakując go w aplikację, która zużywa kolejkę stron do wydrukowania, i używam javascript do dodawania wpisów do kolejki. Podobnie możesz zawinąć go w usługę. Aby kontrolować sposób drukowania rzeczy, mimo że podlegasz tym samym ograniczeniom co chrome print (np. Css i js, aby utworzyć widok przyjazny dla druku)
Shane
7

Użyłem jsPDFi dom-to-imagebiblioteki do eksportu HTML do PDF.

Piszę tutaj jako odniesienie do kogo dotyczy.

$('#downloadPDF').click(function () {
    domtoimage.toPng(document.getElementById('content2'))
      .then(function (blob) {
          var pdf = new jsPDF('l', 'pt', [$('#content2').width(), $('#content2').height()]);
          pdf.addImage(blob, 'PNG', 0, 0, $('#content2').width(), $('#content2').height());
          pdf.save("test.pdf");
      });
});

Demo: https://jsfiddle.net/viethien/md03wb21/27/

Hien Nguyen
źródło
Jeśli przejdę do trybu responsywnego i pobiorę plik PDF, strona PDF nie działa poprawnie. Czy istnieje sposób na osiągnięcie tego samego wyniku, co w trybie pulpitu, gdy pobieram w trybie responsywnym.
Nancy
6

Oto jak bym to zrobił, to pomysł, a nie kuloodporna konstrukcja, trzeba to zmodyfikować

  • Użytkownik klika przycisk Zapisz jako PDF
  • Do serwera wysyłane jest wywołanie za pomocą ajax
  • Serwer odpowiada za pomocą adresu URL do pliku PDF wygenerowanego przy użyciu HTML, bardzo pomyślnie użyłem Apache FOP
  • Js obsługujący odpowiedź ajax wykonuje polecenie location.href, aby wskazać adres URL wysłany przez JS, a gdy tylko ten adres URL zostanie załadowany, wysyła plik, używając nagłówka dyspozycji zawartości jako załącznika, zmuszając użytkownika do pobrania pliku.
Kumar
źródło
2

Konwersja html do pdf po stronie serwera jest znacznie łatwiejsza i bardziej niezawodna. Używamy Google Puppeteer. Jest dobrze utrzymany z opakowaniami dla dowolnego wybranego języka serwera. Puppeteer używa bezgłowego Chrome do generowania zrzutów ekranu i / lub plików PDF. Zaoszczędzi ci to DUŻO bólu głowy, zwłaszcza jeśli musisz wygenerować złożone pliki PDF z tabelami, obrazami, wykresami, wieloma stronami itp.

https://developers.google.com/web/tools/puppeteer/

Matej Janovčík
źródło
2

Jest jeszcze jeden bardzo oczywisty sposób konwersji HTML na PDf przy użyciu JavaScript: użyj do tego internetowego interfejsu API. To zadziała dobrze, jeśli nie musisz wykonywać konwersji, gdy użytkownik jest offline.

PdfMage to jedna z opcji, która ma fajne API i oferuje darmowe konta. Jestem pewien, że możesz znaleźć wiele alternatyw (na przykład tutaj )

W przypadku API PdfMage miałbyś coś takiego:

 $.ajax({
    url: "https://pdfmage.org/pdf-api/v1/process",
    type: "POST",
    crossDomain: true,
    data: { Html:"<html><body>Hi there!</body></html>" },
    dataType: "json",
    headers: {
        "X-Api-Key": "your-key-here" // not very secure, but a valid option for non-public domains/intranet
    },
    success: function (response) {
        window.location = response.Data.DownloadUrl;
    },
    error: function (xhr, status) {
        alert("error");
    }
});
Andre Borges
źródło
3
Wydaje się, że Freehtmltopdf.com już nie działa
Zach Saucier,
1
Niestety PDFMage nie jest już darmowy.
Prometeusz
1

Tak. Na przykład możesz skorzystać z rozwiązania https://grabz.it .

Posiada interfejs API JavaScript, którego można używać na różne sposoby, aby pobrać zrzut ekranu i manipulować nim. Aby użyć go w swojej aplikacji, musisz najpierw uzyskać klucz i tajny klucz aplikacji, a następnie pobrać bezpłatny pakiet SDK JavaScript.

Zobaczmy więc prosty przykład jego użycia:

//first include the grabzit.min.js library in the web page
<script src="grabzit.min.js"></script>
//include the code below to add the screenshot to the body tag    
<script>
//use secret key to sign in. replace the url.
GrabzIt("Sign in to view your Application Key").ConvertURL("http://www.google.com").Create();
</script>

Następnie po prostu poczekaj chwilę, a obraz automatycznie pojawi się na dole strony, bez konieczności ponownego ładowania strony.

To jest najprostsze. Więcej przykładów manipulacji obrazami, dołączania zrzutów ekranu do elementów itp. Można znaleźć w dokumentacji .

Jasio
źródło
1

$('#cmd2').click(function() {
  	var options = {
		//'width': 800,
  	};
  	var pdf = new jsPDF('p', 'pt', 'a4');
  	pdf.addHTML($("#content2"), -1, 220, options, function() {
    	pdf.save('admit_card.pdf');
  	});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.5/jspdf.min.js"></script>

<div id="content2" style="background: #fff;border-bottom: 1px solid #ffffff;">
                    	<div class="tokenDet" style="padding: 15px;border: 1px solid #000;width: 80%;margin: 0 auto;position: relative;overflow: hidden;">
                        	<div class="title" style="text-align: center;border-bottom: 1px solid #000;margin-bottom: 15px;">
                            	<h2>Entrance Exam Hall Ticket</h2>
                            </div>
                            <div class="parentdiv" style="display: inline-block;width: 100%;position: relative;">
                            	<div class="innerdiv" style="width: 80%;float: left;">
                            		<div class="restDet">
                                        <div class="div">
                                            <div class="label" style="width: 30%;float: left;">
                                                <strong>Name</strong>
                                            </div>
                                            <div class="data" style="width: 70%;display: inline-block;">
                                                <span>Santanu Patra</span>
                                            </div>
                                            <div class="label" style="width: 30%;float: left;">
                                                <strong>D.O.B.</strong>
                                            </div>
                                            <div class="data" style="width: 70%;display: inline-block;">
                                                <span>17th April, 1995</span>
                                            </div>
                                            <div class="label" style="width: 30%;float: left;">
                                                <strong>Address</strong>
                                            </div>
                                            <div class="data" style="width: 70%;display: inline-block;">
                                                <span>P.S. Srijan Corporate Park, Saltlake, Sector 5, Kolkata-91</span>
                                            </div>
                                            <div class="label" style="width: 30%;float: left;">
                                                <strong>Contact Number</strong>
                                            </div>
                                            <div class="data" style="width: 70%;display: inline-block;">
                                                <span>9874563210</span>
                                            </div>
                                            <div class="label" style="width: 30%;float: left;">
                                                <strong>Email Id</strong>
                                            </div>
                                            <div class="data" style="width: 70%;display: inline-block;">
                                                <span>[email protected]</span>
                                            </div>
                                            <div class="label" style="width: 30%;float: left;">
                                                <strong>Parent(s) Name</strong>
                                            </div>
                                            <div class="data" style="width: 70%;display: inline-block;">
                                                <span>S. Patra</span><br /><span>7896541230</span>
                                            </div>
                                            <div class="label" style="width: 30%;float: left;">
                                                <strong>Exam Center</strong>
                                            </div>
                                            <div class="data" style="width: 70%;display: inline-block;">
                                                <span>Institute of Engineering & Management</span>
                                            </div>
                                            <div class="label" style="width: 30%;float: left;">
                                                <strong>Hall Number</strong>
                                            </div>
                                            <div class="data" style="width: 70%;display: inline-block;">
                                                <span>COM-32</span>
                                            </div>
                                        </div>
                                    </div>
                            	</div>
                                <div class="sideDiv" style="width: 20%;float: left;">
                                	<div class="atts" style="float: left;width: 100%;">
                                    	<div class="photo" style="width: 115px;height: 150px;float: right;">
                                        	<img src="images/candidateImg.gif" style="width: 100%;"/>
                                        </div>
                                        <div class="sign" style="position: absolute;bottom: 0;right: 0;border-top: 1px dashed #000;left: 80%;text-align: right;">
                                        	<small>Self Attested</small>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <button class="btn btn-info" id="cmd2">Download Token</button>

Santanu
źródło
Powyższy kod nie działa w żadnej z testowanych przeze mnie przeglądarek. Po kliknięciu przycisku „Pobierz token” nic się nie dzieje i żadne błędy nie są rejestrowane. Czy możesz proszę sprawdzić.
Almeister 9
0

Krótko mówiąc: nie. Pierwszym problemem jest dostęp do systemu plików, który w większości przeglądarek jest domyślnie ustawiony na no ze względów bezpieczeństwa. Nowoczesne przeglądarki czasami obsługują minimalistyczne przechowywanie w postaci bazy danych lub można poprosić użytkownika o włączenie dostępu do plików.

Jeśli masz dostęp do systemu plików, zapisywanie jako HTML nie jest takie trudne (zobacz obiekt pliku w dokumentacji JS) - ale PDF nie jest taki łatwy. PDF to dość zaawansowany format plików, który naprawdę nie nadaje się do Javascript. Wymaga pisania informacji w typach danych, które nie są bezpośrednio obsługiwane przez Javascript, takich jak słowa i quady. Musisz także wstępnie zdefiniować system wyszukiwania w słowniku, który należy zapisać w pliku. Jestem pewien, że ktoś mógłby sprawić, że to zadziała, ale zaangażowany wysiłek i czas lepiej poświęcić na naukę C ++ lub Delphi.

Jednak eksport HTML powinien być możliwy, jeśli użytkownik zapewnia nieograniczony dostęp

Jon Lennart Aasenden
źródło
2
Dlaczego C ++ i Delphi są dziedzicznie lepsze w tworzeniu edytora PDF?
Raynos
2
Ponieważ języki te zostały stworzone do tworzenia zaawansowanego oprogramowania. Javascript nie był. Javascript nigdy nie został ukończony, dlatego prototyp systemu się kręci. Autor planował dodać do niego HL z prawdziwymi klasami i większą liczbą typów danych - ale nie miał czasu. Więc został opublikowany „tak jak jest”. Nie obsługuje wskaźników, alokacja surowej pamięci jest bolesna, nie obsługuje niektórych rodzimych typów danych, które można znaleźć w innych językach, nie obsługuje spakowanych struktur (struct w C, Record in Pascal) ... lista jest nieskończona. Uwielbiam JS, ale to zabawka na przeglądarkę, a nie prawdziwy język.
Jon Lennart Aasenden
Istnieje kilka pisarzy opartych na Javie i jeśli spojrzysz na rozmiar kodu źródłowego, powinno być dość oczywiste, że byłby on jeszcze dłuższy w Javascript - ale kluczową kwestią jest: formatowanie we / wy i tabele wyszukiwania . Jestem pewien, że ktoś mógłby to zrobić - ale byłoby to bardzo powolne i zasadniczo strata czasu. Jak osadzisz dane czcionek? Nie możesz nawet pobrać pliku z systemu operacyjnego, nie mówiąc już o jego konwersji (która jest samą w sobie biblioteką) i osadzeniu. Po co budować dom zapałek, skoro można po prostu zbudować normalny dom?
Jon Lennart Aasenden
2
„ale to zabawka przeglądarki, a nie prawdziwy język”. To tak, jakby powiedzieć, że Schemat nie jest prawdziwym językiem. „powinno być dość oczywiste, że będzie to jeszcze dłużej w Javascripcie”, nie. Java jest znacznie bardziej szczegółowa niż JavaScript. Powinien być o około 2/3 krótszy niż wersja Java. „ale byłoby to bardzo powolne i zasadniczo:” Przez ekstremalnie wolne masz na myśli 3 / 4x wolniej niż C ++? Czy możemy przestać traktować js jako obywatela drugiej kategorii. Dzięki.
Raynos
11
Niezależnie od twojej osobistej opinii, Jon, czy mógłbyś przestać twierdzić, że „Javascript nie jest prawdziwym językiem”, ponieważ to jest BS. Javascript to niezwykle wydajny język, zoptymalizowany pod kątem innych celów niż język skompilowany niskiego poziomu, taki jak C lub C ++. Są rzeczy, które możesz zrobić w JS, których nie możesz zrobić w C lub C ++, ale czy to oznacza, że ​​C i C ++ nie są „prawdziwymi” językami? Nie, tylko, że są przeznaczone do różnych rzeczy. JS jest tak samo rzeczywistym językiem programowania, jak każdy inny język kompletny.
Izochroniczny