Skip to content

Commit

Permalink
Add table-of-contents outline.
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelrsweet committed Dec 20, 2024
1 parent 5bc7ebe commit e4081f2
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 3 deletions.
1 change: 1 addition & 0 deletions examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

# Common options
CFLAGS = -g $(CPPFLAGS)
#CFLAGS = -g -fsanitize=address $(CPPFLAGS)
CPPFLAGS = -I..
LIBS = -L.. -lpdfio -lz

Expand Down
148 changes: 146 additions & 2 deletions examples/md2pdf.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,16 @@ typedef struct doctarget_s // Document target info

#define DOCTARGET_MAX 1000 // Maximum number of targets per document

typedef struct doctoc_s // Document table-of-contents entry
{
int level; // Level
int count; // Total number of child entries
pdfio_obj_t *obj; // Dictionary object
pdfio_dict_t *dict; // Dictionary value
} doctoc_t;

#define DOCTOC_MAX 1000 // Maximum number of TOC entries

typedef struct docdata_s // Document formatting data
{
pdfio_file_t *pdf; // PDF file
Expand All @@ -113,6 +123,8 @@ typedef struct docdata_s // Document formatting data
docaction_t actions[DOCACTION_MAX]; // Actions for this document
size_t num_targets; // Number of targets for this document
doctarget_t targets[DOCTARGET_MAX]; // Targets for this document
size_t num_toc; // Number of table-of-contents entries
doctoc_t toc[DOCTOC_MAX]; // Table-of-contents entries
} docdata_t;

typedef struct linefrag_s // Line fragment
Expand Down Expand Up @@ -244,6 +256,7 @@ static void render_row(docdata_t *dd, size_t num_cols, tablecol_t *cols, tablero
static void set_color(docdata_t *dd, doccolor_t color);
static void set_font(docdata_t *dd, docfont_t font, double fsize);
static void write_actions(docdata_t *dd);
static void write_toc(docdata_t *dd);


//
Expand Down Expand Up @@ -344,7 +357,12 @@ main(int argc, // I - Number of command-line arguments
pdfioStreamClose(dd.st);
add_links(&dd);
}

write_actions(&dd);

if (dd.num_toc > 0)
write_toc(&dd);

pdfioFileClose(dd.pdf);

mmdFree(doc);
Expand Down Expand Up @@ -912,13 +930,35 @@ format_doc(docdata_t *dd, // I - Document data
case MMD_TYPE_HEADING_4 :
case MMD_TYPE_HEADING_5 :
case MMD_TYPE_HEADING_6 :
if (dd->heading)
free(dd->heading);
free(dd->heading);

dd->heading = mmdCopyAllText(current);

format_block(dd, current, DOCFONT_BOLD, heading_sizes[curtype - MMD_TYPE_HEADING_1], left, right, /*leader*/NULL);

if (dd->num_toc < DOCTOC_MAX)
{
doctoc_t *t = dd->toc + dd->num_toc;
// New TOC
pdfio_array_t *dest; // Destination array

t->level = curtype - MMD_TYPE_HEADING_1;
t->dict = pdfioDictCreate(dd->pdf);
t->obj = pdfioFileCreateObj(dd->pdf, t->dict);
dest = pdfioArrayCreate(dd->pdf);

pdfioArrayAppendObj(dest, pdfioFileGetPage(dd->pdf, pdfioFileGetNumPages(dd->pdf) - 1));
pdfioArrayAppendName(dest, "XYZ");
pdfioArrayAppendNumber(dest, PAGE_LEFT);
pdfioArrayAppendNumber(dest, dd->y + heading_sizes[curtype - MMD_TYPE_HEADING_1] * LINE_HEIGHT);
pdfioArrayAppendNumber(dest, 0.0);

pdfioDictSetArray(t->dict, "Dest", dest);
pdfioDictSetString(t->dict, "Title", pdfioStringCreate(dd->pdf, dd->heading));

dd->num_toc ++;
}

if (dd->num_targets < DOCTARGET_MAX)
{
doctarget_t *t = dd->targets + dd->num_targets;
Expand Down Expand Up @@ -1690,3 +1730,107 @@ write_actions(docdata_t *dd) // I - Document data
pdfioObjClose(a->obj);
}
}


//
// 'write_toc()' - Write the table-of-contents outline.
//

static void
write_toc(docdata_t *dd) // I - Document data
{
size_t i, j; // Looping vars
doctoc_t *t, // Table-of-contents entry
*nt, // Next entry
*levels[6]; // Current entries for each level
int level; // Current level
pdfio_dict_t *dict; // Outline dictionary
pdfio_obj_t *obj; // Outline object


// Initialize the various TOC levels...
levels[0] = levels[1] = levels[2] = levels[3] = levels[4] = levels[5] = NULL;

// Scan the table of contents and finalize the dictionaries...
for (i = 0, t = dd->toc; i < dd->num_toc; i ++, t ++)
{
// Set parent, previous, and next entries...
if (t->level > 0 && levels[t->level - 1])
pdfioDictSetObj(t->dict, "Parent", levels[t->level - 1]->obj);

if (levels[t->level])
pdfioDictSetObj(t->dict, "Prev", levels[t->level]->obj);

for (j = i + 1, nt = t + 1; j < dd->num_toc; j ++, nt ++)
{
if (nt->level == t->level)
{
pdfioDictSetObj(t->dict, "Next", nt->obj);
break;
}
else if (nt->level < t->level)
{
break;
}
}

// First, last, and count...
for (level = 0; level < t->level; level ++)
levels[level]->count ++;

levels[t->level] = t;

if ((i + 1) < dd->num_toc && t[1].level > t->level)
pdfioDictSetObj(t->dict, "First", t[1].obj);

if ((i + 1) >= dd->num_toc)
{
// Close out all levels...
for (level = t->level; level > 0; level --)
{
pdfioDictSetObj(levels[level - 1]->dict, "Last", levels[level]->obj);
levels[level] = NULL;
}
}
else if (t->level > t[1].level)
{
// Close out N levels...
for (level = t->level; level > t[1].level; level --)
{
pdfioDictSetObj(levels[level - 1]->dict, "Last", levels[level]->obj);
levels[level] = NULL;
}
}
}

// Create the top-level outline object...
dict = pdfioDictCreate(dd->pdf);
obj = pdfioFileCreateObj(dd->pdf, dict);

pdfioDictSetName(dict, "Type", "Outline");
pdfioDictSetNumber(dict, "Count", dd->num_toc);
pdfioDictSetObj(dict, "First", dd->toc[0].obj);

// Close the objects for the entries...
for (i = 0, t = dd->toc; i < dd->num_toc; i ++, t ++)
{
if (t->level == 0)
pdfioDictSetObj(dict, "Last", t->obj);

if (t->count)
{
// Set Count value...
if (t->level == 0)
pdfioDictSetNumber(t->dict, "Count", t->count);
else
pdfioDictSetNumber(t->dict, "Count", -t->count);
}

pdfioObjClose(t->obj);
}

// Close the outline object and add it to the document catalog...
pdfioObjClose(obj);

pdfioDictSetObj(pdfioFileGetCatalog(dd->pdf), "Outlines", obj);
}
2 changes: 1 addition & 1 deletion examples/mmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ mmdCopyAllText(mmd_t *node) // I - Parent node
char *all = NULL, // String buffer
*allptr = NULL, // Pointer into string buffer
*temp; // Temporary pointer
size_t allsize = 0, // Size of "all" buffer
size_t allsize = 1, // Size of "all" buffer
textlen; // Length of "text" string
mmd_t *current, // Current node
*next; // Next node
Expand Down

0 comments on commit e4081f2

Please sign in to comment.