mirror of
https://github.com/michaelrsweet/pdfio.git
synced 2024-12-25 12:58:21 +01:00
Compare commits
3 Commits
a39b01ec9c
...
63cdb13b1b
Author | SHA1 | Date | |
---|---|---|---|
|
63cdb13b1b | ||
|
72e55b5bd1 | ||
|
dc65eb8d2f |
@ -20,6 +20,12 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#ifdef _WIN32
|
||||
# include <io.h>
|
||||
#else
|
||||
# include <unistd.h>
|
||||
#endif // _WIN32
|
||||
#include "mmd.h"
|
||||
#include <pdfio.h>
|
||||
#include <pdfio-content.h>
|
||||
@ -55,6 +61,14 @@ typedef struct docimage_s // Document image info
|
||||
|
||||
#define DOCIMAGE_MAX 1000 // Maximum number of images
|
||||
|
||||
typedef struct doclink_s // Document link info
|
||||
{
|
||||
const char *url; // Reference URL
|
||||
pdfio_rect_t box; // Link box
|
||||
} doclink_t;
|
||||
|
||||
#define DOCLINK_MAX 1000 // Maximum number of links/page
|
||||
|
||||
typedef struct docdata_s // Document formatting data
|
||||
{
|
||||
pdfio_file_t *pdf; // PDF file
|
||||
@ -68,9 +82,28 @@ typedef struct docdata_s // Document formatting data
|
||||
char *heading; // Current document heading
|
||||
pdfio_stream_t *st; // Current page stream
|
||||
double y; // Current position on page
|
||||
docfont_t font; // Current font
|
||||
double fsize; // Current font size
|
||||
doccolor_t color; // Current color
|
||||
pdfio_obj_t *annots; // Annotations object (for links)
|
||||
size_t num_links; // Number of links for this page
|
||||
doclink_t links[DOCLINK_MAX]; // Links for this page
|
||||
} docdata_t;
|
||||
|
||||
typedef struct linefrag_s // Line fragment
|
||||
{
|
||||
double x, // X position of item
|
||||
width, // Width of item
|
||||
height; // Height of item
|
||||
size_t imagenum; // Image number
|
||||
const char *text; // Text string
|
||||
bool ws; // Whitespace before text?
|
||||
docfont_t font; // Text font
|
||||
doccolor_t color; // Text color
|
||||
} linefrag_t;
|
||||
|
||||
#define LINEFRAG_MAX 200 // Maximum number of fragments on a line
|
||||
|
||||
|
||||
//
|
||||
// Macros...
|
||||
@ -129,13 +162,100 @@ static const char * const docfont_names[] =
|
||||
|
||||
|
||||
//
|
||||
// 'set_color()' - Set the stroke and fill color.
|
||||
// 'mmd_walk_next()' - Find the next markdown node.
|
||||
//
|
||||
|
||||
static mmd_t * // O - Next node or `NULL` at end
|
||||
mmd_walk_next(mmd_t *top, // I - Top node
|
||||
mmd_t *node) // I - Current node
|
||||
{
|
||||
mmd_t *next, // Next node
|
||||
*parent; // Parent node
|
||||
|
||||
|
||||
// Figure out the next node under "top"...
|
||||
if ((next = mmdGetFirstChild(node)) == NULL)
|
||||
{
|
||||
if ((next = mmdGetNextSibling(node)) == NULL)
|
||||
{
|
||||
if ((parent = mmdGetParent(node)) != top)
|
||||
{
|
||||
while ((next = mmdGetNextSibling(parent)) == NULL)
|
||||
{
|
||||
if ((parent = mmdGetParent(parent)) == top)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (next);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 'add_images()' - Scan the markdown document for images.
|
||||
//
|
||||
|
||||
static void
|
||||
add_images(docdata_t *dd, // I - Document data
|
||||
mmd_t *doc) // I - Markdown document
|
||||
{
|
||||
mmd_t *current, // Current node
|
||||
*next; // Next node
|
||||
|
||||
|
||||
// Scan the entire document for images...
|
||||
for (current = mmdGetFirstChild(doc); current; current = next)
|
||||
{
|
||||
// Get next node
|
||||
next = mmd_walk_next(doc, current);
|
||||
|
||||
// Look for image nodes...
|
||||
if (mmdGetType(current) == MMD_TYPE_IMAGE)
|
||||
{
|
||||
const char *url, // URL for image
|
||||
*ext; // Extension
|
||||
|
||||
url = mmdGetURL(current);
|
||||
ext = strrchr(url, '.');
|
||||
|
||||
fprintf(stderr, "IMAGE(%s), ext=\"%s\"\n", url, ext);
|
||||
|
||||
if (!access(url, 0) && ext && (!strcmp(ext, ".png") || !strcmp(ext, ".jpg") || !strcmp(ext, ".jpeg")))
|
||||
{
|
||||
// Local JPEG or PNG file, so add it if we haven't already...
|
||||
size_t i; // Looping var
|
||||
|
||||
for (i = 0; i < dd->num_images; i ++)
|
||||
{
|
||||
if (!strcmp(dd->images[i].url, url))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= dd->num_images && dd->num_images < DOCIMAGE_MAX)
|
||||
{
|
||||
dd->images[i].url = url;
|
||||
if ((dd->images[i].obj = pdfioFileCreateImageObjFromFile(dd->pdf, url, false)) != NULL)
|
||||
dd->num_images ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 'set_color()' - Set the stroke and fill color as needed.
|
||||
//
|
||||
|
||||
static void
|
||||
set_color(docdata_t *dd, // I - Document data
|
||||
doccolor_t color) // I - Document color
|
||||
{
|
||||
if (color == dd->color)
|
||||
return;
|
||||
|
||||
switch (color)
|
||||
{
|
||||
case DOCCOLOR_BLACK :
|
||||
@ -164,6 +284,28 @@ set_color(docdata_t *dd, // I - Document data
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 'set_font()' - Set the font typeface and size as needed.
|
||||
//
|
||||
|
||||
static void
|
||||
set_font(docdata_t *dd, // I - Document data
|
||||
docfont_t font, // I - Font
|
||||
double fsize) // I - Font size
|
||||
{
|
||||
if (font == dd->font && fabs(fsize - dd->fsize) < 0.1)
|
||||
return;
|
||||
|
||||
if (font == DOCFONT_MAX)
|
||||
return;
|
||||
|
||||
pdfioContentSetTextFont(dd->st, docfont_names[font], fsize);
|
||||
|
||||
dd->font = font;
|
||||
dd->fsize = fsize;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 'new_page()' - Start a new page.
|
||||
//
|
||||
@ -192,16 +334,17 @@ new_page(docdata_t *dd) // I - Document data
|
||||
pdfioPageDictAddFont(page_dict, docfont_names[fontface], dd->fonts[fontface]);
|
||||
|
||||
for (i = 0; i < dd->num_images; i ++)
|
||||
{
|
||||
snprintf(temp, sizeof(temp), "I%u", (unsigned)i);
|
||||
pdfioPageDictAddImage(page_dict, temp, dd->images[i].obj);
|
||||
}
|
||||
pdfioPageDictAddImage(page_dict, pdfioStringCreatef(dd->pdf, "I%u", (unsigned)i), dd->images[i].obj);
|
||||
|
||||
dd->st = pdfioFileCreatePage(dd->pdf, page_dict);
|
||||
dd->st = pdfioFileCreatePage(dd->pdf, page_dict);
|
||||
dd->color = DOCCOLOR_BLACK;
|
||||
dd->font = DOCFONT_MAX;
|
||||
dd->fsize = 0.0;
|
||||
dd->y = dd->art_box.y2;
|
||||
|
||||
// Add header/footer text
|
||||
set_color(dd, DOCCOLOR_GRAY);
|
||||
pdfioContentSetTextFont(dd->st, docfont_names[DOCFONT_REGULAR], SIZE_HEADFOOT);
|
||||
set_font(dd, DOCFONT_REGULAR, SIZE_HEADFOOT);
|
||||
|
||||
if (pdfioFileGetNumPages(dd->pdf) > 1 && dd->title)
|
||||
{
|
||||
@ -258,11 +401,81 @@ new_page(docdata_t *dd) // I - Document data
|
||||
pdfioContentTextShow(dd->st, UNICODE_VALUE, dd->heading);
|
||||
pdfioContentTextEnd(dd->st);
|
||||
}
|
||||
}
|
||||
|
||||
// The rest of the text will be full black...
|
||||
set_color(dd, DOCCOLOR_BLACK);
|
||||
|
||||
dd->y = dd->art_box.y2;
|
||||
//
|
||||
// 'render_line()' - Render a line of text/graphics.
|
||||
//
|
||||
|
||||
static void
|
||||
render_line(docdata_t *dd, // I - Document data
|
||||
double margin_top, // I - Top margin
|
||||
double lineheight, // I - Height of line
|
||||
size_t num_frags, // I - Number of line fragments
|
||||
linefrag_t *frags) // I - Line fragments
|
||||
{
|
||||
size_t i; // Looping var
|
||||
linefrag_t *frag; // Current line fragment
|
||||
bool in_text = false; // Are we in a text block?
|
||||
|
||||
|
||||
if (!dd->st)
|
||||
new_page(dd);
|
||||
|
||||
dd->y -= margin_top + lineheight;
|
||||
if (dd->y < dd->art_box.y1)
|
||||
{
|
||||
new_page(dd);
|
||||
|
||||
dd->y -= lineheight;
|
||||
}
|
||||
|
||||
fprintf(stderr, "num_frags=%u, y=%g\n", (unsigned)num_frags, dd->y);
|
||||
|
||||
for (i = 0, frag = frags; i < num_frags; i ++, frag ++)
|
||||
{
|
||||
if (frag->text)
|
||||
{
|
||||
// Draw text
|
||||
fprintf(stderr, " text=\"%s\", font=%d, color=%d, x=%g\n", frag->text, frag->font, frag->color, frag->x);
|
||||
|
||||
set_color(dd, frag->color);
|
||||
set_font(dd, frag->font, frag->height);
|
||||
|
||||
if (!in_text)
|
||||
{
|
||||
pdfioContentTextBegin(dd->st);
|
||||
pdfioContentTextMoveTo(dd->st, frag->x, dd->y);
|
||||
|
||||
in_text = true;
|
||||
}
|
||||
|
||||
if (frag->ws)
|
||||
pdfioContentTextShowf(dd->st, UNICODE_VALUE, " %s", frag->text);
|
||||
else
|
||||
pdfioContentTextShow(dd->st, UNICODE_VALUE, frag->text);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw image
|
||||
char imagename[32]; // Current image name
|
||||
|
||||
fprintf(stderr, " imagenum=%u, x=%g, width=%g, height=%g\n", (unsigned)frag->imagenum, frag->x, frag->width, frag->height);
|
||||
|
||||
if (in_text)
|
||||
{
|
||||
pdfioContentTextEnd(dd->st);
|
||||
in_text = false;
|
||||
}
|
||||
|
||||
snprintf(imagename, sizeof(imagename), "I%u", (unsigned)frag->imagenum);
|
||||
pdfioContentDrawImage(dd->st, imagename, frag->x, dd->y, frag->width, frag->height);
|
||||
}
|
||||
}
|
||||
|
||||
if (in_text)
|
||||
pdfioContentTextEnd(dd->st);
|
||||
}
|
||||
|
||||
|
||||
@ -273,8 +486,8 @@ new_page(docdata_t *dd) // I - Document data
|
||||
static void
|
||||
format_block(docdata_t *dd, // I - Document data
|
||||
mmd_t *block, // I - Block to format
|
||||
docfont_t fontface, // I - Default font
|
||||
double fontsize, // I - Size of font
|
||||
docfont_t deffont, // I - Default font
|
||||
double fsize, // I - Size of font
|
||||
double left, // I - Left margin
|
||||
double right, // I - Right margin
|
||||
const char *leader) // I - Leader text on the first line
|
||||
@ -282,185 +495,165 @@ format_block(docdata_t *dd, // I - Document data
|
||||
mmd_type_t blocktype; // Block type
|
||||
mmd_t *current, // Current node
|
||||
*next; // Next node
|
||||
mmd_type_t curtype; // Current node type
|
||||
const char *curtext, // Current text
|
||||
*cururl; // Current URL, if any
|
||||
bool curws; // Current whitespace
|
||||
docfont_t curface, // Current font face
|
||||
prevface; // Previous font face
|
||||
double x, y; // Current position
|
||||
double width, // Width of current fragment
|
||||
lwidth, // Leader width
|
||||
wswidth; // Width of whitespace
|
||||
doccolor_t color; // Color of text
|
||||
size_t num_frags; // Number of line fragments
|
||||
linefrag_t frags[LINEFRAG_MAX], // Line fragments
|
||||
*frag; // Current fragment
|
||||
mmd_type_t type; // Current node type
|
||||
const char *text, // Current text
|
||||
*url; // Current URL, if any
|
||||
bool ws; // Current whitespace
|
||||
pdfio_obj_t *image; // Current image, if any
|
||||
size_t imagenum; // Current image number
|
||||
doccolor_t color = DOCCOLOR_BLACK; // Current text color
|
||||
docfont_t font = deffont; // Current text font
|
||||
double x, // Current position
|
||||
width, // Width of current fragment
|
||||
wswidth, // Width of whitespace
|
||||
margin_top, // Top margin
|
||||
height, // Height of current fragment
|
||||
lineheight; // Height of current line
|
||||
|
||||
|
||||
blocktype = mmdGetType(block);
|
||||
|
||||
if (!dd->st)
|
||||
new_page(dd);
|
||||
|
||||
if ((y = dd->y - 2.0 * fontsize * LINE_HEIGHT) < dd->art_box.y1)
|
||||
{
|
||||
new_page(dd);
|
||||
y = dd->y - fontsize;
|
||||
}
|
||||
blocktype = mmdGetType(block);
|
||||
margin_top = fsize * LINE_HEIGHT;
|
||||
|
||||
if (leader)
|
||||
{
|
||||
// Add leader text on first line...
|
||||
pdfioContentSetTextFont(dd->st, docfont_names[prevface = fontface], fontsize);
|
||||
frags[0].width = pdfioContentTextMeasure(dd->fonts[deffont], leader, fsize);
|
||||
frags[0].height = fsize;
|
||||
frags[0].x = left - frags[0].width;
|
||||
frags[0].text = leader;
|
||||
frags[0].font = deffont;
|
||||
frags[0].color = DOCCOLOR_BLACK;
|
||||
|
||||
lwidth = pdfioContentTextMeasure(dd->fonts[fontface], leader, fontsize);
|
||||
|
||||
pdfioContentTextBegin(dd->st);
|
||||
pdfioContentTextMoveTo(dd->st, left - lwidth, y);
|
||||
pdfioContentTextShow(dd->st, UNICODE_VALUE, leader);
|
||||
num_frags = 1;
|
||||
lineheight = fsize * LINE_HEIGHT;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No leader text...
|
||||
prevface = DOCFONT_MAX;
|
||||
lwidth = 0.0;
|
||||
num_frags = 0;
|
||||
lineheight = 0.0;
|
||||
}
|
||||
|
||||
frag = frags + num_frags;
|
||||
|
||||
// Loop through the block and render lines...
|
||||
for (current = mmdGetFirstChild(block), x = left; current; current = next)
|
||||
{
|
||||
// Get information about the current node...
|
||||
curtype = mmdGetType(current);
|
||||
curtext = mmdGetText(current);
|
||||
cururl = mmdGetURL(current);
|
||||
curws = mmdGetWhitespace(current);
|
||||
|
||||
// fprintf(stderr, "current=%p, curtype=%d, curtext=\"%s\", cururl=\"%s\", curws=%s\n", (void *)current, curtype, curtext, cururl, curws ? "true" : "false");
|
||||
|
||||
// Figure out the next node under this block...
|
||||
if ((next = mmdGetFirstChild(current)) == NULL)
|
||||
{
|
||||
if ((next = mmdGetNextSibling(current)) == NULL)
|
||||
{
|
||||
mmd_t *parent; // Parent node
|
||||
|
||||
if ((parent = mmdGetParent(current)) != block)
|
||||
{
|
||||
while ((next = mmdGetNextSibling(parent)) == NULL)
|
||||
{
|
||||
if ((parent = mmdGetParent(parent)) == block)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
type = mmdGetType(current);
|
||||
text = mmdGetText(current);
|
||||
image = NULL;
|
||||
imagenum = 0;
|
||||
url = mmdGetURL(current);
|
||||
ws = mmdGetWhitespace(current);
|
||||
wswidth = 0.0;
|
||||
next = mmd_walk_next(block, current);
|
||||
|
||||
// Process the node...
|
||||
if (!curtext)
|
||||
if (type == MMD_TYPE_IMAGE && url)
|
||||
{
|
||||
// Embed an image
|
||||
size_t i; // Looping var
|
||||
|
||||
for (i = 0; i < dd->num_images; i ++)
|
||||
{
|
||||
if (!strcmp(dd->images[i].url, url))
|
||||
{
|
||||
image = dd->images[i].obj;
|
||||
imagenum = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!image)
|
||||
continue;
|
||||
|
||||
// Image - treat as 100dpi
|
||||
width = 72.0 * pdfioImageGetWidth(image) / 100.0;
|
||||
height = 72.0 * pdfioImageGetHeight(image) / 100.0;
|
||||
text = NULL;
|
||||
|
||||
if (width > (right - left))
|
||||
{
|
||||
// Too wide, scale to width...
|
||||
width = right - left;
|
||||
height = width * pdfioImageGetHeight(image) / pdfioImageGetWidth(image);
|
||||
}
|
||||
else if (height > (dd->art_box.y2 - dd->art_box.y1))
|
||||
{
|
||||
// Too tall, scale to height...
|
||||
height = dd->art_box.y2 - dd->art_box.y1;
|
||||
width = height * pdfioImageGetWidth(image) / pdfioImageGetHeight(image);
|
||||
}
|
||||
}
|
||||
else if (!text)
|
||||
{
|
||||
continue;
|
||||
|
||||
if (curtype == MMD_TYPE_EMPHASIZED_TEXT)
|
||||
curface = DOCFONT_ITALIC;
|
||||
else if (curtype == MMD_TYPE_STRONG_TEXT)
|
||||
curface = DOCFONT_BOLD;
|
||||
else if (curtype == MMD_TYPE_CODE_TEXT)
|
||||
curface = DOCFONT_MONOSPACE;
|
||||
}
|
||||
else
|
||||
curface = fontface;
|
||||
{
|
||||
// Text fragment...
|
||||
if (type == MMD_TYPE_EMPHASIZED_TEXT)
|
||||
font = DOCFONT_ITALIC;
|
||||
else if (type == MMD_TYPE_STRONG_TEXT)
|
||||
font = DOCFONT_BOLD;
|
||||
else if (type == MMD_TYPE_CODE_TEXT)
|
||||
font = DOCFONT_MONOSPACE;
|
||||
else
|
||||
font = deffont;
|
||||
|
||||
if (curtype == MMD_TYPE_CODE_TEXT)
|
||||
color = DOCCOLOR_RED;
|
||||
else if (curtype == MMD_TYPE_LINKED_TEXT)
|
||||
color = DOCCOLOR_BLUE;
|
||||
else
|
||||
color = DOCCOLOR_BLACK;
|
||||
if (type == MMD_TYPE_CODE_TEXT)
|
||||
color = DOCCOLOR_RED;
|
||||
else if (type == MMD_TYPE_LINKED_TEXT)
|
||||
color = DOCCOLOR_BLUE;
|
||||
else
|
||||
color = DOCCOLOR_BLACK;
|
||||
|
||||
width = pdfioContentTextMeasure(dd->fonts[curface], curtext, fontsize);
|
||||
if (curws)
|
||||
wswidth = pdfioContentTextMeasure(dd->fonts[curface], " ", fontsize);
|
||||
else
|
||||
width = pdfioContentTextMeasure(dd->fonts[font], text, fsize);
|
||||
height = fsize * LINE_HEIGHT;
|
||||
|
||||
if (ws)
|
||||
wswidth = pdfioContentTextMeasure(dd->fonts[font], " ", fsize);
|
||||
}
|
||||
|
||||
// See if this node will fit on the current line...
|
||||
if ((num_frags > 0 && (x + width + wswidth) >= right) || num_frags == LINEFRAG_MAX)
|
||||
{
|
||||
// No, render this line and start over...
|
||||
render_line(dd, margin_top, lineheight, num_frags, frags);
|
||||
|
||||
num_frags = 0;
|
||||
frag = frags;
|
||||
x = left;
|
||||
lineheight = 0.0;
|
||||
margin_top = 0.0;
|
||||
}
|
||||
|
||||
// Add the current node to the fragment list
|
||||
if (num_frags == 0)
|
||||
wswidth = 0.0;
|
||||
|
||||
if (x > left && (x + width + wswidth) >= right)
|
||||
{
|
||||
// New line...
|
||||
x = left;
|
||||
y -= fontsize * LINE_HEIGHT;
|
||||
frag->x = x;
|
||||
frag->width = width + wswidth;
|
||||
frag->height = text ? fsize : height;
|
||||
frag->imagenum = imagenum;
|
||||
frag->text = text;
|
||||
frag->ws = ws;
|
||||
frag->font = font;
|
||||
frag->color = color;
|
||||
|
||||
if (y < dd->art_box.y1)
|
||||
{
|
||||
// New page...
|
||||
if (prevface != DOCFONT_MAX)
|
||||
{
|
||||
pdfioContentTextEnd(dd->st);
|
||||
prevface = DOCFONT_MAX;
|
||||
}
|
||||
|
||||
new_page(dd);
|
||||
|
||||
y = dd->y - fontsize * LINE_HEIGHT;
|
||||
}
|
||||
else
|
||||
{
|
||||
pdfioContentTextMoveTo(dd->st, lwidth, -fontsize * LINE_HEIGHT);
|
||||
lwidth = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (curface != prevface)
|
||||
{
|
||||
if (prevface == DOCFONT_MAX)
|
||||
{
|
||||
pdfioContentTextBegin(dd->st);
|
||||
pdfioContentTextMoveTo(dd->st, x, y);
|
||||
}
|
||||
|
||||
pdfioContentSetTextFont(dd->st, docfont_names[prevface = curface], fontsize);
|
||||
}
|
||||
|
||||
if (color != dd->color)
|
||||
set_color(dd, color);
|
||||
|
||||
if (x > left && curws)
|
||||
{
|
||||
pdfioContentTextShowf(dd->st, UNICODE_VALUE, " %s", curtext);
|
||||
x += width + wswidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
pdfioContentTextShow(dd->st, UNICODE_VALUE, curtext);
|
||||
x += width;
|
||||
}
|
||||
|
||||
if (blocktype == MMD_TYPE_CODE_BLOCK)
|
||||
{
|
||||
// Force a new line...
|
||||
x = left;
|
||||
y -= fontsize * LINE_HEIGHT;
|
||||
|
||||
if (y < dd->art_box.y1)
|
||||
{
|
||||
// New page...
|
||||
if (prevface != DOCFONT_MAX)
|
||||
{
|
||||
pdfioContentTextEnd(dd->st);
|
||||
prevface = DOCFONT_MAX;
|
||||
}
|
||||
|
||||
new_page(dd);
|
||||
|
||||
y = dd->y - fontsize * LINE_HEIGHT;
|
||||
}
|
||||
else
|
||||
{
|
||||
pdfioContentTextMoveTo(dd->st, lwidth, -fontsize * LINE_HEIGHT);
|
||||
lwidth = 0.0;
|
||||
}
|
||||
}
|
||||
num_frags ++;
|
||||
frag ++;
|
||||
x += width + wswidth;
|
||||
if (height > lineheight)
|
||||
lineheight = height;
|
||||
}
|
||||
|
||||
// End the current text block and save out position on the page...
|
||||
if (prevface != DOCFONT_MAX)
|
||||
pdfioContentTextEnd(dd->st);
|
||||
|
||||
dd->y = y;
|
||||
if (num_frags > 0)
|
||||
render_line(dd, margin_top, lineheight, num_frags, frags);
|
||||
}
|
||||
|
||||
|
||||
@ -539,7 +732,29 @@ format_doc(docdata_t *dd, // I - Document data
|
||||
break;
|
||||
|
||||
case MMD_TYPE_CODE_BLOCK :
|
||||
format_block(dd, current, DOCFONT_MONOSPACE, SIZE_BODY, left + 36.0, right, /*leader*/NULL);
|
||||
{
|
||||
mmd_t *code; // Current code block
|
||||
linefrag_t frag; // Line fragment
|
||||
double margin_top; // Top margin
|
||||
|
||||
frag.x = left + 36.0;
|
||||
frag.width = 0.0;
|
||||
frag.height = SIZE_CODEBLOCK;
|
||||
frag.imagenum = 0;
|
||||
frag.ws = false;
|
||||
frag.font = DOCFONT_MONOSPACE;
|
||||
frag.color = DOCCOLOR_RED;
|
||||
margin_top = SIZE_CODEBLOCK * LINE_HEIGHT;
|
||||
|
||||
for (code = mmdGetFirstChild(current); code; code = mmdGetNextSibling(code))
|
||||
{
|
||||
frag.text = mmdGetText(code);
|
||||
|
||||
render_line(dd, margin_top, SIZE_CODEBLOCK * LINE_HEIGHT, 1, &frag);
|
||||
|
||||
margin_top = 0.0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -575,6 +790,8 @@ main(int argc, // I - Number of command-line arguments
|
||||
const char *value; // Metadata value
|
||||
|
||||
|
||||
setbuf(stderr, NULL);
|
||||
|
||||
// Get the markdown file from the command-line...
|
||||
if (argc != 2)
|
||||
{
|
||||
@ -632,6 +849,9 @@ main(int argc, // I - Number of command-line arguments
|
||||
return (1);
|
||||
}
|
||||
|
||||
// Add images...
|
||||
add_images(&dd, doc);
|
||||
|
||||
// Parse the markdown document...
|
||||
format_doc(&dd, doc, dd.art_box.x1, dd.art_box.x2);
|
||||
|
||||
|
235
examples/md2pdf.md
Normal file
235
examples/md2pdf.md
Normal file
@ -0,0 +1,235 @@
|
||||
---
|
||||
title: Mini-Markdown Test Document
|
||||
...
|
||||
|
||||
All heading levels are supported from 1 to 6, using both the ATX and Setext
|
||||
forms. As an indented code block:
|
||||
|
||||
# Heading 1
|
||||
## Heading 2
|
||||
### Heading 3
|
||||
#### Heading 4
|
||||
##### Heading 5
|
||||
###### Heading 6
|
||||
|
||||
Setext Heading 1
|
||||
================
|
||||
|
||||
Setext Heading 2
|
||||
----------------
|
||||
|
||||
As block headings:
|
||||
|
||||
# Heading 1
|
||||
## Heading 2
|
||||
### Heading 3
|
||||
#### Heading 4
|
||||
##### Heading 5
|
||||
###### Heading 6
|
||||
|
||||
Setext Heading 1
|
||||
================
|
||||
|
||||
Setext Heading 2
|
||||
----------------
|
||||
|
||||
And block quotes:
|
||||
|
||||
> # BQ Heading 1
|
||||
> ## BQ Heading 2
|
||||
> ### BQ Heading 3
|
||||
> #### BQ Heading 4
|
||||
> ##### BQ Heading 5
|
||||
> ###### BQ Heading 6
|
||||
>
|
||||
> Setext Heading 1
|
||||
> ================
|
||||
>
|
||||
> Setext Heading 2
|
||||
> ----------------
|
||||
|
||||
And ordered lists:
|
||||
|
||||
1. First item.
|
||||
|
||||
2. Second item.
|
||||
|
||||
3. Third item with very long text that wraps
|
||||
across multiple lines.
|
||||
|
||||
With a secondary paragraph associated with
|
||||
the third item.
|
||||
|
||||
And unordered lists:
|
||||
|
||||
- First item.
|
||||
|
||||
+ Second item.
|
||||
|
||||
* Third item.
|
||||
|
||||
* [ ] Fourth item (unchecked)
|
||||
|
||||
- [x] Fifth item (checked)
|
||||
|
||||
Code block with `\``:
|
||||
|
||||
```
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
puts("Hello, World!");
|
||||
return (0);
|
||||
}
|
||||
~~~
|
||||
```
|
||||
|
||||
Code block with `~`:
|
||||
|
||||
~~~
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
puts("Hello, World!");
|
||||
return (0);
|
||||
}
|
||||
```
|
||||
~~~
|
||||
|
||||
Link to [mmd web site](https://michaelrsweet.github.io/mmd).
|
||||
|
||||
Normal link to [Heading 1](@).
|
||||
|
||||
Code link to [`Heading 2`](@).
|
||||
|
||||
Inner emphasized link to [*Heading 3*](@).
|
||||
|
||||
Outer emphasized link to *[Heading 3](@)*.
|
||||
|
||||
Inner strong link to [**Heading 4**](@).
|
||||
|
||||
Outer strong link to **[Heading 4](@)**.
|
||||
|
||||
Implicit link to [reference1][].
|
||||
|
||||
Shortcut link to [reference1] without a link title.
|
||||
|
||||
[reference1]: https://michaelrsweet.github.io/mmd 'MMD Home Page'
|
||||
|
||||
[reference2]: https://michaelrsweet.github.io/mmd/mmd.html 'MMD Documentation'
|
||||
|
||||
[reference3]: https://michaelrsweet.github.io/mmd/mmd-160.png "MMD Logo"
|
||||
|
||||
Link to [mmd web site][reference1] works.
|
||||
Link to [mmd documentation][reference2] works.
|
||||
Link to ![mmd logo][reference3] image.
|
||||
Link to [bad reference][reference4] doesn't work.
|
||||
|
||||
Autolink to <https://michaelrsweet.github.io/mmd>.
|
||||
|
||||
Autolink in parenthesis (<https://michaelrsweet.github.io/mmd>).
|
||||
|
||||
[Link broken
|
||||
across two lines](https://michaelrsweet.github.io/mmd)
|
||||
|
||||
Color JPEG Image: ![Color JPEG Image](../testfiles/color.jpg)
|
||||
Grayscale JPEG Image: ![Grayscale JPEG Image](../testfiles/gray.jpg)
|
||||
Color PNG Image: ![Color PNG Image](../testfiles/pdfio-color.png)
|
||||
Grayscale PNG Image: ![Grayscale PNG Image](../testfiles/pdfio-gray.png)
|
||||
Indexed PNG Image: ![Indexed PNG Image](../testfiles/pdfio-indexed.png)
|
||||
|
||||
This sentence contains *Emphasized Text*, **Bold Text**, and `Code Text` for
|
||||
testing the MMD parser. The `<mmd.h>` header file.
|
||||
|
||||
This sentence contains _Emphasized Text_, __Bold Text__, and
|
||||
~~Strikethrough Text~~ for testing the MMD parser.
|
||||
|
||||
*Emphasized Text Split
|
||||
Across Two Lines*
|
||||
|
||||
**Bold Text Split
|
||||
Across Two Lines**
|
||||
|
||||
`Code Text Split
|
||||
Across Two lines`
|
||||
|
||||
_Emphasized Text Split
|
||||
Across Two Lines_
|
||||
|
||||
__Bold Text Split
|
||||
Across Two Lines__
|
||||
|
||||
~~Strikethrough Text Split
|
||||
Across Two Lines~~
|
||||
|
||||
All work and no play makes Johnny a dull boy.
|
||||
All work and no play makes Johnny a dull boy.
|
||||
All work and no play makes Johnny a dull boy.
|
||||
|
||||
All work and no play makes Johnny a dull boy.
|
||||
All work and no play makes Johnny a dull boy.
|
||||
All work and no play makes Johnny a dull boy.
|
||||
|
||||
\(Escaped Parenthesis)
|
||||
|
||||
\(*Emphasized Parenthesis*)
|
||||
|
||||
\(**Boldface Parenthesis**)
|
||||
|
||||
\(`Code Parenthesis`)
|
||||
|
||||
Escaped backtick (`\``)
|
||||
|
||||
Table as code:
|
||||
|
||||
| Heading 1 | Heading 2 | Heading 3 |
|
||||
| --------- | --------- | --------- |
|
||||
| Cell 1,1 | Cell 1,2 | Cell 1,3 |
|
||||
| Cell 2,1 | Cell 2,2 | Cell 2,3 |
|
||||
| Cell 3,1 | Cell 3,2 | Cell 3,3 |
|
||||
|
||||
Table with leading/trailing pipes:
|
||||
|
||||
| Heading 1 | Heading 2 | Heading 3 |
|
||||
| --------- | --------- | --------- |
|
||||
| Cell 1,1 | Cell 1,2 | Cell 1,3 |
|
||||
| Cell 2,1 | Cell 2,2 | Cell 2,3 |
|
||||
| Cell 3,1 | Cell 3,2 | Cell 3,3 |
|
||||
|
||||
Table without leading/trailing pipes:
|
||||
|
||||
Heading 1 | Heading 2 | Heading 3
|
||||
--------- | --------- | ---------
|
||||
Cell 1,1 | Cell 1,2 | Cell 1,3
|
||||
Cell 2,1 | Cell 2,2 | Cell 2,3
|
||||
Cell 3,1 | Cell 3,2 | Cell 3,3
|
||||
|
||||
Table with alignment:
|
||||
|
||||
Left Alignment | Center Alignment | Right Alignment
|
||||
:-------- | :-------: | --------:
|
||||
Cell 1,1 | Cell 1,2 | 1
|
||||
Cell 2,1 | Cell 2,2 | 12
|
||||
Cell 3,1 | Cell 3,2 | 123
|
||||
|
||||
Table in block quote:
|
||||
|
||||
> Heading 1 | Heading 2 | Heading 3
|
||||
> --------- | --------- | ---------
|
||||
> Cell 1,1 | Cell 1,2 | Cell 1,3
|
||||
> Cell 2,1 | Cell 2,2 | Cell 2,3
|
||||
> Cell 3,1 | Cell 3,2 | Cell 3,3
|
||||
|
||||
# Tests for Bugs/Edge Cases
|
||||
|
||||
Paragraph with "|" that should not
|
||||
be interpreted as a table.
|
||||
|
||||
code before a bulleted list
|
||||
|
||||
- First item
|
||||
- Second item
|
||||
- Some pathological nested link and inline style features supported by
|
||||
CommonMark like "`******Really Strong Text******`".
|
Loading…
Reference in New Issue
Block a user