Skip to content

Commit

Permalink
Save work on docos.
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelrsweet committed Dec 22, 2024
1 parent 52b508b commit 5dc68f3
Showing 1 changed file with 64 additions and 169 deletions.
233 changes: 64 additions & 169 deletions doc/pdfio.md
Original file line number Diff line number Diff line change
Expand Up @@ -1425,9 +1425,8 @@ Four functions handle the formatting of the markdown document:
- `format_table`: formats a table.

Formatted content is organized into arrays of `linefrag_t` and `tablerow_t`
structures for a line of content or row of table cells, respectively. These
are passed to the `render_line` and `render_row` functions to actually produce
content in the PDF document.
structures for a line of content or row of table cells, respectively.


#### High-Level Formatting

Expand Down Expand Up @@ -2037,196 +2036,92 @@ format_table(docdata_t *dd, // I - Document data

### Rendering the Markdown Document

The formatted content in arrays of `linefrag_t` and `tablerow_t` structures
are passed to the `render_line` and `render_row` functions respectively to
produce content in the PDF document.


#### Rendering a Line in a Paragraph, Heading, or Table Cell

The `render_line` function adds content from the `linefrag_t` array to a PDF
page. It starts by determining whether a new page is needed:

```c
static void
render_line(docdata_t *dd, // I - Document data
double margin_left, // I - Left margin
double margin_top, // I - Top margin
double need_bottom, // I - How much space is needed after
double lineheight, // I - Height of line
size_t num_frags, // I - Number of line fragments
linefrag_t *frags) // I - Line fragments
if (!dd->st)
{
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);
margin_top = 0.0;
}

dd->y -= margin_top + lineheight;
if ((dd->y - need_bottom) < dd->art_box.y1)
{
new_page(dd);

dd->y -= lineheight;
}

for (i = 0, frag = frags; i < num_frags; i ++, frag ++)
{
if (frag->type == MMD_TYPE_CHECKBOX)
{
// Draw checkbox...
set_color(dd, frag->color);

if (in_text)
{
pdfioContentTextEnd(dd->st);
in_text = false;
}

// Add box
pdfioContentPathRect(dd->st, frag->x + 1.0 + margin_left, dd->y, frag->width - 3.0, frag->height - 3.0);

if (frag->text)
{
// Add check
pdfioContentPathMoveTo(dd->st, frag->x + 3.0 + margin_left, dd->y + 2.0);
pdfioContentPathLineTo(dd->st, frag->x + frag->width - 4.0 + margin_left, dd->y + frag->height - 5.0);

pdfioContentPathMoveTo(dd->st, frag->x + 3.0 + margin_left, dd->y + frag->height - 5.0);
pdfioContentPathLineTo(dd->st, frag->x + frag->width - 4.0 + margin_left, dd->y + 2.0);
}

pdfioContentStroke(dd->st);
}
else if (frag->text)
{
// Draw text
if (!in_text)
{
pdfioContentTextBegin(dd->st);
pdfioContentTextMoveTo(dd->st, frag->x + margin_left, dd->y);

in_text = true;
}

if (frag->ws && frag->font == DOCFONT_MONOSPACE)
{
set_font(dd, DOCFONT_REGULAR, frag->height);
pdfioContentTextShow(dd->st, UNICODE_VALUE, " ");
}

set_color(dd, frag->color);
set_font(dd, frag->font, frag->height);

if (frag->font == DOCFONT_MONOSPACE)
pdfioContentTextShow(dd->st, UNICODE_VALUE, frag->text);
else
pdfioContentTextShowf(dd->st, UNICODE_VALUE, "%s%s", frag->ws ? " " : "", frag->text);

if (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
{
// Draw image
char imagename[32]; // Current image name

if (in_text)
{
pdfioContentTextEnd(dd->st);
in_text = false;
}
new_page(dd);
margin_top = 0.0;
}

snprintf(imagename, sizeof(imagename), "I%u", (unsigned)frag->imagenum);
pdfioContentDrawImage(dd->st, imagename, frag->x + margin_left, dd->y, frag->width, frag->height);
}
}
dd->y -= margin_top + lineheight;
if ((dd->y - need_bottom) < dd->art_box.y1)
{
new_page(dd);

if (in_text)
pdfioContentTextEnd(dd->st);
dd->y -= lineheight;
}
```

#### Rendering a Table Row
We then loops through the fragments for the current line, drawing checkboxes,
images, and text as needed. When a hyperlink is present, we add the link to the
`links` array in the `docdata_t` structure, mapping "@" and "@@" to an internal
link corresponding to the linked text:

```c
static void
render_row(docdata_t *dd, // I - Document data
size_t num_cols, // I - Number of columns
tablecol_t *cols, // I - Column information
tablerow_t *row) // I - Row
if (frag->url && dd->num_links < DOCLINK_MAX)
{
size_t col; // Current column
double row_y; // Start of row
docfont_t deffont; // Default font
doclink_t *l = dd->links + dd->num_links;
// Pointer to this link record

// Start a new page as needed...
if (!dd->st)
new_page(dd);
if (!strcmp(frag->url, "@"))
{
// Use mapped text as link target...
char targetlink[129]; // Targeted link

if ((dd->y - row->height) < dd->art_box.y1)
new_page(dd);
targetlink[0] = '#';
make_target_name(targetlink + 1, frag->text, sizeof(targetlink) - 1);

if (mmdGetType(row->cells[0]) == MMD_TYPE_TABLE_HEADER_CELL)
l->url = pdfioStringCreate(dd->pdf, targetlink);
}
else if (!strcmp(frag->url, "@@"))
{
// Header row, no border...
deffont = DOCFONT_BOLD;
// Use literal text as anchor...
l->url = pdfioStringCreatef(dd->pdf, "#%s", frag->text);
}
else
{
// Regular body row, add borders...
deffont = DOCFONT_REGULAR;
set_color(dd, DOCCOLOR_GRAY);
pdfioContentPathRect(dd->st, cols[0].left - TABLE_PADDING, dd->y - row->height, cols[num_cols - 1].right - cols[0].left + 2.0 * TABLE_PADDING, row->height);
for (col = 1; col < num_cols; col ++)
{
pdfioContentPathMoveTo(dd->st, cols[col].left - TABLE_PADDING, dd->y);
pdfioContentPathLineTo(dd->st, cols[col].left - TABLE_PADDING, dd->y - row->height);
}
pdfioContentStroke(dd->st);
// Use URL as-is...
l->url = frag->url;
}

row_y = dd->y;
l->box.x1 = frag->x;
l->box.y1 = dd->y;
l->box.x2 = frag->x + frag->width;
l->box.y2 = dd->y + frag->height;

for (col = 0; col < num_cols; col ++)
{
dd->y = row_y;
dd->num_links ++;
}
```

format_block(dd, row->cells[col], deffont, SIZE_TABLE, cols[col].left, cols[col].right, /*leader*/NULL);
}
These are later written as annotations in the `add_links` function.

dd->y = row_y - row->height;

#### Rendering a Table Row

The `render_row` function takes a row of cells and the corresponding column
definitions, draws the border boxes around body cells, and then formats each
cell using the `format_block` function described previously. The key is to
reset the page `y` value before formatting each cell:

```c
row_y = dd->y;

for (col = 0; col < num_cols; col ++)
{
dd->y = row_y;

format_block(dd, row->cells[col], deffont, SIZE_TABLE, cols[col].left, cols[col].right, /*leader*/NULL);
}

dd->y = row_y - row->height;
```

0 comments on commit 5dc68f3

Please sign in to comment.