Skip to content

Commit

Permalink
Added support for rotated text (#133)
Browse files Browse the repository at this point in the history
* Added support for rotated text - this necessitates the addition of `-lm` for sinf/cosf support.
  • Loading branch information
AndreRenaud authored Jul 16, 2023
1 parent 2352e5d commit 0beb206
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 34 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
make CC=/usr/lib/mxe/usr/bin/i686-w64-mingw32.static-gcc CLANG_FORMAT=clang-format-10
cp testprog testprog.exe
- name: Infer
run: make clean && infer run --no-progress-bar -- make CFLAGS="-g -Wall -pipe" LFLAGS=""
run: make clean && infer run --no-progress-bar -- make CFLAGS="-g -Wall -pipe" LFLAGS="-lm"
- name: Build
run: make clean && make
- name: Update coverage statistics
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
LFLAGS=-fprofile-arcs -ftest-coverage
LFLAGS=-fprofile-arcs -ftest-coverage -lm
CLANG=clang
CLANG_FORMAT=clang-format
XXD=xxd
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ All contained a single C-file with header and no external library dependencies.
Useful for embedding into other programs that require rudimentary PDF output.

Supports the following PDF features
* Text of various fonts/sizes/colours
* Text of various fonts/sizes/colours/rotation
* Primitive drawing elements
* Lines
* Rectangles
Expand Down
51 changes: 33 additions & 18 deletions pdfgen.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* It supports text, shapes, images etc...
* Capable of handling millions of objects without too much performance
* penalty.
* Public domain license - no warrenty implied; use at your own risk.
* Public domain license - no warranty implied; use at your own risk.
*/

/**
Expand Down Expand Up @@ -1374,7 +1374,7 @@ static int utf8_to_pdfencoding(struct pdf_doc *pdf, const char *utf8, int len,
if (code > 255) {
/* We support *some* minimal UTF-8 characters */
// See Appendix D of
// https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf
// https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/pdfreference1.7old.pdf
// These are all in WinAnsiEncoding
switch (code) {
case 0x160: // Latin Capital Letter S with Caron
Expand Down Expand Up @@ -1409,8 +1409,8 @@ static int utf8_to_pdfencoding(struct pdf_doc *pdf, const char *utf8, int len,
break;
default:
return pdf_set_err(pdf, -EINVAL,
"Unsupported UTF-8 character: 0x%x 0o%o", code,
code);
"Unsupported UTF-8 character: 0x%x 0o%o %s",
code, code, utf8);
}
} else {
*res = code;
Expand All @@ -1420,7 +1420,8 @@ static int utf8_to_pdfencoding(struct pdf_doc *pdf, const char *utf8, int len,

static int pdf_add_text_spacing(struct pdf_doc *pdf, struct pdf_object *page,
const char *text, float size, float xoff,
float yoff, uint32_t colour, float spacing)
float yoff, uint32_t colour, float spacing,
float angle)
{
int ret;
size_t len = text ? strlen(text) : 0;
Expand All @@ -1433,7 +1434,12 @@ static int pdf_add_text_spacing(struct pdf_doc *pdf, struct pdf_object *page,

dstr_append(&str, "BT ");
dstr_printf(&str, "/GS%d gs ", alpha);
dstr_printf(&str, "%f %f TD ", xoff, yoff);
if (angle != 0) {
dstr_printf(&str, "%f %f %f %f %f %f Tm ", cosf(angle), sinf(angle),
-sinf(angle), cosf(angle), xoff, yoff);
} else {
dstr_printf(&str, "%f %f TD ", xoff, yoff);
}
dstr_printf(&str, "/F%d %f Tf ", pdf->current_font->font.index, size);
dstr_printf(&str, "%f %f %f rg ", PDF_RGB_R(colour), PDF_RGB_G(colour),
PDF_RGB_B(colour));
Expand Down Expand Up @@ -1478,7 +1484,16 @@ int pdf_add_text(struct pdf_doc *pdf, struct pdf_object *page,
const char *text, float size, float xoff, float yoff,
uint32_t colour)
{
return pdf_add_text_spacing(pdf, page, text, size, xoff, yoff, colour, 0);
return pdf_add_text_spacing(pdf, page, text, size, xoff, yoff, colour, 0,
0);
}

int pdf_add_text_rotate(struct pdf_doc *pdf, struct pdf_object *page,
const char *text, float size, float xoff, float yoff,
float angle, uint32_t colour)
{
return pdf_add_text_spacing(pdf, page, text, size, xoff, yoff, colour, 0,
angle);
}

/* How wide is each character, in points, at size 14 */
Expand Down Expand Up @@ -1812,8 +1827,8 @@ static const char *find_word_break(const char *string)

int pdf_add_text_wrap(struct pdf_doc *pdf, struct pdf_object *page,
const char *text, float size, float xoff, float yoff,
uint32_t colour, float wrap_width, int align,
float *height)
float angle, uint32_t colour, float wrap_width,
int align, float *height)
{
/* Move through the text string, stopping at word boundaries,
* trying to find the longest text string we can fit in the given width
Expand Down Expand Up @@ -1913,7 +1928,7 @@ int pdf_add_text_wrap(struct pdf_doc *pdf, struct pdf_object *page,

if (align != PDF_ALIGN_NO_WRITE) {
pdf_add_text_spacing(pdf, page, line, size, xoff_align, yoff,
colour, char_spacing);
colour, char_spacing, angle);
}

if (*end == ' ')
Expand Down Expand Up @@ -1985,7 +2000,7 @@ int pdf_add_quadratic_bezier(struct pdf_doc *pdf, struct pdf_object *page,
}

int pdf_add_custom_path(struct pdf_doc *pdf, struct pdf_object *page,
struct pdf_path_operation *operations,
const struct pdf_path_operation *operations,
int operation_count, float stroke_width,
uint32_t stroke_colour, uint32_t fill_colour)
{
Expand Down Expand Up @@ -2632,7 +2647,7 @@ static int pdf_add_barcode_ean13(struct pdf_doc *pdf, struct pdf_object *page,

for (int i = 0; i != 6; i++) {
text[0] = *string;
e = pdf_add_text_wrap(pdf, page, text, font, x, y, colour,
e = pdf_add_text_wrap(pdf, page, text, font, x, y, 0, colour,
7 * x_width, PDF_ALIGN_CENTER, NULL);
if (e < 0) {
pdf_set_font(pdf, save_font);
Expand All @@ -2659,7 +2674,7 @@ static int pdf_add_barcode_ean13(struct pdf_doc *pdf, struct pdf_object *page,

for (int i = 0; i != 6; i++) {
text[0] = *string;
e = pdf_add_text_wrap(pdf, page, text, font, x, y, colour,
e = pdf_add_text_wrap(pdf, page, text, font, x, y, 0, colour,
7 * x_width, PDF_ALIGN_CENTER, NULL);
if (e < 0) {
pdf_set_font(pdf, save_font);
Expand Down Expand Up @@ -2746,7 +2761,7 @@ static int pdf_add_barcode_upca(struct pdf_doc *pdf, struct pdf_object *page,
for (int i = 0; i != 6; i++) {
text[0] = *string;
if (i) {
e = pdf_add_text_wrap(pdf, page, text, font, x, y, colour,
e = pdf_add_text_wrap(pdf, page, text, font, x, y, 0, colour,
7 * x_width, PDF_ALIGN_CENTER, NULL);
if (e < 0) {
pdf_set_font(pdf, save_font);
Expand Down Expand Up @@ -2775,7 +2790,7 @@ static int pdf_add_barcode_upca(struct pdf_doc *pdf, struct pdf_object *page,
for (int i = 0; i != 6; i++) {
text[0] = *string;
if (i != 5) {
e = pdf_add_text_wrap(pdf, page, text, font, x, y, colour,
e = pdf_add_text_wrap(pdf, page, text, font, x, y, 0, colour,
7 * x_width, PDF_ALIGN_CENTER, NULL);
if (e < 0) {
pdf_set_font(pdf, save_font);
Expand Down Expand Up @@ -2864,7 +2879,7 @@ static int pdf_add_barcode_ean8(struct pdf_doc *pdf, struct pdf_object *page,

for (int i = 0; i != 4; i++) {
text[0] = *string;
e = pdf_add_text_wrap(pdf, page, text, font, x, y, colour,
e = pdf_add_text_wrap(pdf, page, text, font, x, y, 0, colour,
7 * x_width, PDF_ALIGN_CENTER, NULL);
if (e < 0) {
pdf_set_font(pdf, save_font);
Expand All @@ -2890,7 +2905,7 @@ static int pdf_add_barcode_ean8(struct pdf_doc *pdf, struct pdf_object *page,

for (int i = 0; i != 4; i++) {
text[0] = *string;
e = pdf_add_text_wrap(pdf, page, text, font, x, y, colour,
e = pdf_add_text_wrap(pdf, page, text, font, x, y, 0, colour,
7 * x_width, PDF_ALIGN_CENTER, NULL);
if (e < 0) {
pdf_set_font(pdf, save_font);
Expand Down Expand Up @@ -3013,7 +3028,7 @@ static int pdf_add_barcode_upce(struct pdf_doc *pdf, struct pdf_object *page,

for (int i = 0; i != 6; i++) {
text[0] = X[i];
e = pdf_add_text_wrap(pdf, page, text, font, x, y, colour,
e = pdf_add_text_wrap(pdf, page, text, font, x, y, 0, colour,
7 * x_width, PDF_ALIGN_CENTER, NULL);
if (e < 0) {
pdf_set_font(pdf, save_font);
Expand Down
22 changes: 19 additions & 3 deletions pdfgen.h
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,21 @@ int pdf_add_text(struct pdf_doc *pdf, struct pdf_object *page,
const char *text, float size, float xoff, float yoff,
uint32_t colour);

/**
* Add a text string to the document at a rotated angle
* @param pdf PDF document to add to
* @param page Page to add object to (NULL => most recently added page)
* @param text String to display
* @param size Point size of the font
* @param xoff X location to put it in
* @param yoff Y location to put it in
* @param angle Rotation angle of text (in radians)
* @param colour Colour to draw the text
* @return 0 on success, < 0 on failure
*/
int pdf_add_text_rotate(struct pdf_doc *pdf, struct pdf_object *page,
const char *text, float size, float xoff, float yoff,
float angle, uint32_t colour);
/**
* Add a text string to the document, making it wrap if it is too
* long
Expand All @@ -434,6 +449,7 @@ int pdf_add_text(struct pdf_doc *pdf, struct pdf_object *page,
* @param size Point size of the font
* @param xoff X location to put it in
* @param yoff Y location to put it in
* @param angle Rotation angle of text (in radians)
* @param colour Colour to draw the text
* @param wrap_width Width at which to wrap the text
* @param align Text alignment (see PDF_ALIGN_xxx)
Expand All @@ -442,8 +458,8 @@ int pdf_add_text(struct pdf_doc *pdf, struct pdf_object *page,
*/
int pdf_add_text_wrap(struct pdf_doc *pdf, struct pdf_object *page,
const char *text, float size, float xoff, float yoff,
uint32_t colour, float wrap_width, int align,
float *height);
float angle, uint32_t colour, float wrap_width,
int align, float *height);

/**
* Add a line to the document
Expand Down Expand Up @@ -512,7 +528,7 @@ int pdf_add_quadratic_bezier(struct pdf_doc *pdf, struct pdf_object *page,
* @return 0 on success, < 0 on failure
*/
int pdf_add_custom_path(struct pdf_doc *pdf, struct pdf_object *page,
struct pdf_path_operation *operations,
const struct pdf_path_operation *operations,
int operation_count, float stroke_width,
uint32_t stroke_colour, uint32_t fill_colour);

Expand Down
4 changes: 2 additions & 2 deletions tests/fuzz-text.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ int LLVMFuzzerTestOneInput(char *data, int size)
memcpy(text, data, size);
text[size] = '\0';
pdf_add_text(pdf, NULL, text, 10, 20, 30, PDF_RGB(0xff, 0, 0));
pdf_add_text_wrap(pdf, NULL, text, 10, 20, 30, PDF_RGB(0xff, 0, 0), 200,
PDF_ALIGN_LEFT, NULL);
pdf_add_text_wrap(pdf, NULL, text, 10, 20, 30, 0, PDF_RGB(0xff, 0, 0),
200, PDF_ALIGN_LEFT, NULL);
free(text);
pdf_save(pdf, "fuzz.pdf");
pdf_destroy(pdf);
Expand Down
22 changes: 14 additions & 8 deletions tests/main.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
#include <locale.h>
#include <math.h>
#include <stdio.h>
#include <string.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

#include "pdfgen.h"

extern unsigned char data_penguin_jpg[];
Expand Down Expand Up @@ -89,7 +94,7 @@ int main(int argc, char *argv[])
"And special stuff €ÜŽžŠšÁ that áüöä should ÄÜÖß— “”‘’ break\n"
"————————————————————————————————————————————————\n"
"thisisanenourmouswordthatwillneverfitandwillhavetobecut",
16, 60, 800, PDF_RGB(0, 0, 0), 300, PDF_ALIGN_JUSTIFY, &height);
16, 60, 800, 0, PDF_RGB(0, 0, 0), 300, PDF_ALIGN_JUSTIFY, &height);
pdf_add_rectangle(pdf, NULL, 58, 800 + 16, 304, -height, 2,
PDF_RGB(0, 0, 0));
pdf_add_image_file(pdf, NULL, 10, 10, 20, 30, "data/teapot.ppm");
Expand Down Expand Up @@ -135,8 +140,9 @@ int main(int argc, char *argv[])
pdf_add_rectangle(pdf, NULL, 150, 150, 100, 100, 4, PDF_RGB(0, 0, 0xff));
pdf_add_filled_rectangle(pdf, NULL, 150, 450, 100, 100, 4,
PDF_RGB(0, 0xff, 0), PDF_TRANSPARENT);
pdf_add_text(pdf, NULL, "This should be transparent", 20, 160, 500,
PDF_ARGB(0x80, 0, 0, 0));
pdf_add_text_rotate(pdf, NULL, "This should be transparent", 20, 160, 500,
M_PI / 4, PDF_ARGB(0x80, 0, 0, 0));

float p1X[] = {200, 200, 300, 300};
float p1Y[] = {200, 300, 200, 300};
pdf_add_polygon(pdf, NULL, p1X, p1Y, 4, 4, PDF_RGB(0xaa, 0xff, 0xee));
Expand All @@ -158,7 +164,7 @@ int main(int argc, char *argv[])
pdf, NULL,
"Control characters ( ) < > [ ] { } / % \n \r \t \b \f ending", 10,
50, 45, PDF_RGB(0, 0, 0));
pdf_add_text(pdf, NULL, "Special characters: €ÜŽžŠšÁáüöäÄÜÖß—“”‘’", 10,
pdf_add_text(pdf, NULL, "Special characters: €ÜŽžŠšÁáüöäÄÜÖß—“”‘’Æ", 10,
50, 15, PDF_RGB(0, 0, 0));
pdf_add_text(pdf, NULL, "This one has a new line in it\nThere it was", 10,
50, 80, PDF_RGB(0, 0, 0));
Expand Down Expand Up @@ -226,7 +232,7 @@ int main(int argc, char *argv[])
PDF_MM_TO_POINT(20), "Code128", PDF_RGB(0, 0, 0));

pdf_add_text_wrap(pdf, NULL, "EAN13 Barcode", 10, PDF_MM_TO_POINT(20),
PDF_MM_TO_POINT(155), PDF_RGB(0, 0, 0),
PDF_MM_TO_POINT(155), 0, PDF_RGB(0, 0, 0),
PDF_MM_TO_POINT(60), PDF_ALIGN_CENTER, NULL);
pdf_add_rectangle(pdf, NULL, PDF_MM_TO_POINT(20), PDF_MM_TO_POINT(160),
PDF_MM_TO_POINT(60), PDF_MM_TO_POINT(40), 1,
Expand All @@ -235,7 +241,7 @@ int main(int argc, char *argv[])
PDF_MM_TO_POINT(160), PDF_MM_TO_POINT(60),
PDF_MM_TO_POINT(40), "4003994155486", PDF_BLACK);
pdf_add_text_wrap(pdf, NULL, "UPCA Barcode", 10, PDF_MM_TO_POINT(100),
PDF_MM_TO_POINT(155), PDF_RGB(0, 0, 0),
PDF_MM_TO_POINT(155), 0, PDF_RGB(0, 0, 0),
PDF_MM_TO_POINT(60), PDF_ALIGN_CENTER, NULL);
pdf_add_rectangle(pdf, NULL, PDF_MM_TO_POINT(100), PDF_MM_TO_POINT(160),
PDF_MM_TO_POINT(60), PDF_MM_TO_POINT(80), 1,
Expand All @@ -245,7 +251,7 @@ int main(int argc, char *argv[])
PDF_MM_TO_POINT(80), "003994155480", PDF_BLACK);

pdf_add_text_wrap(pdf, NULL, "EAN8 Barcode", 10, PDF_MM_TO_POINT(20),
PDF_MM_TO_POINT(55), PDF_RGB(0, 0, 0),
PDF_MM_TO_POINT(55), 0, PDF_RGB(0, 0, 0),
PDF_MM_TO_POINT(60), PDF_ALIGN_CENTER, NULL);
pdf_add_rectangle(pdf, NULL, PDF_MM_TO_POINT(20), PDF_MM_TO_POINT(60),
PDF_MM_TO_POINT(60), PDF_MM_TO_POINT(40), 1,
Expand All @@ -254,7 +260,7 @@ int main(int argc, char *argv[])
PDF_MM_TO_POINT(60), PDF_MM_TO_POINT(60),
PDF_MM_TO_POINT(40), "95012346", PDF_BLACK);
pdf_add_text_wrap(pdf, NULL, "UPCE Barcode", 10, PDF_MM_TO_POINT(100),
PDF_MM_TO_POINT(55), PDF_RGB(0, 0, 0),
PDF_MM_TO_POINT(55), 0, PDF_RGB(0, 0, 0),
PDF_MM_TO_POINT(60), PDF_ALIGN_CENTER, NULL);
pdf_add_rectangle(pdf, NULL, PDF_MM_TO_POINT(100), PDF_MM_TO_POINT(60),
PDF_MM_TO_POINT(60), PDF_MM_TO_POINT(80), 1,
Expand Down

0 comments on commit 0beb206

Please sign in to comment.