parallax / jsPDF

Client-side JavaScript PDF generation for everyone.
https://parall.ax/products/jspdf
MIT License
29.19k stars 4.66k forks source link

Hyperlinks #170

Closed diegocr closed 9 years ago

diegocr commented 10 years ago

Hi,

It looks like Hyperlinks aren't supported. Is there any plan to add support for them or some hint on how to do it, ie through a plugin? :$

Thanks in advance for any help you can bring, and needless to say for a great lib.

MrRio commented 10 years ago

Reverse engineering a hyperlink produces this output when uncompress:

3 0 obj
<<
  /Annots [
    <<
      /A <<
        /S /URI
        /Type /Action
        /URI (http://www.google.com/url?q=http%3A%2F%2Fen.wikipedia.org%2Fwiki%2FHyperlink&sa=D&sntz=1&usg=AFQjCNH5FcSa6jKnDjpMwm1M5sc87ixB0Q)
      >>
      /Border [
        0
        0
        0
      ]
      /Rect [
        72
        706.5
        119.25
        719.25
      ]
      /Subtype /Link
      /Type /Annot
    >>
  ]
  /Contents 4 0 R
  /MediaBox [
    0
    0
    612
    792
  ]
  /Parent 2 0 R
  /Resources <<
    /ExtGState <<
      /G0 6 0 R
    >>
    /Font <<
      /F0 7 0 R
    >>
    /ProcSets [
      /PDF
      /Text
      /ImageB
      /ImageC
      /ImageI
    ]
  >>
  /Type /Page
>>
endobj
MrRio commented 10 years ago

I bet most of this can come out, looks like it just draws a box over the text with the action on. I'll have a go at adding it.

diegocr commented 10 years ago

Great, Thank you!

I've been looking around too and found this sample PDF:

http://www.antennahouse.com/XSLsample/pdf/sample-link_1.pdf

Where the link chunk is:

12 0 obj
<<
/Type /Annot
/Subtype /Link
/Rect [ 89.17827 464.13385 235.85307 479.13385 ]
/M (D:20130325152227-05'00')
/Border [0 0  0]
/A << /S /URI /URI (hxxp://www.antennahouse.com/xx.htm) >>
>>
endobj

Thing is, i dunno how to associate that to a word/sentence. Also tried altering some of the Rect values and apparently it changed nothing, which confused me and i gave up since at the end I wasn't sure if that could be implemented through jsPDF, so nice to know it's possible :)

MrRio commented 10 years ago

I reckon we need to take the coordinates of the drawn words and place this object over the top. Which isn't entirely straightforward. Especially if the words span over multiple lines. — Sent on the run. Apologies for brevity.

On Fri, Jan 31, 2014 at 8:58 PM, Diego Casorran notifications@github.com wrote:

Great, Thank you! I've been looking around too and found this sample PDF: http://www.antennahouse.com/XSLsample/pdf/sample-link_1.pdf Where the link chunk is: 12 0 obj << /Type /Annot /Subtype /Link /Rect [ 89.17827 464.13385 235.85307 479.13385 ] /M (D:20130325152227-05'00') /Border [0 0 0] /A << /S /URI /URI (hxxp://www.antennahouse.com/xx.htm) >>

endobj

Thing is, i dunno how to associate that to a word/sentence. Also tried altering some of the Rect values and apparently it changed nothing, which confused me and i gave up since at the end I wasn't sure if that could be implemented through jsPDF, so nice to know it's possible :)

Reply to this email directly or view it on GitHub: https://github.com/MrRio/jsPDF/issues/170#issuecomment-33841811

MrRio commented 10 years ago

Or there's a rich text object type that can include A tags I think — Sent on the run. Apologies for brevity.

On Fri, Jan 31, 2014 at 8:58 PM, Diego Casorran notifications@github.com wrote:

Great, Thank you! I've been looking around too and found this sample PDF: http://www.antennahouse.com/XSLsample/pdf/sample-link_1.pdf Where the link chunk is: 12 0 obj << /Type /Annot /Subtype /Link /Rect [ 89.17827 464.13385 235.85307 479.13385 ] /M (D:20130325152227-05'00') /Border [0 0 0] /A << /S /URI /URI (hxxp://www.antennahouse.com/xx.htm) >>

endobj

Thing is, i dunno how to associate that to a word/sentence. Also tried altering some of the Rect values and apparently it changed nothing, which confused me and i gave up since at the end I wasn't sure if that could be implemented through jsPDF, so nice to know it's possible :)

Reply to this email directly or view it on GitHub: https://github.com/MrRio/jsPDF/issues/170#issuecomment-33841811

diegocr commented 10 years ago

I've downloaded a copy of the PDF 1.3 specs from the Adobe site, i'll check if i can figure out how to do it myself, but no promises... in any case, i get what you mean about how it should work, thanks!

For now i've forked your repo and made some fixes as a first attempt getting familiar on how things works internally.

Cheers!

MrRio commented 10 years ago

Thanks for that :)

diegocr commented 10 years ago

Apparently i can't get this to work :(

I've tried - on a quick & dirty way - to hack into .text() to get the coordinates of the word being inserted, and adding that to an object which is later used to construct the Annot at putResources(), diff as follow:

+            putAnnots = function () {
+               for(var k in annots) {
+                   var v = annots[k],
+                       u = v.pop();
+                   newObject();
+                   out('<<');
+                   out('/Type /Annot');
+                   out('/Subtype /' + k);
+                   out('/Rect [ ' + v.join(" ") + ' ]');
+                   out('/A << /S /URI /URI (' + pdfEscape(u) + ') >>');
+                   out('>>');
+                   out('endobj');
+               }
+           },
             putResources = function () {
                 putFonts();
+               putAnnots();
                 events.publish('putResources');
@@ -1227,6 +1246,10 @@ PubSub implementation
             // Using "'" ("go next line and render text" mark) would save space but would complicate our rendering code, templates
+           
+           var _x = (x * k), _y = ((pageHeight - y) * k), _w = (0.84 * 300), _h = (activeFontSize * lineHeightProportion);
+           annots['Link'] = [f3(_x), f3(_y), f3(_x+_w), f3(_y+_h), 'http://example.com/'];
+           console.log('Adding annot: ' + str, annots['Link']);
             // BT .. [...snip..]

Some clue about what i'm missing? :$

MrRio commented 10 years ago

Hmmm. Seems like it's not as straightforward as I thought.

diegocr commented 10 years ago

I still think there should be some kind of association, other than the /Rect values, not sure though. Reading through the document spec was a little puzzle.

I'm sure that shouldn't be much of a headache for someone already experienced working with the PDF spec, so feel free to give it a try yourself when you get a chance, if you don't mind. Thanks!

ashburnham commented 10 years ago

I just added this on to the core jsPDF on my local and it's working. Will make pull request with code when it's a bit more tested.

diegocr commented 10 years ago

@ashburnham Awesome! it'll be quite appreciated that PR :-)

ashburnham commented 10 years ago

Thanks, sorry taken so long. I had to tweak the package to make it play nicely with Meteor.js so need to change it back first

On 30 June 2014 05:28, Diego Casorran notifications@github.com wrote:

@ashburnham https://github.com/ashburnham Awesome! it'll be quite appreciated that PR :-)

— Reply to this email directly or view it on GitHub https://github.com/MrRio/jsPDF/issues/170#issuecomment-47493936.

diegocr commented 10 years ago

No problem, there is no hurry.

Flamenco commented 9 years ago

@MrRio I really need this feature...

I found this http://www.w3.org/TR/WCAG20-TECHS/PDF11.html It explains how we can overlay the link:

"In PDF the page must be painted first and then a link annotation placed over the area where the object action will occur."

I am going to take a stab at it tonight.

Flamenco commented 9 years ago

I successfully created hyperlinks in jsPDF that

  1. go to a given page in the pdf
  2. go to an external url

These 2 cases do not need a 'named target', so I did not have to go down that path... However, I can now create a table of contents in my PDF.

I will put this into a plugin and post to github soon.

Flamenco commented 9 years ago

I added an annotation plugin. For now, it just supports internal goto page and external goto url.

Here is test that generates a table of contents. http://htmlpreview.github.io/?https://github.com/Flamenco/jsPDF/blob/master/test/test_annotation.html

diegocr commented 9 years ago

Awesome, thanks a lot.

Please test the generated PDF in Adobe Reader because only the first page is shown.

Flamenco commented 9 years ago

Oops. I pushed the wrong branch. I will fix that in a couple of hours.

Steven@Spungin.tv

On Nov 9, 2014, at 1:07 PM, Diego Casorran notifications@github.com wrote:

Awesome, thanks a lot.

Please test the generated PDF in Adobe Reader because only the first page is shown.

— Reply to this email directly or view it on GitHub.

Steve1990 commented 9 years ago

can anyone paste the working snippet of it Thanks!

Flamenco commented 9 years ago

Try: http://rawgit.com/Flamenco/jsPDF/master/test/test_annotation.html

Flamenco commented 9 years ago

This now seems to have a bug in the reader-client...

miguelismo commented 9 years ago

Hello! I found a pseudo solution to insert hyperlinks in the generated pdf (I'm using doc.fromHTML to generate pdf from html) ... I have replaced the "<" and ">" signs on the <a> tag to ascii code.

RahulBuge commented 9 years ago

miguelismo whatever you implemented pls share your code

miguelismo commented 9 years ago

Here is the code ;)

    <script src="js/jquery.js"></script>
    <script src="js/jspdf.js"></script>
    <script src="js/jspdf.plugin.from_html.js"></script>
    <script src="js/jspdf.plugin.split_text_to_size.js"></script>
    <script src="js/jspdf.plugin.standard_fonts_metrics.js"></script>
    $(document).ready(function() {

      $(".generaPdf").click(function() {

            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");

      });

    });
    <div>
    Shirt / Price: $15.990</div>
    <div>
    <span style="display:none;">
    Link: &#60;a href="http://www.google.com&#62;
    </span>
    </div>

    <div class="generaPdf" id="ignorePDF" style="cursor:pointer;">Generate PDF</div>
RahulBuge commented 9 years ago

Thanks miguelismo

dbmeister commented 9 years ago

Thanks miguelismo. I'd been struggling with this until I saw your post

SandrineProusteau commented 9 years ago

I'm interested in bookmarks settings, from HTML titles. I've tried replacing <a by & # 60;a$ # 62;, but my result is displaying <a .. in the pdf...

RahulBuge commented 9 years ago

Thanks miguelismo, I have used your code and which is working fine, hyperlink created by this code works only in desktop (browser/Adobe Reader) but it is not supported for device(tablet).

prashantsable commented 9 years ago

Hi All, I am able to add the hyperlink to pdf by using following code snippet. You have add following method into the jspdf.js file.

//Add link start here
        API.addLink = function (text, x, y) {
            var defaultFontSize = 18;
            var maxLineHeight = 1.25;
            var font_id ="F9";
            var style_font_size = 1;
            out('q');
            this.internal.write(
                'q' // canning the scope
                , 'BT' // Begin Text
                // and this moves the text start to desired position.
                , this.internal.getCoordinateString(x)
                , this.internal.getVerticalCoordinateString(y)
                , 'Td'
            );

            this.internal.write(
                0
                , (-1 * defaultFontSize * maxLineHeight).toFixed(2) // shifting down a line in native `points' means reducing y coordinate
                , 'Td'
                // , (defaultFontSize * maxLineHeight).toFixed(2) // line height comes as float = proportion to normal.
                // , 'TL' // line height marker. Not sure we need it with "Td", but... 
            );

            //Line NO(233)calls pdfEscape:
            this.internal.write(
                '/' + 'F9' // font key
                , (defaultFontSize * style_font_size).toFixed(2) // font size comes as float = proportion to normal.
                , 'Tf' // font def marker
                , '('+this.internal.pdfEscape(text)+') Tj'
            );

            this.internal.write(
                'ET' // End Text
                , 'Q' // restore scope
            );
            out('Q');
            return this;
        };
//End of Add link function      

This is working fine in Adobe Reader on desktop. But this is giving me problem for links, if I open the PDF file on Android Tablet using Adobe reader App. Could you help me in solving this issue?

MrRio commented 9 years ago

This is working in the master branch now with annotations, thanks for all your help :)

sudhindrapc commented 9 years ago

@MrRio FYI for android hyperlinks are active in Hancom office app.

trevordowdle commented 8 years ago

@MrRio Hey Mr Rio. I am not seeing the addLink function in master, is this still in the works?

Thanks

stephenq commented 8 years ago

examples are here: https://github.com/MrRio/jsPDF/blob/master/examples/annotation/test_annotation.html

the function is called textWithLink

eKoopmans commented 7 years ago

Hi everyone, I've noticed that there isn't clear documentation for the .link() and .textWithLink() features, so I'm writing up a doc here.

Features

jsPDF Method Argument type(s) Description
link(x, y, w, h, options) number, number, number, number, Object Creates a transparent rectangular hyperlink at the chosen coordinates.
textWithLink(text, x, y, options) string, number, number, Object Creates plain text with a hyperlink overlay at the chosen coordinates.

options object

Optional Field Type Default Description
url string undefined An external URL to link to.
pageNumber number undefined A PDF page number to link to.
name string undefined Name of an element in the PDF to link to? (Unknown)
magFactor string 'XYZ' For pageNumber links: The zoom/magnification strategy to use. Available options: 'Fit', 'FitH', 'FitV', and 'XYZ'.
top number 0 For pageNumber links: The vertical position to use as the top of the page (only used on 'FitH' and 'XYZ' magFactors).
left number 0 For pageNumber links: The horizontal position to use as the left of the page (only used on 'FitV' and 'XYZ' magFactors).
zoom number 0 For pageNumber links: The zoom level to apply (only used on 'XYZ' magFactors).

Usage

// Create the jsPDF object.
var pdf = new jsPDF();

// Create 'test' with a link at position 25,25 (default units: mm).
var textX = 25, textY = 25;
pdf.textWithLink('test', textX, textY, {url: 'https://www.google.com/'});

// Create a rectangle (just for visual effect) and place a link on top of it.
var rectX = 25, rectY = 50, rectW = 25, rectH = 25;
pdf.rect(rectX, rectY, rectW, rectH, 'f');
pdf.link(rectX, rectY, rectW, rectH, {url: 'https://www.google.com/'});

// Save the PDF.
pdf.save('test.pdf');
ElMaghri commented 6 years ago

hello i like to create a summary with textWithLink and linked with an element that i will create but i dont know how to give an element a name and referenced

mrcoles commented 5 years ago

Btw, the pageNumber doesn't seem to work for me, so I’m calling doc.setPage first instead. Also in the progress of that I learned that pages are 1-indexed and added this PR https://github.com/MrRio/jsPDF/pull/2579

bytrangle commented 4 years ago

I know this is a long shot, but how can I use pdf.link() with pdf.html() method? The html2pdf library generates clickable hyperlink automatically, but it's an image, not a real pdf so that kinda defeats the purpose of exporting to pdf for me.

HackbrettXXX commented 4 years ago

@trangthule I'm not sure if this is even possible with the approach of using html2canvas together with a custom canvas context. The information about the links is lost through the canvas API. You might manually add the links after the html call, but finding out the right coordinates is probably very hard.

royalvincent commented 4 years ago

@eKoopmans is there a way to add hyperlink to an image?

HackbrettXXX commented 4 years ago

@royalvincent you can use the link method and use the same x/y/width/height properties as the image.

eduardo-escobar commented 3 years ago

Hello, I need a hyperlink with the target _blank option, does anyone know how to do this?, I've been looking for more than two days it

HackbrettXXX commented 3 years ago

@eduardo-escobar AFAIK that is not even possible in PDF files.

hrc7505 commented 2 years ago

How can we add a hyperlink to an image? Usecase: hyperlink on social media icons

Is there a way to do it?

HackbrettXXX commented 2 years ago

@hrc7505 use the link function and pass in the image location/dimensions.

minhduyphamdev commented 1 year ago

Please update doc I think top, left position option is really important