23 Commits

Author SHA1 Message Date
6d65a609e5 Update documentation and examples makefile. 2024-12-26 15:12:56 -05:00
e96f9bfa6b Fix compiler warning and update Xcode project. 2024-12-23 15:07:32 -05:00
10c15fc281 Bump NuGet package versions. 2024-12-22 21:33:35 -05:00
fd8427d68a Add pdf2text example docos, install examples to doc directory. 2024-12-22 21:29:32 -05:00
ed1421287f Move pdfiototext to examples. 2024-12-22 19:00:17 -05:00
aa91b141a8 Finalize md2pdf example docos. 2024-12-22 12:09:03 -05:00
5dc68f3285 Save work on docos. 2024-12-21 23:20:36 -05:00
52b508bdd2 Block quote rendering changes. 2024-12-21 14:15:48 -05:00
41ebe39f3b Save work. 2024-12-21 14:04:27 -05:00
62df5f5c78 Add CODE_PADDING and use it for code blocks. 2024-12-21 12:16:36 -05:00
a1237db52c Use regular font for whitespace before monospace text. 2024-12-21 11:50:35 -05:00
a24fdee335 Fix an uninitialized pointer issue in format_block, and some margin issues on the top of the page. 2024-12-21 11:31:54 -05:00
e4081f2ba3 Add table-of-contents outline. 2024-12-20 07:18:10 -05:00
5bc7ebee2c Add actual example programs from prior examples, start documentation updates. 2024-12-19 16:43:21 -05:00
b872df5a1e Fix validation of date/time values (Issue #83) 2024-12-19 15:41:43 -05:00
53967552df Add some documentation on the code128 example.
Clean up the code128 code a bit and use the Helvetica font for the text.

Add support for writing to a PDF file on the command-line vs. just doing
redirection.
2024-12-15 22:52:03 -05:00
f8639fbd64 Add "need_bottom" argument to keep the following block on the same page. 2024-12-15 18:17:31 -05:00
9020e92928 Make sure we have room for at least two lines in a paragraph or code block at the bottom of the page. 2024-12-15 17:46:03 -05:00
48e6597337 Make block quote bar a thick orange so it stands out. 2024-12-15 17:35:16 -05:00
d4e3bbcf16 Doco updates. 2024-12-15 11:28:09 -05:00
2c8a996875 Add green bar next to block quote text (for notes). 2024-12-15 11:27:32 -05:00
3d6d9e3e3e Fix name of ZapfDingbats. 2024-12-15 11:27:18 -05:00
62fdf48ff9 Fix links for code text, background of code blocks. 2024-12-15 11:00:30 -05:00
20 changed files with 3961 additions and 375 deletions

4
.gitignore vendored
View File

@ -12,14 +12,16 @@
/configure~
/doc/pdfio.epub
/examples/code128
/examples/image2pdf
/examples/md2pdf
/examples/pdf2text
/examples/pdfioinfo
/Makefile
/packages
/pdfio.pc
/pdfio.xcodeproj/xcshareddata
/pdfio-*.tar.gz*
/pdfio-*.zip*
/pdfiototext
/testpdfio
/testpdfio-*.pdf
/testttf

View File

@ -14,6 +14,7 @@ v1.4.0 - YYYY-MM-DD
with `pdfioFileCreateFontObjFromBase` (Issue #84)
- Fixed reading of PDF files whose trailer is missing a newline (Issue #80)
- Fixed builds with some versions of VC++ (Issue #81)
- Fixed validation of date/time values (Issue #83)
v1.3.2 - 2024-08-15

View File

@ -103,15 +103,33 @@ LIBOBJS = \
ttf.o
OBJS = \
$(LIBOBJS) \
pdfiototext.o \
testpdfio.o \
testttf.o
TARGETS = \
$(LIBPDFIO) \
$(LIBPDFIO_STATIC) \
pdfiototext \
testpdfio \
testttf
DOCFILES = \
doc/pdfio.html \
doc/pdfio-512.png \
LICENSE \
NOTICE
EXAMPLES = \
examples/Makefile \
examples/Roboto-Bold.ttf \
examples/Roboto-Italic.ttf \
examples/Roboto-Regular.ttf \
examples/RobotoMono-Regular.ttf \
examples/code128.c \
examples/code128.ttf \
examples/image2pdf.c \
examples/md2pdf.c \
examples/md2pdf.md \
examples/mmd.c \
examples/mmd.h \
examples/pdf2text.c \
examples/pdfioinfo.c
# Make everything
@ -152,8 +170,13 @@ install: $(TARGETS)
$(INSTALL) -c -m 644 pdfio.pc $(BUILDROOT)$(libdir)/pkgconfig
echo Installing documentation to $(BUILDROOT)$(datadir)/doc/pdfio...
$(INSTALL) -d -m 755 $(BUILDROOT)$(datadir)/doc/pdfio
for file in doc/pdfio.html doc/pdfio-512.png LICENSE NOTICE; do \
$(INSTALL) -c -m 644 $$file $(BUILDROOT)$(datadir)/doc/pdfio; \
for file in $(DOCFILES); do \
$(INSTALL) -c -m 644 $$file $(BUILDROOT)$(datadir)/doc/pdfio; \
done
echo Installing examples to $(BUILDROOT)$(datadir)/doc/pdfio/examples...
$(INSTALL) -d -m 755 $(BUILDROOT)$(datadir)/doc/pdfio/examples
for file in $(EXAMPLES); do \
$(INSTALL) -c -m 644 $$file $(BUILDROOT)$(datadir)/doc/pdfio/examples; \
done
echo Installing man page to $(BUILDROOT)$(mandir)/man3...
$(INSTALL) -d -m 755 $(BUILDROOT)$(mandir)/man3
@ -201,12 +224,6 @@ pdfio1.def: $(LIBOBJS) Makefile
grep -v '^_ttf' | sed -e '1,$$s/^_//' | sort >>$@
# pdfio text extraction (demo, doesn't handle a lot of things yet)
pdfiototext: pdfiototext.o libpdfio.a
echo Linking $@...
$(CC) $(LDFLAGS) -o $@ pdfiototext.o libpdfio.a $(LIBS)
# pdfio test program
testpdfio: testpdfio.o libpdfio.a
echo Linking $@...

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -13,14 +13,18 @@
# Common options
CFLAGS = -g $(CPPFLAGS)
CPPFLAGS = -I..
LIBS = -L.. -lpdfio -lz
#CFLAGS = -g -fsanitize=address $(CPPFLAGS)
CPPFLAGS = -I.. -I/usr/local/include
LIBS = -L.. -L/usr/local/lib -lpdfio -lz
# Targets
TARGETS = \
code128 \
md2pdf
image2pdf \
md2pdf \
pdf2text \
pdfioinfo
# Make everything
@ -37,10 +41,25 @@ code128: code128.c
$(CC) $(CFLAGS) -o $@ code128.c $(LIBS)
# image2pdf
image2pdf: image2pdf.c
$(CC) $(CFLAGS) -o $@ image2pdf.c $(LIBS)
# md2pdf
md2pdf: md2pdf.c mmd.c mmd.h
$(CC) $(CFLAGS) -o $@ md2pdf.c mmd.c $(LIBS)
# pdfio text extraction (demo, doesn't handle a lot of things yet)
pdf2text: pdf2text.c
$(CC) $(CFLAGS) -o $@ pdf2text.c $(LIBS)
# pdfioinfo
pdfioinfo: pdfioinfo.c
$(CC) $(CFLAGS) -o $@ pdfioinfo.c $(LIBS)
# Common dependencies...
$(TARGETS): Makefile ../pdfio.h ../pdfio-content.h

View File

@ -23,7 +23,6 @@
// extended characters are ignored in the source string.
//
static char * // O - Output string
make_code128(char *dst, // I - Destination buffer
const char *src, // I - Source string
@ -54,9 +53,9 @@ make_code128(char *dst, // I - Destination buffer
static const char code128_start_code_a = '\313';
// Start code A
static const char code128_start_code_b = '\314';
// Start code A
// Start code B
static const char code128_start_code_c = '\315';
// Start code A
// Start code C
static const char code128_stop = '\316';
// Stop pattern
@ -149,7 +148,7 @@ main(int argc, // I - Number of command-line arguments
// Load fonts...
barcode_font = pdfioFileCreateFontObjFromFile(pdf, "code128.ttf", /*unicode*/false);
if (text)
text_font = pdfioFileCreateFontObjFromFile(pdf, "../testfiles/OpenSans-Regular.ttf", /*unicode*/true);
text_font = pdfioFileCreateFontObjFromBase(pdf, "Helvetica");
// Generate Code128 characters for the desired barcode...
if (!(barcode[0] & 0x80))
@ -182,7 +181,7 @@ main(int argc, // I - Number of command-line arguments
page_st = pdfioFileCreatePage(pdf, page_dict);
// Draw the page...
pdfioContentSetStrokeColorGray(page_st, 0.0);
pdfioContentSetFillColorGray(page_st, 0.0);
pdfioContentSetTextFont(page_st, "B128", barcode_height);
pdfioContentTextBegin(page_st);
@ -195,7 +194,7 @@ main(int argc, // I - Number of command-line arguments
pdfioContentSetTextFont(page_st, "TEXT", text_height);
pdfioContentTextBegin(page_st);
pdfioContentTextMoveTo(page_st, 0.5 * (media_box.x2 - text_width), 9.0);
pdfioContentTextShow(page_st, /*unicode*/true, text);
pdfioContentTextShow(page_st, /*unicode*/false, text);
pdfioContentTextEnd(page_st);
}

139
examples/image2pdf.c Normal file
View File

@ -0,0 +1,139 @@
//
// Image example for PDFio.
//
// Copyright © 2023-2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
// Usage:
//
// ./image2pdf FILENAME.{jpg,png} FILENAME.pdf ["TEXT"]
//
#include <pdfio.h>
#include <pdfio-content.h>
#include <string.h>
//
// 'create_pdf_image_file()' - Create a PDF file of an image with optional caption.
//
bool // O - True on success, false on failure
create_pdf_image_file(
const char *pdfname, // I - PDF filename
const char *imagename, // I - Image filename
const char *caption) // I - Caption filename
{
pdfio_file_t *pdf; // PDF file
pdfio_obj_t *font; // Caption font
pdfio_obj_t *image; // Image
pdfio_dict_t *dict; // Page dictionary
pdfio_stream_t *page; // Page stream
double width, height; // Width and height of image
double swidth, sheight; // Scaled width and height on page
double tx, ty; // Position on page
// Create the PDF file...
pdf = pdfioFileCreate(pdfname, /*version*/NULL, /*media_box*/NULL,
/*crop_box*/NULL, /*error_cb*/NULL,
/*error_cbdata*/NULL);
if (!pdf)
return (false);
// Create a Courier base font for the caption
font = pdfioFileCreateFontObjFromBase(pdf, "Courier");
if (!font)
{
pdfioFileClose(pdf);
return (false);
}
// Create an image object from the JPEG/PNG image file...
image = pdfioFileCreateImageObjFromFile(pdf, imagename, true);
if (!image)
{
pdfioFileClose(pdf);
return (false);
}
// Create a page dictionary with the font and image...
dict = pdfioDictCreate(pdf);
pdfioPageDictAddFont(dict, "F1", font);
pdfioPageDictAddImage(dict, "IM1", image);
// Create the page and its content stream...
page = pdfioFileCreatePage(pdf, dict);
// Position and scale the image on the page...
width = pdfioImageGetWidth(image);
height = pdfioImageGetHeight(image);
// Default media_box is "universal" 595.28x792 points (8.27x11in or
// 210x279mm). Use margins of 36 points (0.5in or 12.7mm) with another
// 36 points for the caption underneath...
swidth = 595.28 - 72.0;
sheight = swidth * height / width;
if (sheight > (792.0 - 36.0 - 72.0))
{
sheight = 792.0 - 36.0 - 72.0;
swidth = sheight * width / height;
}
tx = 0.5 * (595.28 - swidth);
ty = 0.5 * (792 - 36 - sheight);
pdfioContentDrawImage(page, "IM1", tx, ty + 36.0, swidth, sheight);
// Draw the caption in black...
pdfioContentSetFillColorDeviceGray(page, 0.0);
// Compute the starting point for the text - Courier is monospaced
// with a nominal width of 0.6 times the text height...
tx = 0.5 * (595.28 - 18.0 * 0.6 * strlen(caption));
// Position and draw the caption underneath...
pdfioContentTextBegin(page);
pdfioContentSetTextFont(page, "F1", 18.0);
pdfioContentTextMoveTo(page, tx, ty);
pdfioContentTextShow(page, /*unicode*/false, caption);
pdfioContentTextEnd(page);
// Close the page stream and the PDF file...
pdfioStreamClose(page);
pdfioFileClose(pdf);
return (true);
}
//
// 'main()' - Produce a single-page file from an image.
//
int // O - Exit status
main(int argc, // I - Number of command-line arguments
char *argv[]) // I - Command-line arguments
{
const char *imagefile, // Image filename
*pdffile, // PDF filename
*caption; // Caption text
// Get the image file, PDF file, and optional caption text from the command-line...
if (argc < 3 || argc > 4)
{
fputs("Usage: image2pdf FILENAME.{jpg,png} FILENAME.pdf [\"TEXT\"]\n", stderr);
return (1);
}
imagefile = argv[1];
pdffile = argv[2];
caption = argv[3];
return (create_pdf_image_file(imagefile, pdffile, caption) ? 0 : 1);
}

View File

@ -8,6 +8,7 @@
//
// Usage:
//
// ./md2pdf FILENAME.md FILENAME.pdf
// ./md2pdf FILENAME.md >FILENAME.pdf
//
// The generated PDF file is formatted for a "universal" paper size (8.27x11",
@ -40,7 +41,7 @@ typedef enum doccolor_e // Document color enumeration
{
DOCCOLOR_BLACK, // #000
DOCCOLOR_RED, // #900
DOCCOLOR_GREEN, // #090
DOCCOLOR_ORANGE, // #CC0
DOCCOLOR_BLUE, // #00C
DOCCOLOR_LTGRAY, // #EEE
DOCCOLOR_GRAY // #555
@ -88,17 +89,37 @@ 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
{
// State for the whole document
pdfio_file_t *pdf; // PDF file
pdfio_rect_t media_box; // Media (page) box
pdfio_rect_t crop_box; // Crop box (for margins)
pdfio_rect_t art_box; // Art box (for markdown content)
pdfio_obj_t *fonts[DOCFONT_MAX]; // Embedded fonts
double font_space; // Unit width of a space
size_t num_images; // Number of embedded images
docimage_t images[DOCIMAGE_MAX]; // Embedded images
const char *title; // Document title
char *heading; // Current document heading
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
size_t num_toc; // Number of table-of-contents entries
doctoc_t toc[DOCTOC_MAX]; // Table-of-contents entries
// State for the current page
pdfio_stream_t *st; // Current page stream
double y; // Current position on page
docfont_t font; // Current font
@ -108,10 +129,6 @@ typedef struct docdata_s // Document formatting data
pdfio_obj_t *annots_obj; // Annotations object (for links)
size_t num_links; // Number of 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;
typedef struct linefrag_s // Line fragment
@ -192,10 +209,17 @@ static const char * const docfont_names[] =
"FM"
};
#define BQ_PADDING 18.0 // Padding for block quotes
#define BQ_THICKNESS 3.0 // Thickness of block quote bar
#define CODE_PADDING 4.5 // Padding for code blocks
#define IMAGE_PPI 100.0 // Pixels per inch for images
#define LINE_HEIGHT 1.4 // Multiplier for line height
#define LIST_PADDING 36.0 // Padding/indentation for lists
#define SIZE_BODY 11.0 // Size of body text (points)
#define SIZE_CODEBLOCK 10.0 // Size of code block text (points)
#define SIZE_HEADFOOT 9.0 // Size of header/footer text (points)
@ -231,18 +255,19 @@ static void add_links(docdata_t *dd);
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_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, docfont_t deffont, 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 mmd_t *mmd_walk_next(mmd_t *top, mmd_t *node);
static void new_page(docdata_t *dd);
static ssize_t output_cb(void *output_cbdata, const void *buffer, size_t bytes);
static void render_line(docdata_t *dd, double margin_left, double margin_top, double lineheight, size_t num_frags, linefrag_t *frags);
static void render_line(docdata_t *dd, double margin_left, double need_bottom, double lineheight, size_t num_frags, linefrag_t *frags);
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_font(docdata_t *dd, docfont_t font, double fsize);
static void write_actions(docdata_t *dd);
static void write_toc(docdata_t *dd);
//
@ -262,9 +287,10 @@ main(int argc, // I - Number of command-line arguments
setbuf(stderr, NULL);
// Get the markdown file from the command-line...
if (argc != 2)
if (argc < 2 || argc > 3)
{
fputs("Usage: md2pdf FILENANE.md >FILENAME.pdf\n", stderr);
fputs("Usage: md2pdf FILENANE.md [FILENAME.pdf]\n", stderr);
fputs(" md2pdf FILENANE.md >FILENAME.pdf\n", stderr);
return (1);
}
@ -287,15 +313,23 @@ main(int argc, // I - Number of command-line arguments
dd.art_box.x2 = PAGE_RIGHT;
dd.art_box.y2 = PAGE_TOP;
dd.title = mmdGetMetadata(doc, "title");
if ((dd.title = mmdGetMetadata(doc, "title")) == NULL)
dd.art_box.y2 = PAGE_HEADER; // No header if there is no title
// Output a PDF file to the standard output...
if (argc == 2)
{
// Output a PDF file to the standard output...
#ifdef _WIN32
setmode(1, O_BINARY); // Force binary output on Windows
setmode(1, O_BINARY); // Force binary output on Windows
#endif // _WIN32
if ((dd.pdf = pdfioFileCreateOutput(output_cb, /*output_cbdata*/NULL, /*version*/NULL, /*media_box*/NULL, /*crop_box*/NULL, /*error_cb*/NULL, /*error_data*/NULL)) == NULL)
if ((dd.pdf = pdfioFileCreateOutput(output_cb, /*output_cbdata*/NULL, /*version*/NULL, /*media_box*/NULL, /*crop_box*/NULL, /*error_cb*/NULL, /*error_data*/NULL)) == NULL)
return (1);
}
else if ((dd.pdf = pdfioFileCreate(argv[2], /*version*/NULL, /*media_box*/NULL, /*crop_box*/NULL, /*error_cb*/NULL, /*error_data*/NULL)) == NULL)
{
return (1);
}
if ((value = mmdGetMetadata(doc, "author")) != NULL)
pdfioFileSetAuthor(dd.pdf, value);
@ -323,11 +357,13 @@ main(int argc, // I - Number of command-line arguments
#endif // USE_TRUETYPE
}
dd.font_space = pdfioContentTextMeasure(dd.fonts[DOCFONT_REGULAR], " ", 1.0);
// Add images...
add_images(&dd, doc);
// Parse the markdown document...
format_doc(&dd, doc, dd.art_box.x1, dd.art_box.x2);
format_doc(&dd, doc, DOCFONT_REGULAR, dd.art_box.x1, dd.art_box.x2);
// Close the PDF and return...
if (dd.st)
@ -335,7 +371,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);
@ -539,27 +580,31 @@ format_block(docdata_t *dd, // I - Document data
width, // Width of current fragment
wswidth, // Width of whitespace
margin_left, // Left margin
margin_top, // Top margin
need_bottom, // Space needed after this block
height, // Height of current fragment
lineheight; // Height of current line
blocktype = mmdGetType(block);
if ((blocktype >= MMD_TYPE_TABLE_HEADER_CELL && blocktype <= MMD_TYPE_TABLE_BODY_CELL_RIGHT) || blocktype == MMD_TYPE_LIST_ITEM)
margin_top = 0.0;
if (mmdGetNextSibling(block))
need_bottom = 3.0 * SIZE_BODY * LINE_HEIGHT;
else
margin_top = fsize * LINE_HEIGHT;
need_bottom = 0.0;
if (leader)
{
// Add leader text on first line...
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;
frags[0].type = MMD_TYPE_NORMAL_TEXT;
frags[0].width = pdfioContentTextMeasure(dd->fonts[deffont], leader, fsize);
frags[0].height = fsize;
frags[0].x = left - frags[0].width;
frags[0].imagenum = 0;
frags[0].text = leader;
frags[0].url = NULL;
frags[0].ws = false;
frags[0].font = deffont;
frags[0].color = DOCCOLOR_BLACK;
num_frags = 1;
lineheight = fsize * LINE_HEIGHT;
@ -583,7 +628,7 @@ format_block(docdata_t *dd, // I - Document data
imagenum = 0;
url = mmdGetURL(current);
ws = mmdGetWhitespace(current);
wswidth = 0.0;
wswidth = ws ? dd->font_space * fsize : 0.0;
next = mmd_walk_next(block, current);
// Process the node...
@ -620,13 +665,21 @@ format_block(docdata_t *dd, // I - Document data
else
margin_left = 0.0;
render_line(dd, margin_left, margin_top, lineheight, num_frags, frags);
render_line(dd, margin_left, need_bottom, lineheight, num_frags, frags);
num_frags = 0;
frag = frags;
x = left;
lineheight = 0.0;
margin_top = 0.0;
if (deffont == DOCFONT_ITALIC)
{
// Add an orange bar to the left of block quotes...
set_color(dd, DOCCOLOR_ORANGE);
pdfioContentPathRect(dd->st, left - BQ_PADDING, dd->y - (LINE_HEIGHT - 1.0) * fsize - BQ_THICKNESS, BQ_THICKNESS, lineheight + 2.0 * BQ_THICKNESS);
pdfioContentFill(dd->st, /*even_odd*/false);
}
num_frags = 0;
frag = frags;
x = left;
lineheight = 0.0;
need_bottom = 0.0;
continue;
}
@ -660,9 +713,6 @@ format_block(docdata_t *dd, // I - Document data
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...
@ -676,18 +726,31 @@ format_block(docdata_t *dd, // I - Document data
else
margin_left = 0.0;
render_line(dd, margin_left, margin_top, lineheight, num_frags, frags);
render_line(dd, margin_left, need_bottom, lineheight, num_frags, frags);
num_frags = 0;
frag = frags;
x = left;
lineheight = 0.0;
margin_top = 0.0;
num_frags = 0;
frag = frags;
x = left;
lineheight = 0.0;
need_bottom = 0.0;
if (deffont == DOCFONT_ITALIC)
{
// Add an orange bar to the left of block quotes...
set_color(dd, DOCCOLOR_ORANGE);
pdfioContentSave(dd->st);
pdfioContentSetLineWidth(dd->st, 3.0);
pdfioContentPathMoveTo(dd->st, left - 6.0, dd->y - (LINE_HEIGHT - 1.0) * fsize);
pdfioContentPathLineTo(dd->st, left - 6.0, dd->y + fsize);
pdfioContentStroke(dd->st);
pdfioContentRestore(dd->st);
}
}
// Add the current node to the fragment list
if (num_frags == 0)
{
// No leading whitespace at the start of the line
ws = false;
wswidth = 0.0;
}
@ -719,7 +782,19 @@ format_block(docdata_t *dd, // I - Document data
else
margin_left = 0.0;
render_line(dd, margin_left, margin_top, lineheight, num_frags, frags);
render_line(dd, margin_left, need_bottom, lineheight, num_frags, frags);
if (deffont == DOCFONT_ITALIC)
{
// Add an orange bar to the left of block quotes...
set_color(dd, DOCCOLOR_ORANGE);
pdfioContentSave(dd->st);
pdfioContentSetLineWidth(dd->st, 3.0);
pdfioContentPathMoveTo(dd->st, left - 6.0, dd->y - (LINE_HEIGHT - 1.0) * fsize);
pdfioContentPathLineTo(dd->st, left - 6.0, dd->y + fsize);
pdfioContentStroke(dd->st);
pdfioContentRestore(dd->st);
}
}
}
@ -738,19 +813,27 @@ format_code(docdata_t *dd, // I - Document data
double lineheight; // Line height
// Compute line height...
lineheight = SIZE_CODEBLOCK * LINE_HEIGHT;
// Start a new page as needed...
if (!dd->st)
new_page(dd);
lineheight = SIZE_CODEBLOCK * LINE_HEIGHT;
dd->y -= 2.0 * lineheight;
if (dd->y < dd->art_box.y1)
dd->y -= lineheight + CODE_PADDING;
if ((dd->y - lineheight) < dd->art_box.y1)
{
new_page(dd);
dd->y -= lineheight;
dd->y -= lineheight + CODE_PADDING;
}
// Draw the top padding...
set_color(dd, DOCCOLOR_LTGRAY);
pdfioContentPathRect(dd->st, left - CODE_PADDING, dd->y + SIZE_CODEBLOCK, right - left + 2.0 * CODE_PADDING, CODE_PADDING);
pdfioContentFillAndStroke(dd->st, false);
// Start a code text block...
set_font(dd, DOCFONT_MONOSPACE, SIZE_CODEBLOCK);
pdfioContentTextBegin(dd->st);
@ -759,7 +842,7 @@ format_code(docdata_t *dd, // I - Document data
for (code = mmdGetFirstChild(block); code; code = mmdGetNextSibling(code))
{
set_color(dd, DOCCOLOR_LTGRAY);
pdfioContentPathRect(dd->st, left - 3.0, dd->y - (LINE_HEIGHT - 1.0) * SIZE_CODEBLOCK, right - left + 3.0, lineheight);
pdfioContentPathRect(dd->st, left - CODE_PADDING, dd->y - (LINE_HEIGHT - 1.0) * SIZE_CODEBLOCK, right - left + 2.0 * CODE_PADDING, lineheight);
pdfioContentFillAndStroke(dd->st, false);
set_color(dd, DOCCOLOR_RED);
@ -784,6 +867,12 @@ format_code(docdata_t *dd, // I - Document data
// End the current text block...
pdfioContentTextEnd(dd->st);
dd->y += lineheight;
// Draw the bottom padding...
set_color(dd, DOCCOLOR_LTGRAY);
pdfioContentPathRect(dd->st, left - CODE_PADDING, dd->y - CODE_PADDING - (LINE_HEIGHT - 1.0) * SIZE_CODEBLOCK, right - left + 2.0 * CODE_PADDING, CODE_PADDING);
pdfioContentFillAndStroke(dd->st, false);
}
@ -794,6 +883,7 @@ format_code(docdata_t *dd, // I - Document data
static void
format_doc(docdata_t *dd, // I - Document data
mmd_t *doc, // I - Document node to format
docfont_t deffont, // I - Default font
double left, // I - Left margin
double right) // I - Right margin
{
@ -822,27 +912,31 @@ format_doc(docdata_t *dd, // I - Document data
default :
break;
case MMD_TYPE_THEMATIC_BREAK :
// Force a page break
dd->y = dd->art_box.y1;
break;
case MMD_TYPE_BLOCK_QUOTE :
format_doc(dd, current, left + 36.0, right - 36.0);
format_doc(dd, current, DOCFONT_ITALIC, left + BQ_PADDING, right - BQ_PADDING);
break;
case MMD_TYPE_ORDERED_LIST :
case MMD_TYPE_UNORDERED_LIST :
if (dd->st)
dd->y -= SIZE_BODY * LINE_HEIGHT;
dd->y -= SIZE_BODY * LINE_HEIGHT;
format_doc(dd, current, left + 36.0, right);
format_doc(dd, current, deffont, left + LIST_PADDING, right);
break;
case MMD_TYPE_LIST_ITEM :
if (doctype == MMD_TYPE_ORDERED_LIST)
{
snprintf(leader, sizeof(leader), "%d. ", i);
format_block(dd, current, DOCFONT_REGULAR, SIZE_BODY, left, right, leader);
format_block(dd, current, deffont, SIZE_BODY, left, right, leader);
}
else
{
format_block(dd, current, DOCFONT_REGULAR, SIZE_BODY, left, right, /*leader*/"");
format_block(dd, current, deffont, SIZE_BODY, left, right, /*leader*/"");
}
break;
@ -852,13 +946,41 @@ 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);
// Update the current heading
free(dd->heading);
dd->heading = mmdCopyAllText(current);
// Add a blank line before the heading...
dd->y -= heading_sizes[curtype - MMD_TYPE_HEADING_1] * LINE_HEIGHT;
// Format the heading...
format_block(dd, current, DOCFONT_BOLD, heading_sizes[curtype - MMD_TYPE_HEADING_1], left, right, /*leader*/NULL);
// Add the heading to the table-of-contents...
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 ++;
}
// Add the heading to the list of link targets...
if (dd->num_targets < DOCTARGET_MAX)
{
doctarget_t *t = dd->targets + dd->num_targets;
@ -873,15 +995,27 @@ format_doc(docdata_t *dd, // I - Document data
break;
case MMD_TYPE_PARAGRAPH :
format_block(dd, current, DOCFONT_REGULAR, SIZE_BODY, left, right, /*leader*/NULL);
// Add a blank line before the paragraph...
dd->y -= SIZE_BODY * LINE_HEIGHT;
// Format the paragraph...
format_block(dd, current, deffont, SIZE_BODY, left, right, /*leader*/NULL);
break;
case MMD_TYPE_TABLE :
// Add a blank line before the paragraph...
dd->y -= SIZE_BODY * LINE_HEIGHT;
// Format the table...
format_table(dd, current, left, right);
break;
case MMD_TYPE_CODE_BLOCK :
format_code(dd, current, left + 36.0, right - 36.0);
// Add a blank line before the code block...
dd->y -= SIZE_BODY * LINE_HEIGHT;
// Format the code block...
format_code(dd, current, left + CODE_PADDING, right - CODE_PADDING);
break;
}
}
@ -915,14 +1049,10 @@ format_table(docdata_t *dd, // I - Document data
// Find all of the rows and columns in the table...
num_cols = num_rows = 0;
memset(cols, 0, sizeof(cols));
memset(rows, 0, sizeof(rows));
rowptr = rows;
for (current = mmdGetFirstChild(table); current && num_rows < TABLEROW_MAX; current = next)
for (num_cols = 0, num_rows = 0, rowptr = rows, current = mmdGetFirstChild(table); current && num_rows < TABLEROW_MAX; current = next)
{
next = mmd_walk_next(table, current);
type = mmdGetType(current);
@ -1014,9 +1144,6 @@ format_table(docdata_t *dd, // I - Document data
}
// Render each table row...
if (dd->st)
dd->y -= SIZE_TABLE * LINE_HEIGHT;
for (row = 0, rowptr = rows; row < num_rows; row ++, rowptr ++)
render_row(dd, num_cols, cols, rowptr);
}
@ -1238,15 +1365,15 @@ new_page(docdata_t *dd) // I - Document data
// Prep the new page...
page_dict = pdfioDictCreate(dd->pdf);
dd->annots_array = pdfioArrayCreate(dd->pdf);
dd->annots_obj = pdfioFileCreateArrayObj(dd->pdf, dd->annots_array);
pdfioDictSetObj(page_dict, "Annots", dd->annots_obj);
pdfioDictSetRect(page_dict, "MediaBox", &dd->media_box);
// pdfioDictSetRect(page_dict, "CropBox", &dd->crop_box);
pdfioDictSetRect(page_dict, "ArtBox", &dd->art_box);
pdfioDictSetObj(page_dict, "Annots", dd->annots_obj);
for (fontface = DOCFONT_REGULAR; fontface < DOCFONT_MAX; fontface ++)
pdfioPageDictAddFont(page_dict, docfont_names[fontface], dd->fonts[fontface]);
@ -1343,7 +1470,7 @@ output_cb(void *output_cbdata, // I - Callback data (not used)
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
@ -1354,13 +1481,10 @@ render_line(docdata_t *dd, // I - Document data
if (!dd->st)
{
new_page(dd);
margin_top = 0.0;
}
dd->y -= margin_top + lineheight;
if (dd->y < dd->art_box.y1)
dd->y -= lineheight;
if ((dd->y - need_bottom) < dd->art_box.y1)
{
new_page(dd);
@ -1398,9 +1522,6 @@ render_line(docdata_t *dd, // I - Document data
else if (frag->text)
{
// Draw text
set_color(dd, frag->color);
set_font(dd, frag->font, frag->height);
if (!in_text)
{
pdfioContentTextBegin(dd->st);
@ -1409,12 +1530,21 @@ render_line(docdata_t *dd, // I - Document data
in_text = true;
}
if (frag->ws)
pdfioContentTextShowf(dd->st, UNICODE_VALUE, " %s", frag->text);
else
pdfioContentTextShow(dd->st, UNICODE_VALUE, frag->text);
if (frag->ws && frag->font == DOCFONT_MONOSPACE)
{
set_font(dd, DOCFONT_REGULAR, frag->height);
pdfioContentTextShow(dd->st, UNICODE_VALUE, " ");
}
if (frag->type == MMD_TYPE_LINKED_TEXT && frag->url && dd->num_links < DOCLINK_MAX)
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
@ -1545,9 +1675,9 @@ set_color(docdata_t *dd, // I - Document data
pdfioContentSetFillColorDeviceRGB(dd->st, 0.6, 0.0, 0.0);
pdfioContentSetStrokeColorDeviceRGB(dd->st, 0.6, 0.0, 0.0);
break;
case DOCCOLOR_GREEN :
pdfioContentSetFillColorDeviceRGB(dd->st, 0.0, 0.6, 0.0);
pdfioContentSetStrokeColorDeviceRGB(dd->st, 0.0, 0.6, 0.0);
case DOCCOLOR_ORANGE :
pdfioContentSetFillColorDeviceRGB(dd->st, 1.0, 0.5, 0.0);
pdfioContentSetStrokeColorDeviceRGB(dd->st, 1.0, 0.5, 0.0);
break;
case DOCCOLOR_BLUE :
pdfioContentSetFillColorDeviceRGB(dd->st, 0.0, 0.0, 0.8);
@ -1629,3 +1759,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);
}

View File

@ -1,22 +1,10 @@
---
title: Markdown to PDF Converter Example
title: Markdown to PDF Converter Test File
...
Markdown to PDF Converter Example
=================================
The `md2pdf` example program reads a markdown file and formats the content onto
pages in a PDF file. It demonstrates how to:
- Embed base and TrueType fonts,
- Format text,
- Embed JPEG and PNG images,
- Add headers and footers, and
- Add hyperlinks.
Source Files
------------
Markdown to PDF Converter Test File
===================================
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` (from
@ -24,4 +12,73 @@ the [Miniature Markdown Library][MMD] project) which load the markdown content.
[MMD]: https://www.msweet.org/mmd/
This is a test file for `md2pdf`. Here is a bullet list:
- Embed base and TrueType fonts,
- Format text with embedded JPEG and PNG images and check boxes, with support
for wrapping, alignment in table cells, leader text (as used for lists), and
variable line height,
- Add headers and footers, and
- Add hyperlinks and document platform.
And here is an ordered list:
1. Embed base and TrueType fonts,
2. Format text with embedded JPEG and PNG images and check boxes, with support
for wrapping, alignment in table cells, leader text (as used for lists), and
variable line height,
3. Add headers and footers, and
4. Add hyperlinks and document platform.
Code Blocks
-----------
```
0 1 2 3 4 5 6 7 8
12345678901234567890123456789012345678901234567890123456789012345678901234567890
```
Images
------
PDFio book cover image:
![PDFio](../doc/pdfio-epub.png)
Tables
------
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

View File

@ -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

View File

@ -1,17 +1,17 @@
//
// PDF to text program for PDFio.
//
// Copyright © 2022 by Michael R Sweet.
// Copyright © 2022-2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
// Usage:
//
// ./pdfiototext FILENAME.pdf > FILENAME.txt
// ./pdf2text FILENAME.pdf > FILENAME.txt
//
#include "pdfio.h"
#include <pdfio.h>
#include <string.h>
@ -36,16 +36,14 @@ main(int argc, // I - Number of command-line arguments
// Verify command-line arguments...
if (argc != 2)
{
puts("Usage: pdfiototext FILENAME.pdf > FILENAME.txt");
puts("Usage: pdf2text FILENAME.pdf > FILENAME.txt");
return (1);
}
// Open the PDF file...
if ((file = pdfioFileOpen(argv[1], NULL, NULL, NULL, NULL)) == NULL)
if ((file = pdfioFileOpen(argv[1], /*password_cb*/NULL, /*password_data*/NULL, /*error_cb*/NULL, /*error_data*/NULL)) == NULL)
return (1);
// printf("%s: %u pages\n", argv[1], (unsigned)pdfioFileGetNumPages(file));
// Try grabbing content from all of the pages...
for (i = 0, num_pages = pdfioFileGetNumPages(file); i < num_pages; i ++)
{
@ -54,29 +52,28 @@ main(int argc, // I - Number of command-line arguments
num_streams = pdfioPageGetNumStreams(obj);
// printf("%s: page%u=%p, num_streams=%u\n", argv[1], (unsigned)i, obj, (unsigned)num_streams);
for (j = 0; j < num_streams; j ++)
{
if ((st = pdfioPageOpenStream(obj, j, true)) == NULL)
continue;
// printf("%s: page%u st%u=%p\n", argv[1], (unsigned)i, (unsigned)j, st);
// Read PDF tokens from the page stream...
first = true;
while (pdfioStreamGetToken(st, buffer, sizeof(buffer)))
{
if (buffer[0] == '(')
{
// Text string using an 8-bit encoding
if (first)
first = false;
else
else if (buffer[1] != ' ')
putchar(' ');
fputs(buffer + 1, stdout);
}
else if (!strcmp(buffer, "Td") || !strcmp(buffer, "TD") || !strcmp(buffer, "T*") || !strcmp(buffer, "\'") || !strcmp(buffer, "\""))
{
// Text operators that advance to the next line in the block
putchar('\n');
first = true;
}

65
examples/pdfioinfo.c Normal file
View File

@ -0,0 +1,65 @@
//
// PDF metadata example for PDFio.
//
// Copyright © 2023-2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
// Usage:
//
// ./pdfioinfo FILENAME.pdf
//
#include <pdfio.h>
#include <time.h>
//
// 'main()' - Open a PDF file and show its metadata.
//
int // O - Exit status
main(int argc, // I - Number of command-line arguments
char *argv[]) // Command-line arguments
{
const char *filename; // PDF filename
pdfio_file_t *pdf; // PDF file
time_t creation_date; // Creation date
struct tm *creation_tm; // Creation date/time information
char creation_text[256]; // Creation date/time as a string
// Get the filename from the command-line...
if (argc != 2)
{
fputs("Usage: ./pdfioinfo FILENAME.pdf\n", stderr);
return (1);
}
filename = argv[1];
// Open the PDF file with the default callbacks...
pdf = pdfioFileOpen(filename, /*password_cb*/NULL,
/*password_cbdata*/NULL, /*error_cb*/NULL,
/*error_cbdata*/NULL);
if (pdf == NULL)
return (1);
// Get the creation date and convert to a string...
creation_date = pdfioFileGetCreationDate(pdf);
creation_tm = localtime(&creation_date);
strftime(creation_text, sizeof(creation_text), "%c", creation_tm);
// Print file information to stdout...
printf("%s:\n", filename);
printf(" Title: %s\n", pdfioFileGetTitle(pdf));
printf(" Author: %s\n", pdfioFileGetAuthor(pdf));
printf(" Created On: %s\n", creation_text);
printf(" Number Pages: %u\n", (unsigned)pdfioFileGetNumPages(pdf));
// Close the PDF file...
pdfioFileClose(pdf);
return (0);
}

View File

@ -284,7 +284,7 @@ static short times_roman_widths[256] =
};
static short zapf_dingbats_widths[256] =
static short zapfdingbats_widths[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

View File

@ -1132,7 +1132,7 @@ pdfioContentTextMeasure(
}
if (i < (sizeof(_pdfio_cp1252) / sizeof(_pdfio_cp1252[0])))
ch = i + 0x80; // Extra characters from 0x80 to 0x9f
ch = (int)(i + 0x80); // Extra characters from 0x80 to 0x9f
else
ch = '?'; // Unsupported chars map to ?
}
@ -1171,8 +1171,8 @@ pdfioContentTextMeasure(
widths = times_italic_widths;
else if (!strcmp(basefont, "Times-Roman"))
widths = times_roman_widths;
else if (!strcmp(basefont, "Zapf-Dingbats"))
widths = zapf_dingbats_widths;
else if (!strcmp(basefont, "ZapfDingbats"))
widths = zapfdingbats_widths;
else
return (0.0);

View File

@ -755,6 +755,8 @@ get_date_time(const char *s) // I - PDF date/time value
int offset; // Date offset
PDFIO_DEBUG("get_date_time(s=\"%s\")\n", s);
// Possible date value of the form:
//
// (D:YYYYMMDDhhmmssZ)
@ -772,10 +774,12 @@ get_date_time(const char *s) // I - PDF date/time value
{
if (s[i] == 'Z')
{
// UTC...
i ++;
}
else if (s[i] == '-' || s[i] == '+')
{
// Timezone offset from UTC...
if (isdigit(s[i + 1] & 255) && isdigit(s[i + 2] & 255) && s[i + 3] == '\'' && isdigit(s[i + 4] & 255) && isdigit(s[i + 5] & 255))
{
i += 6;
@ -783,6 +787,11 @@ get_date_time(const char *s) // I - PDF date/time value
i ++;
}
}
else if (!s[i])
{
// Missing zone info, invalid date string...
return (0);
}
}
if (s[i])

View File

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@ -31,6 +31,7 @@
27F2F0612710BE92008ECD36 /* pdfio-rc4.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F2F05E2710BE92008ECD36 /* pdfio-rc4.c */; };
27F2F0622710BE92008ECD36 /* pdfio-crypto.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F2F05F2710BE92008ECD36 /* pdfio-crypto.c */; };
27F2F0642711243D008ECD36 /* pdfio-sha256.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F2F0632711243D008ECD36 /* pdfio-sha256.c */; };
27FCBDE42D19F9B300485EEE /* pdfio-base-font-widths.h in Headers */ = {isa = PBXBuildFile; fileRef = 27FCBDE32D19F9B300485EEE /* pdfio-base-font-widths.h */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -89,6 +90,7 @@
27F2F05E2710BE92008ECD36 /* pdfio-rc4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-rc4.c"; sourceTree = "<group>"; };
27F2F05F2710BE92008ECD36 /* pdfio-crypto.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-crypto.c"; sourceTree = "<group>"; };
27F2F0632711243D008ECD36 /* pdfio-sha256.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-sha256.c"; sourceTree = "<group>"; };
27FCBDE32D19F9B300485EEE /* pdfio-base-font-widths.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "pdfio-base-font-widths.h"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -162,6 +164,7 @@
children = (
27CF90432711DFFE00E50FE4 /* pdfio-aes.c */,
273440BA263D727800FBFD63 /* pdfio-array.c */,
27FCBDE32D19F9B300485EEE /* pdfio-base-font-widths.h */,
273440BB263D727800FBFD63 /* pdfio-common.c */,
271EA703265B2B1000ACDD39 /* pdfio-content.c */,
27F2F05F2710BE92008ECD36 /* pdfio-crypto.c */,
@ -205,6 +208,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
27FCBDE42D19F9B300485EEE /* pdfio-base-font-widths.h in Headers */,
273440CC263D727800FBFD63 /* pdfio.h in Headers */,
271EA706265B2B1000ACDD39 /* pdfio-content.h in Headers */,
273440C3263D727800FBFD63 /* pdfio-private.h in Headers */,
@ -256,7 +260,8 @@
273440A8263D6FE200FBFD63 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1600;
TargetAttributes = {
273440AF263D6FE200FBFD63 = {
CreatedOnToolsVersion = 12.5;
@ -373,9 +378,11 @@
CODE_SIGN_IDENTITY = "Apple Development";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1.1.2;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
@ -450,15 +457,15 @@
CODE_SIGN_IDENTITY = "Apple Development";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1.1.2;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
);
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
@ -486,6 +493,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = RU58A2256H;
EXECUTABLE_PREFIX = lib;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -497,6 +505,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = RU58A2256H;
EXECUTABLE_PREFIX = lib;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -509,6 +518,7 @@
buildSettings = {
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
GCC_DYNAMIC_NO_PIC = NO;
@ -525,6 +535,7 @@
buildSettings = {
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
MACOSX_DEPLOYMENT_TARGET = 11.0;

View File

@ -3,7 +3,7 @@
<metadata>
<id>pdfio_native</id>
<title>PDFio Library for VS2019+</title>
<version>1.3.2</version>
<version>1.4.0</version>
<authors>Michael R Sweet</authors>
<owners>michaelrsweet</owners>
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
@ -16,7 +16,7 @@
<copyright>Copyright © 2019-2024 by Michael R Sweet</copyright>
<tags>pdf file native</tags>
<dependencies>
<dependency id="pdfio_native.redist" version="1.3.2" />
<dependency id="pdfio_native.redist" version="1.4.0" />
<dependency id="zlib_native.redist" version="1.2.11" />
</dependencies>
</metadata>

View File

@ -3,7 +3,7 @@
<metadata>
<id>pdfio_native.redist</id>
<title>PDFio Library for VS2019+</title>
<version>1.3.2</version>
<version>1.4.0</version>
<authors>Michael R Sweet</authors>
<owners>michaelrsweet</owners>
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>