Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Table of Contents #171

Open
theotheo opened this issue Jan 6, 2019 · 4 comments
Open

Table of Contents #171

theotheo opened this issue Jan 6, 2019 · 4 comments
Labels

Comments

@theotheo
Copy link

theotheo commented Jan 6, 2019

Hi there!

Could you tell me, please, how can I create a table of contents?

@astefanutti
Copy link
Owner

We would need to generate a document outline while writing the PDF. As per the PDF specification:

12.3.3 Document Outline
A PDF document may contain a document outline that the conforming reader may display on the screen, allowing the user to navigate interactively from one part of the document to another. The outline consists of a tree-structured hierarchy of outline items (sometimes called bookmarks), which serve as a visual table of contents to display the document’s structure to the user.

In order to generate the outline model, we would need to enrich the plugin API so that we can build the TOC for each presentation frameworks. Maybe a simpler approach would be to parse the header tags, as https://github.com/frederickf/presentable seems to be doing.

@astefanutti
Copy link
Owner

Some interesting pointers about how to write TOC with hummus:

And PoC code:

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

@zeliboba7
Copy link

We would need to generate a document outline while writing the PDF. As per the PDF specification:

12.3.3 Document Outline
A PDF document may contain a document outline that the conforming reader may display on the screen, allowing the user to navigate interactively from one part of the document to another. The outline consists of a tree-structured hierarchy of outline items (sometimes called bookmarks), which serve as a visual table of contents to display the document’s structure to the user.

In order to generate the outline model, we would need to enrich the plugin API so that we can build the TOC for each presentation frameworks. Maybe a simpler approach would be to parse the header tags, as https://github.com/frederickf/presentable seems to be doing.

There is reveal.js-menu plugin, it generates table of content for slide navigation. Maybe it can be used for TOC in PDF export?

@astefanutti
Copy link
Owner

There is reveal.js-menu plugin, it generates table of content for slide navigation. Maybe it can be used for TOC in PDF export?

@zeliboba7 thanks. It could be used to build the TOC, and then create the PDF document outline from it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants