mirror of
https://github.com/michaelrsweet/pdfio.git
synced 2024-12-26 13:28:22 +01:00
Compare commits
2 Commits
2d175fdf70
...
294f5e07c5
Author | SHA1 | Date | |
---|---|---|---|
|
294f5e07c5 | ||
|
4baafde74b |
@ -20,6 +20,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <io.h>
|
# include <io.h>
|
||||||
@ -64,11 +65,28 @@ typedef struct docimage_s // Document image info
|
|||||||
|
|
||||||
typedef struct doclink_s // Document link info
|
typedef struct doclink_s // Document link info
|
||||||
{
|
{
|
||||||
const char *url; // Reference URL
|
const char *url; // Target URL
|
||||||
pdfio_rect_t box; // Link box
|
pdfio_rect_t box; // Link box
|
||||||
} doclink_t;
|
} doclink_t;
|
||||||
|
|
||||||
#define DOCLINK_MAX 1000 // Maximum number of links/page
|
#define DOCLINK_MAX 1000 // Maximum number of links per page
|
||||||
|
|
||||||
|
typedef struct docaction_s // Document action info
|
||||||
|
{
|
||||||
|
const char *target; // Target name
|
||||||
|
pdfio_obj_t *obj; // Link object
|
||||||
|
} docaction_t;
|
||||||
|
|
||||||
|
#define DOCACTION_MAX 10000 // Maximum number of actions per document
|
||||||
|
|
||||||
|
typedef struct doctarget_s // Document target info
|
||||||
|
{
|
||||||
|
char name[128]; // Target name
|
||||||
|
size_t page; // Target page
|
||||||
|
double y; // Target page position
|
||||||
|
} doctarget_t;
|
||||||
|
|
||||||
|
#define DOCTARGET_MAX 1000 // Maximum number of targets per document
|
||||||
|
|
||||||
typedef struct docdata_s // Document formatting data
|
typedef struct docdata_s // Document formatting data
|
||||||
{
|
{
|
||||||
@ -86,9 +104,14 @@ typedef struct docdata_s // Document formatting data
|
|||||||
docfont_t font; // Current font
|
docfont_t font; // Current font
|
||||||
double fsize; // Current font size
|
double fsize; // Current font size
|
||||||
doccolor_t color; // Current color
|
doccolor_t color; // Current color
|
||||||
pdfio_obj_t *annots; // Annotations object (for links)
|
pdfio_array_t *annots_array; // Annotations array (for links)
|
||||||
|
pdfio_obj_t *annots_obj; // Annotations object (for links)
|
||||||
size_t num_links; // Number of links for this page
|
size_t num_links; // Number of links for this page
|
||||||
doclink_t links[DOCLINK_MAX]; // Links for this page
|
doclink_t links[DOCLINK_MAX]; // Links for this page
|
||||||
|
size_t num_actions; // Number of actions for this document
|
||||||
|
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
|
||||||
} docdata_t;
|
} docdata_t;
|
||||||
|
|
||||||
typedef struct linefrag_s // Line fragment
|
typedef struct linefrag_s // Line fragment
|
||||||
@ -99,6 +122,7 @@ typedef struct linefrag_s // Line fragment
|
|||||||
height; // Height of item
|
height; // Height of item
|
||||||
size_t imagenum; // Image number
|
size_t imagenum; // Image number
|
||||||
const char *text; // Text string
|
const char *text; // Text string
|
||||||
|
const char *url; // Link URL string
|
||||||
bool ws; // Whitespace before text?
|
bool ws; // Whitespace before text?
|
||||||
docfont_t font; // Text font
|
docfont_t font; // Text font
|
||||||
doccolor_t color; // Text color
|
doccolor_t color; // Text color
|
||||||
@ -203,11 +227,13 @@ static const char * const docfont_names[] =
|
|||||||
//
|
//
|
||||||
|
|
||||||
static void add_images(docdata_t *dd, mmd_t *doc);
|
static void add_images(docdata_t *dd, mmd_t *doc);
|
||||||
|
static void add_links(docdata_t *dd);
|
||||||
static pdfio_obj_t *find_image(docdata_t *dd, const char *url, size_t *imagenum);
|
static pdfio_obj_t *find_image(docdata_t *dd, const char *url, size_t *imagenum);
|
||||||
static void format_block(docdata_t *dd, mmd_t *block, docfont_t deffont, double fsize, double left, double right, const char *leader);
|
static void format_block(docdata_t *dd, mmd_t *block, docfont_t deffont, double fsize, double left, double right, const char *leader);
|
||||||
static void format_code(docdata_t *dd, mmd_t *block, double left, double right);
|
static void format_code(docdata_t *dd, mmd_t *block, double left, double right);
|
||||||
static void format_doc(docdata_t *dd, mmd_t *doc, double left, double right);
|
static void format_doc(docdata_t *dd, mmd_t *doc, double left, double right);
|
||||||
static void format_table(docdata_t *dd, mmd_t *table, double left, double right);
|
static void format_table(docdata_t *dd, mmd_t *table, double left, double right);
|
||||||
|
static void make_target_name(char *dst, const char *src, size_t dstsize);
|
||||||
static double measure_cell(docdata_t *dd, mmd_t *cell, tablecol_t *col);
|
static double measure_cell(docdata_t *dd, mmd_t *cell, tablecol_t *col);
|
||||||
static mmd_t *mmd_walk_next(mmd_t *top, mmd_t *node);
|
static mmd_t *mmd_walk_next(mmd_t *top, mmd_t *node);
|
||||||
static void new_page(docdata_t *dd);
|
static void new_page(docdata_t *dd);
|
||||||
@ -216,6 +242,7 @@ static void render_line(docdata_t *dd, double margin_left, double margin_top, do
|
|||||||
static void render_row(docdata_t *dd, size_t num_cols, tablecol_t *cols, tablerow_t *row);
|
static void render_row(docdata_t *dd, size_t num_cols, tablecol_t *cols, tablerow_t *row);
|
||||||
static void set_color(docdata_t *dd, doccolor_t color);
|
static void set_color(docdata_t *dd, doccolor_t color);
|
||||||
static void set_font(docdata_t *dd, docfont_t font, double fsize);
|
static void set_font(docdata_t *dd, docfont_t font, double fsize);
|
||||||
|
static void write_actions(docdata_t *dd);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -303,7 +330,12 @@ main(int argc, // I - Number of command-line arguments
|
|||||||
format_doc(&dd, doc, dd.art_box.x1, dd.art_box.x2);
|
format_doc(&dd, doc, dd.art_box.x1, dd.art_box.x2);
|
||||||
|
|
||||||
// Close the PDF and return...
|
// Close the PDF and return...
|
||||||
|
if (dd.st)
|
||||||
|
{
|
||||||
pdfioStreamClose(dd.st);
|
pdfioStreamClose(dd.st);
|
||||||
|
add_links(&dd);
|
||||||
|
}
|
||||||
|
write_actions(&dd);
|
||||||
pdfioFileClose(dd.pdf);
|
pdfioFileClose(dd.pdf);
|
||||||
|
|
||||||
mmdFree(doc);
|
mmdFree(doc);
|
||||||
@ -362,6 +394,88 @@ add_images(docdata_t *dd, // I - Document data
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// 'add_links()' - Add the page links, if any.
|
||||||
|
//
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_links(docdata_t *dd) // I - Document data
|
||||||
|
{
|
||||||
|
size_t i; // Looping var
|
||||||
|
doclink_t *l; // Current link
|
||||||
|
pdfio_dict_t *dict; // Object dictionary
|
||||||
|
pdfio_obj_t *aobj, // Annotation object
|
||||||
|
*lobj; // Link object
|
||||||
|
pdfio_array_t *border; // Border values
|
||||||
|
|
||||||
|
|
||||||
|
// Add any links we have...
|
||||||
|
for (i = 0, l = dd->links; i < dd->num_links; i ++, l ++)
|
||||||
|
{
|
||||||
|
if (l->url[0] == '#')
|
||||||
|
{
|
||||||
|
// No remote action...
|
||||||
|
aobj = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Create the link action (remote URL)
|
||||||
|
dict = pdfioDictCreate(dd->pdf);
|
||||||
|
pdfioDictSetName(dict, "S", "URI");
|
||||||
|
pdfioDictSetString(dict, "URI", pdfioStringCreate(dd->pdf, l->url));
|
||||||
|
|
||||||
|
aobj = pdfioFileCreateObj(dd->pdf, dict);
|
||||||
|
pdfioObjClose(aobj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the annotation object pointing to the action...
|
||||||
|
dict = pdfioDictCreate(dd->pdf);
|
||||||
|
pdfioDictSetName(dict, "Subtype", "Link");
|
||||||
|
pdfioDictSetRect(dict, "Rect", &l->box);
|
||||||
|
border = pdfioArrayCreate(dd->pdf);
|
||||||
|
pdfioArrayAppendNumber(border, 0.0);
|
||||||
|
pdfioArrayAppendNumber(border, 0.0);
|
||||||
|
pdfioArrayAppendNumber(border, 0.0);
|
||||||
|
pdfioDictSetArray(dict, "Border", border);
|
||||||
|
|
||||||
|
lobj = pdfioFileCreateObj(dd->pdf, dict);
|
||||||
|
|
||||||
|
if (l->url[0] == '#' && dd->num_actions < DOCACTION_MAX)
|
||||||
|
{
|
||||||
|
// Save this link action for later...
|
||||||
|
docaction_t *a = dd->actions + dd->num_actions;
|
||||||
|
// New action
|
||||||
|
|
||||||
|
a->target = l->url + 1;
|
||||||
|
a->obj = lobj;
|
||||||
|
|
||||||
|
dd->num_actions ++;
|
||||||
|
}
|
||||||
|
else if (aobj)
|
||||||
|
{
|
||||||
|
// Close out this link since we have a remote URL...
|
||||||
|
pdfioDictSetObj(dict, "A", aobj);
|
||||||
|
pdfioObjClose(lobj);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Nothing that can be done for this one...
|
||||||
|
pdfioObjClose(lobj);
|
||||||
|
}
|
||||||
|
|
||||||
|
pdfioArrayAppendObj(dd->annots_array, lobj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the Annots array object...
|
||||||
|
pdfioObjClose(dd->annots_obj);
|
||||||
|
|
||||||
|
// Reset links...
|
||||||
|
dd->annots_array = NULL;
|
||||||
|
dd->annots_obj = NULL;
|
||||||
|
dd->num_links = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// 'find_image()' - Find an image in the document.
|
// 'find_image()' - Find an image in the document.
|
||||||
//
|
//
|
||||||
@ -584,6 +698,7 @@ format_block(docdata_t *dd, // I - Document data
|
|||||||
frag->height = text ? fsize : height;
|
frag->height = text ? fsize : height;
|
||||||
frag->imagenum = imagenum;
|
frag->imagenum = imagenum;
|
||||||
frag->text = text;
|
frag->text = text;
|
||||||
|
frag->url = url;
|
||||||
frag->ws = ws;
|
frag->ws = ws;
|
||||||
frag->font = font;
|
frag->font = font;
|
||||||
frag->color = color;
|
frag->color = color;
|
||||||
@ -743,6 +858,18 @@ format_doc(docdata_t *dd, // I - Document data
|
|||||||
dd->heading = mmdCopyAllText(current);
|
dd->heading = mmdCopyAllText(current);
|
||||||
|
|
||||||
format_block(dd, current, DOCFONT_BOLD, heading_sizes[curtype - MMD_TYPE_HEADING_1], left, right, /*leader*/NULL);
|
format_block(dd, current, DOCFONT_BOLD, heading_sizes[curtype - MMD_TYPE_HEADING_1], left, right, /*leader*/NULL);
|
||||||
|
|
||||||
|
if (dd->num_targets < DOCTARGET_MAX)
|
||||||
|
{
|
||||||
|
doctarget_t *t = dd->targets + dd->num_targets;
|
||||||
|
// New target
|
||||||
|
|
||||||
|
make_target_name(t->name, dd->heading, sizeof(t->name));
|
||||||
|
t->page = pdfioFileGetNumPages(dd->pdf) - 1;
|
||||||
|
t->y = dd->y + heading_sizes[curtype - MMD_TYPE_HEADING_1] * LINE_HEIGHT;
|
||||||
|
|
||||||
|
dd->num_targets ++;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MMD_TYPE_PARAGRAPH :
|
case MMD_TYPE_PARAGRAPH :
|
||||||
@ -895,6 +1022,33 @@ format_table(docdata_t *dd, // I - Document data
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// 'make_target_name()' - Convert text to a target name.
|
||||||
|
//
|
||||||
|
|
||||||
|
static void
|
||||||
|
make_target_name(char *dst, // I - Destination buffer
|
||||||
|
const char *src, // I - Source text
|
||||||
|
size_t dstsize) // I - Size of destination buffer
|
||||||
|
{
|
||||||
|
char *dstptr = dst, // Pointer into destination
|
||||||
|
*dstend = dst + dstsize - 1; // End of destination
|
||||||
|
|
||||||
|
|
||||||
|
while (*src && dstptr < dstend)
|
||||||
|
{
|
||||||
|
if (isalnum(*src) || *src == '.' || *src == '-')
|
||||||
|
*dstptr++ = tolower(*src);
|
||||||
|
else if (*src == ' ')
|
||||||
|
*dstptr++ = '-';
|
||||||
|
|
||||||
|
src ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*dstptr = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// 'measure_cell()' - Measure the dimensions of a table cell.
|
// 'measure_cell()' - Measure the dimensions of a table cell.
|
||||||
//
|
//
|
||||||
@ -1076,15 +1230,23 @@ new_page(docdata_t *dd) // I - Document data
|
|||||||
|
|
||||||
|
|
||||||
// Close the current page...
|
// Close the current page...
|
||||||
|
if (dd->st)
|
||||||
|
{
|
||||||
pdfioStreamClose(dd->st);
|
pdfioStreamClose(dd->st);
|
||||||
|
add_links(dd);
|
||||||
|
}
|
||||||
|
|
||||||
// Prep the new page...
|
// Prep the new page...
|
||||||
page_dict = pdfioDictCreate(dd->pdf);
|
page_dict = pdfioDictCreate(dd->pdf);
|
||||||
|
dd->annots_array = pdfioArrayCreate(dd->pdf);
|
||||||
|
dd->annots_obj = pdfioFileCreateArrayObj(dd->pdf, dd->annots_array);
|
||||||
|
|
||||||
pdfioDictSetRect(page_dict, "MediaBox", &dd->media_box);
|
pdfioDictSetRect(page_dict, "MediaBox", &dd->media_box);
|
||||||
// pdfioDictSetRect(page_dict, "CropBox", &dd->crop_box);
|
// pdfioDictSetRect(page_dict, "CropBox", &dd->crop_box);
|
||||||
pdfioDictSetRect(page_dict, "ArtBox", &dd->art_box);
|
pdfioDictSetRect(page_dict, "ArtBox", &dd->art_box);
|
||||||
|
|
||||||
|
pdfioDictSetObj(page_dict, "Annots", dd->annots_obj);
|
||||||
|
|
||||||
for (fontface = DOCFONT_REGULAR; fontface < DOCFONT_MAX; fontface ++)
|
for (fontface = DOCFONT_REGULAR; fontface < DOCFONT_MAX; fontface ++)
|
||||||
pdfioPageDictAddFont(page_dict, docfont_names[fontface], dd->fonts[fontface]);
|
pdfioPageDictAddFont(page_dict, docfont_names[fontface], dd->fonts[fontface]);
|
||||||
|
|
||||||
@ -1192,7 +1354,10 @@ render_line(docdata_t *dd, // I - Document data
|
|||||||
|
|
||||||
|
|
||||||
if (!dd->st)
|
if (!dd->st)
|
||||||
|
{
|
||||||
new_page(dd);
|
new_page(dd);
|
||||||
|
margin_top = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
dd->y -= margin_top + lineheight;
|
dd->y -= margin_top + lineheight;
|
||||||
if (dd->y < dd->art_box.y1)
|
if (dd->y < dd->art_box.y1)
|
||||||
@ -1248,6 +1413,40 @@ render_line(docdata_t *dd, // I - Document data
|
|||||||
pdfioContentTextShowf(dd->st, UNICODE_VALUE, " %s", frag->text);
|
pdfioContentTextShowf(dd->st, UNICODE_VALUE, " %s", frag->text);
|
||||||
else
|
else
|
||||||
pdfioContentTextShow(dd->st, UNICODE_VALUE, frag->text);
|
pdfioContentTextShow(dd->st, UNICODE_VALUE, frag->text);
|
||||||
|
|
||||||
|
if (frag->type == MMD_TYPE_LINKED_TEXT && frag->url && dd->num_links < DOCLINK_MAX)
|
||||||
|
{
|
||||||
|
doclink_t *l = dd->links + dd->num_links;
|
||||||
|
// Pointer to this link record
|
||||||
|
|
||||||
|
if (!strcmp(frag->url, "@"))
|
||||||
|
{
|
||||||
|
// Use mapped text as link target...
|
||||||
|
char targetlink[129]; // Targeted link
|
||||||
|
|
||||||
|
targetlink[0] = '#';
|
||||||
|
make_target_name(targetlink + 1, frag->text, sizeof(targetlink) - 1);
|
||||||
|
|
||||||
|
l->url = pdfioStringCreate(dd->pdf, targetlink);
|
||||||
|
}
|
||||||
|
else if (!strcmp(frag->url, "@@"))
|
||||||
|
{
|
||||||
|
// Use literal text as anchor...
|
||||||
|
l->url = pdfioStringCreatef(dd->pdf, "#%s", frag->text);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Use URL as-is...
|
||||||
|
l->url = frag->url;
|
||||||
|
}
|
||||||
|
|
||||||
|
l->box.x1 = frag->x;
|
||||||
|
l->box.y1 = dd->y;
|
||||||
|
l->box.x2 = frag->x + frag->width;
|
||||||
|
l->box.y2 = dd->y + frag->height;
|
||||||
|
|
||||||
|
dd->num_links ++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1391,3 +1590,42 @@ set_font(docdata_t *dd, // I - Document data
|
|||||||
dd->font = font;
|
dd->font = font;
|
||||||
dd->fsize = fsize;
|
dd->fsize = fsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// 'write_actions()' - Write remaining actions to the PDF file.
|
||||||
|
//
|
||||||
|
|
||||||
|
static void
|
||||||
|
write_actions(docdata_t *dd) // I - Document data
|
||||||
|
{
|
||||||
|
size_t i, j; // Looping vars
|
||||||
|
docaction_t *a; // Current action
|
||||||
|
doctarget_t *t; // Current target
|
||||||
|
|
||||||
|
|
||||||
|
for (i = 0, a = dd->actions; i < dd->num_actions; i ++, a ++)
|
||||||
|
{
|
||||||
|
for (j = 0, t = dd->targets; j < dd->num_targets; j ++, t ++)
|
||||||
|
{
|
||||||
|
if (!strcmp(a->target, t->name))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j < dd->num_targets)
|
||||||
|
{
|
||||||
|
pdfio_array_t *dest = pdfioArrayCreate(dd->pdf);
|
||||||
|
// Destination array
|
||||||
|
|
||||||
|
pdfioArrayAppendObj(dest, pdfioFileGetPage(dd->pdf, t->page));
|
||||||
|
pdfioArrayAppendName(dest, "XYZ");
|
||||||
|
pdfioArrayAppendNumber(dest, PAGE_LEFT);
|
||||||
|
pdfioArrayAppendNumber(dest, t->y);
|
||||||
|
pdfioArrayAppendNumber(dest, 0.0);
|
||||||
|
|
||||||
|
pdfioDictSetArray(pdfioObjGetDict(a->obj), "Dest", dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
pdfioObjClose(a->obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -12,16 +12,16 @@ pages in a PDF file. It demonstrates how to:
|
|||||||
- Format text,
|
- Format text,
|
||||||
- Embed JPEG and PNG images,
|
- Embed JPEG and PNG images,
|
||||||
- Add headers and footers, and
|
- Add headers and footers, and
|
||||||
- (Future) Add hyperlinks.
|
- Add hyperlinks.
|
||||||
|
|
||||||
|
|
||||||
Source Files
|
Source Files
|
||||||
------------
|
------------
|
||||||
|
|
||||||
The `md2pdf` program is organized into three source files: `md2pdf.c` which
|
The `md2pdf` program is organized into three source files: `md2pdf.c` which
|
||||||
contains the code to format the markdown content and `mmd.h` and `mmd.c` which
|
contains the code to format the markdown content and `mmd.h` and `mmd.c` (from
|
||||||
load the markdown content, which come from the [Miniature Markdown Library][MMD]
|
the [Miniature Markdown Library][MMD] project) which load the markdown content.
|
||||||
project which is provided under the terms of the Apache License v2.0.
|
|
||||||
![Apache License v2.0](apache-badge.png)
|
|
||||||
|
|
||||||
[MMD]: https://www.msweet.org/mmd/
|
[MMD]: https://www.msweet.org/mmd/
|
||||||
|
|
||||||
|
|
||||||
|
@ -544,7 +544,7 @@ mmdLoadIO(mmd_t *root, // I - Root node for document or `NULL` for a new d
|
|||||||
block = NULL;
|
block = NULL;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (*lineptr == '>' && (lineptr - linestart) < 4)
|
else if (stackptr->parent->type != MMD_TYPE_CODE_BLOCK && *lineptr == '>' && (lineptr - linestart) < 4)
|
||||||
{
|
{
|
||||||
// Block quote. See if there is an existing blockquote...
|
// Block quote. See if there is an existing blockquote...
|
||||||
DEBUG_printf(" BLOCKQUOTE (stackptr=%ld)\n", stackptr - stack);
|
DEBUG_printf(" BLOCKQUOTE (stackptr=%ld)\n", stackptr - stack);
|
||||||
|
Loading…
Reference in New Issue
Block a user