hummusRecipe icon indicating copy to clipboard operation
hummusRecipe copied to clipboard

Link annotations?

Open davidduffett opened this issue 7 years ago • 8 comments

I'm still working through understanding PDF structures so forgive me if I'm not quite there but as I understand it links within the document are created using special link annotations, which are provided a URL action ('A') and height and width of the rectangle of the link. This could then be placed over an image for example.

Do you have any plans to support link annotations? From what I can see there isn't the necessary fields available in your API to support them?

davidduffett avatar Jan 09 '18 01:01 davidduffett

Yes, it's in my scope. I will see what I can do and make it easier. You are right about the link annotation. If you want a link on your image, technically you will need to create a image xobjectform and a link annotation with a rectangle using the image dimension. https://github.com/chunyenHuang/hummusRecipe/blob/4ddd1eafe7cdee2505ef2512f1b0e037d980cef9/lib/annotation.js#L50 However, you should be able to create this by something like this eventually if I add the support for annotations.

            .image(myCats, 'center', 'center', {
                width: 300,
                height: 300,
                keepAspectRatio: false,
                opacity: 0.4,
                align: 'center center',
                link: 'https://www.google.com'
            })

chunyenHuang avatar Jan 09 '18 01:01 chunyenHuang

+1 I'd also like to be able to add link annotations. Specifically, I'd like to be able to build a dynamic table of contents that includes links to particular pages in the PDF.

chadkirby avatar Apr 25 '18 20:04 chadkirby

I would love to have this feature as well. However, I will need more time for it and probably wait for this Hummus PR #229

chunyenHuang avatar Apr 26 '18 00:04 chunyenHuang

Until then, it's possible to create a table-of-contents PDF with internal page links using pdfkit, then hummusRecipe can append pages to the pdfkit-generated TOC PDF. You have to make sure that pdfkit has finished writing the TOC before reading the PDF using hummusRecipe, but aside from that, it's pretty straightforward.

chadkirby avatar May 01 '18 21:05 chadkirby

KK, I will steal some ideas from Devon. Thanks for the alternative solution and reference.

chunyenHuang avatar May 01 '18 22:05 chunyenHuang

I just wrote a way to write TOC with hummus, here is the code (public domain):

function main() {
    const hummus = require('hummus');
    let w = hummus.createWriter(...);
    let ctx = w.getObjectsContext();
    let events = w.getEvents();

    let page_ids: number[] = [];
    ...

    let outline = writeOutline(ctx, [
        { title: "Title", page_idx: 1 },
        { title: "Title 2", page_idx: 2, childs: [
            { title: "Title 3", page_idx: 3 },
        ]},
    ], page_ids);
    events.on('OnCatalogWrite', (e: any) => {
        let d = e.catalogDictionaryContext;
        if (outline !== null) {
            d.writeKey("Outlines");
            d.writeObjectReferenceValue(outline);
            d.writeKey("PageMode");
            d.writeNameValue("UseOutlines");
        }
    });
    w.end();
}

type Outline = { title: string, page_idx: number, childs?: Outline[] };
function writeOutline(ctx: any, outlines: Outline[], page_ids: number[]) : number | null
{
    if (outlines.length === 0)
        return null;

    let outline = ctx.allocateNewObjectID();
    let outline_ids = writeOutlines(ctx, outlines, outline, page_ids);
    ctx.startNewIndirectObject(outline);
    let d = ctx.startDictionary();
    d.writeKey("Type");
    d.writeNameValue("Outlines");
    d.writeKey("Count");
    d.writeNumberValue(outline_ids.length);
    d.writeKey("First");
    d.writeObjectReferenceValue(outline_ids[0]);
    d.writeKey("Last");
    d.writeObjectReferenceValue(outline_ids[outline_ids.length - 1]);
    ctx.endDictionary(d);
    ctx.endIndirectObject();
    return outline;
}
function writeOutlines(ctx: any, outlines: Outline[], parent: number, page_ids: number[]) : number[]
{
    let ids = outlines.map(() => ctx.allocateNewObjectID());
    outlines.forEach(({ title, page_idx, childs }, i) => {
        let id = ids[i];
        let child_ids = childs && childs.length ? writeOutlines(ctx, childs, id, page_ids) : null;
        ctx.startNewIndirectObject(id);
        let d = ctx.startDictionary();

        d.writeKey("Title");
        d.writeLiteralStringValue(title);

        d.writeKey("Parent");
        d.writeObjectReferenceValue(parent);

        d.writeKey("Dest");
        ctx.startArray();
        ctx.writeIndirectObjectReference(page_ids[page_idx]);
        ctx.writeName("XYZ");
        let c = ctx.startFreeContext();
        c.write([ 32, 110, 117, 108, 108, 32, 110, 117, 108, 108, 32, 48, 32 ]/*" null null 0 "*/);
        ctx.endFreeContext();
        ctx.endArray();
        ctx.endLine();

        if (child_ids) {
            d.writeKey("Count");
            d.writeNumberValue(outlines.length);
            d.writeKey("First");
            d.writeObjectReferenceValue(child_ids[0]);
            d.writeKey("Last");
            d.writeObjectReferenceValue(child_ids[child_ids.length - 1]);
        }

        if (i + 1 < ids.length) {
            d.writeKey("Next");
            d.writeObjectReferenceValue(ids[i + 1]);
        }

        if (i > 0) {
            d.writeKey("Prev");
            d.writeObjectReferenceValue(ids[i - 1]);
        }

        ctx.endDictionary(d);
        ctx.endIndirectObject();
    });
    return ids;
}

Speedy37 avatar Dec 10 '18 19:12 Speedy37

I created an npm module with this functionality, feel free to give feedback. Thanks!

larryboymi avatar Apr 04 '19 21:04 larryboymi

It is pointless to support pdf editor/maker without modify existing pdf, inserting svg with HTML semantic elements. Make a clear new PDF is like newbie task. We live in 2022.

There is like two pdf libs for nodejs and none of them fulfills requirements I have. What is the point of editing a PDF without possibility to pass HTML element e.g. with url or edit metaData.

Bosper avatar Nov 15 '22 12:11 Bosper