pdfio/pdfio-content.c
Michael R Sweet 94cb915885
Fix xref table - wrong total number of objects.
Update pdfioContentDrawImage to take x/y offsets and sizes.
2021-05-28 22:05:44 -04:00

1263 lines
30 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// Content helper functions for pdfio.
//
// Copyright © 2021 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
//
// Include necessary headers...
//
#include "pdfio-content.h"
#include "pdfio-private.h"
#include <math.h>
//
// Local types...
//
typedef pdfio_obj_t *(*image_func_t)(pdfio_dict_t *dict, int fd);
//
// Local functions...
//
static pdfio_obj_t *copy_gif(pdfio_dict_t *dict, int fd);
static pdfio_obj_t *copy_jpeg(pdfio_dict_t *dict, int fd);
static pdfio_obj_t *copy_png(pdfio_dict_t *dict, int fd);
static bool write_string(pdfio_stream_t *st, const char *s);
//
// 'pdfioContentBeginText()' - Begin a text block.
//
bool // O - `true` on success, `false` on failure
pdfioContentBeginText(
pdfio_stream_t *st) // I - Stream
{
return (pdfioStreamPuts(st, "BT\n"));
}
//
// 'pdfioContentClip()' - Clip output to the current path.
//
bool // O - `true` on success, `false` on failure
pdfioContentClip(
pdfio_stream_t *st, // I - Stream
bool even_odd) // I - Even/odd fill vs. non-zero winding rule
{
return (pdfioStreamPuts(st, even_odd ? "W*\n" : "W\n"));
}
//
// 'pdfioContentDrawImage()' - Draw an image object.
//
// The object name must be part of the page dictionary resources, typically
// using the @link pdfioPageDictAddImage@ function.
//
bool // O - `true` on success, `false` on failure
pdfioContentDrawImage(
pdfio_stream_t *st, // I - Stream
const char *name, // I - Image name
float x, // I - X offset of image
float y, // I - Y offset of image
float w, // I - Width of image
float h) // I - Height of image
{
return (pdfioStreamPrintf(st, "q %g 0 0 %g %g %g cm/%s Do Q\n", w, h, x, y, name));
}
//
// 'pdfioContentEndText()' - End a text block.
//
bool // O - `true` on success, `false` on failure
pdfioContentEndText(pdfio_stream_t *st) // I - Stream
{
return (pdfioStreamPuts(st, "ET\n"));
}
//
// 'pdfioContentFill()' - Fill the current path.
//
bool // O - `true` on success, `false` on failure
pdfioContentFill(
pdfio_stream_t *st, // I - Stream
bool even_odd) // I - Even/odd fill vs. non-zero winding rule
{
return (pdfioStreamPuts(st, even_odd ? "f*\n" : "f\n"));
}
//
// 'pdfioContentFillAndStroke()' - Fill and stroke the current path.
//
bool // O - `true` on success, `false` on failure
pdfioContentFillAndStroke(
pdfio_stream_t *st, // I - Stream
bool even_odd) // I - Even/odd fill vs. non-zero winding
{
return (pdfioStreamPuts(st, even_odd ? "B*\n" : "B\n"));
}
//
// 'pdfioContentMatrixConcat()' - Concatenate a matrix to the current graphics
// state.
//
bool // O - `true` on success, `false` on failure
pdfioContentMatrixConcat(
pdfio_stream_t *st, // I - Stream
pdfio_matrix_t m) // I - Transform matrix
{
return (pdfioStreamPrintf(st, "%g %g %g %g %g %g cm\n", m[0][0], m[0][1], m[1][0], m[1][1], m[2][0], m[2][1]));
}
//
// 'pdfioContentMatrixRotate()' - Rotate the current transform matrix.
//
bool // O - `true` on success, `false` on failure
pdfioContentMatrixRotate(
pdfio_stream_t *st, // I - Stream
float degrees) // I - Rotation angle in degrees counter-clockwise
{
double dcos = cos(degrees / M_PI); // Cosine
double dsin = sin(degrees / M_PI); // Sine
return (pdfioStreamPrintf(st, "%g %g %g %g 0 0 cm\n", dcos, -dsin, dsin, dcos));
}
//
// 'pdfioContentMatrixScale()' - Scale the current transform matrix.
//
bool // O - `true` on success, `false` on failure
pdfioContentMatrixScale(
pdfio_stream_t *st, // I - Stream
float sx, // I - X scale
float sy) // I - Y scale
{
return (pdfioStreamPrintf(st, "%g 0 0 %g 0 0 cm\n", sx, sy));
}
//
// 'pdfioContentMatrixTranslate()' - Translate the current transform matrix.
//
bool // O - `true` on success, `false` on failure
pdfioContentMatrixTranslate(
pdfio_stream_t *st, // I - Stream
float tx, // I - X offset
float ty) // I - Y offset
{
return (pdfioStreamPrintf(st, "1 0 0 1 %g %g cm\n", tx, ty));
}
//
// 'pdfioContentPathClose()' - Close the current path.
//
bool // O - `true` on success, `false` on failure
pdfioContentPathClose(
pdfio_stream_t *st) // I - Stream
{
return (pdfioStreamPuts(st, "h\n"));
}
//
// 'pdfioContentPathCurve()' - Add a Bezier curve with two control points.
//
bool // O - `true` on success, `false` on failure
pdfioContentPathCurve(
pdfio_stream_t *st, // I - Stream
float x1, // I - X position 1
float y1, // I - Y position 1
float x2, // I - X position 2
float y2, // I - Y position 2
float x3, // I - X position 3
float y3) // I - Y position 3
{
return (pdfioStreamPrintf(st, "%g %g %g %g %g %g c\n", x1, y1, x2, y2, x3, y3));
}
//
// 'pdfioContentPathCurve13()' - Add a Bezier curve with an initial control point.
//
bool // O - `true` on success, `false` on failure
pdfioContentPathCurve13(
pdfio_stream_t *st, // I - Stream
float x1, // I - X position 1
float y1, // I - Y position 1
float x3, // I - X position 3
float y3) // I - Y position 3
{
return (pdfioStreamPrintf(st, "%g %g %g %g v\n", x1, y1, x3, y3));
}
//
// 'pdfioContentPathCurve23()' - Add a Bezier curve with a trailing control point.
//
bool // O - `true` on success, `false` on failure
pdfioContentPathCurve23(
pdfio_stream_t *st, // I - Stream
float x2, // I - X position 2
float y2, // I - Y position 2
float x3, // I - X position 3
float y3) // I - Y position 3
{
return (pdfioStreamPrintf(st, "%g %g %g %g y\n", x2, y2, x3, y3));
}
//
// 'pdfioContentPathLineTo()' - Add a straight line to the current path.
//
bool // O - `true` on success, `false` on failure
pdfioContentPathLineTo(
pdfio_stream_t *st, // I - Stream
float x, // I - X position
float y) // I - Y position
{
return (pdfioStreamPrintf(st, "%g %g l\n", x, y));
}
//
// 'pdfioContentPathMoveTo()' - Start a new subpath.
//
bool // O - `true` on success, `false` on failure
pdfioContentPathMoveTo(
pdfio_stream_t *st, // I - Stream
float x, // I - X position
float y) // I - Y position
{
return (pdfioStreamPrintf(st, "%g %g m\n", x, y));
}
//
// 'pdfioContentPathRect()' - Add a rectangle to the current path.
//
bool // O - `true` on success, `false` on failure
pdfioContentPathRect(
pdfio_stream_t *st, // I - Stream
pdfio_rect_t *rect) // I - Rectangle
{
return (pdfioStreamPrintf(st, "%g %g %g %g re\n", rect->x1, rect->y1, rect->x2 - rect->x1, rect->y2 - rect->y1));
}
//
// 'pdfioContentRestore()' - Restore a previous graphics state.
//
bool // O - `true` on success, `false` on failure
pdfioContentRestore(
pdfio_stream_t *st) // I - Stream
{
return (pdfioStreamPuts(st, "Q\n"));
}
//
// 'pdfioContentSave()' - Save the current graphics state.
//
bool // O - `true` on success, `false` on failure
pdfioContentSave(pdfio_stream_t *st) // I - Stream
{
return (pdfioStreamPuts(st, "q\n"));
}
//
// 'pdfioContentSetDashPattern()' - Set the stroke pattern.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetDashPattern(
pdfio_stream_t *st, // I - Stream
int phase, // I - Phase (offset within pattern)
int on, // I - On length
int off) // I - Off length
{
return (pdfioStreamPrintf(st, "[%d %d] %d d\n", on, off, phase));
}
//
// 'pdfioContentSetFillColorDeviceCMYK()' - Set device CMYK fill color.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetFillColorDeviceCMYK(
pdfio_stream_t *st, // I - Stream
float c, // I - Cyan value (0.0 to 1.0)
float m, // I - Magenta value (0.0 to 1.0)
float y, // I - Yellow value (0.0 to 1.0)
float k) // I - Black value (0.0 to 1.0)
{
return (pdfioStreamPrintf(st, "%g %g %g %g k\n", c, m, y, k));
}
//
// 'pdfioContentSetFillColorDeviceGray()' - Set the device gray fill color.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetFillColorDeviceGray(
pdfio_stream_t *st, // I - Stream
float g) // I - Gray value (0.0 to 1.0)
{
return (pdfioStreamPrintf(st, "%g g\n", g));
}
//
// 'pdfioContentSetFillColorDeviceRGB()' - Set the device RGB fill color.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetFillColorDeviceRGB(
pdfio_stream_t *st, // I - Stream
float r, // I - Red value (0.0 to 1.0)
float g, // I - Green value (0.0 to 1.0)
float b) // I - Blue value (0.0 to 1.0)
{
return (pdfioStreamPrintf(st, "%g %g %g rg\n", r, g, b));
}
//
// 'pdfioContentSetFillColorGray()' - Set the calibrated gray fill color.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetFillColorGray(
pdfio_stream_t *st, // I - Stream
float g) // I - Gray value (0.0 to 1.0)
{
return (pdfioStreamPrintf(st, "%g sc\n", g));
}
//
// 'pdfioContentSetFillColorRGB()' - Set the calibrated RGB fill color.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetFillColorRGB(
pdfio_stream_t *st, // I - Stream
float r, // I - Red value (0.0 to 1.0)
float g, // I - Green value (0.0 to 1.0)
float b) // I - Blue value (0.0 to 1.0)
{
return (pdfioStreamPrintf(st, "%g %g %g sc\n", r, g, b));
}
//
// 'pdfioContentSetFillColorSpace()' - Set the fill colorspace.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetFillColorSpace(
pdfio_stream_t *st, // I - Stream
const char *name) // I - Color space name
{
return (pdfioStreamPrintf(st, "/%s cs\n", name));
}
//
// 'pdfioContentSetFlatness()' - Set the flatness tolerance.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetFlatness(
pdfio_stream_t *st, // I - Stream
float flatness) // I - Flatness value (0.0 to 100.0)
{
return (pdfioStreamPrintf(st, "%g i\n", flatness));
}
//
// 'pdfioContentSetLineCap()' - Set the line ends style.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetLineCap(
pdfio_stream_t *st, // I - Stream
pdfio_linecap_t lc) // I - Line cap value
{
return (pdfioStreamPrintf(st, "%d J\n", lc));
}
//
// 'pdfioContentSetLineJoin()' - Set the line joining style.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetLineJoin(
pdfio_stream_t *st, // I - Stream
pdfio_linejoin_t lj) // I - Line join value
{
return (pdfioStreamPrintf(st, "%d j\n", lj));
}
//
// 'pdfioContentSetLineWidth()' - Set the line width.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetLineWidth(
pdfio_stream_t *st, // I - Stream
float width) // I - Line width value
{
return (pdfioStreamPrintf(st, "%g w\n", width));
}
//
// 'pdfioContentSetMiterLimit()' - Set the miter limit.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetMiterLimit(
pdfio_stream_t *st, // I - Stream
float limit) // I - Miter limit value
{
return (pdfioStreamPrintf(st, "%g M\n", limit));
}
//
// 'pdfioContentSetStrokeColorDeviceCMYK()' - Set the device CMYK stroke color.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetStrokeColorDeviceCMYK(
pdfio_stream_t *st, // I - Stream
float c, // I - Cyan value (0.0 to 1.0)
float m, // I - Magenta value (0.0 to 1.0)
float y, // I - Yellow value (0.0 to 1.0)
float k) // I - Black value (0.0 to 1.0)
{
return (pdfioStreamPrintf(st, "%g %g %g %g K\n", c, m, y, k));
}
//
// 'pdfioContentSetStrokeColorDeviceGray()' - Set the device gray stroke color.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetStrokeColorDeviceGray(
pdfio_stream_t *st, // I - Stream
float g) // I - Gray value (0.0 to 1.0)
{
return (pdfioStreamPrintf(st, "%g G\n", g));
}
//
// 'pdfioContentSetStrokeColorDeviceRGB()' - Set the device RGB stroke color.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetStrokeColorDeviceRGB(
pdfio_stream_t *st, // I - Stream
float r, // I - Red value (0.0 to 1.0)
float g, // I - Green value (0.0 to 1.0)
float b) // I - Blue value (0.0 to 1.0)
{
return (pdfioStreamPrintf(st, "%g %g %g RG\n", r, g, b));
}
//
// 'pdfioContentSetStrokeColorGray()' - Set the calibrated gray stroke color.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetStrokeColorGray(
pdfio_stream_t *st, // I - Stream
float g) // I - Gray value (0.0 to 1.0)
{
return (pdfioStreamPrintf(st, "%g SC\n", g));
}
//
// 'pdfioContentSetStrokeColorRGB()' - Set the calibrated RGB stroke color.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetStrokeColorRGB(
pdfio_stream_t *st, // I - Stream
float r, // I - Red value (0.0 to 1.0)
float g, // I - Green value (0.0 to 1.0)
float b) // I - Blue value (0.0 to 1.0)
{
return (pdfioStreamPrintf(st, "%g %g %g SC\n", r, g, b));
}
//
// 'pdfioContentSetStrokeColorSpace()' - Set the stroke color space.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetStrokeColorSpace(
pdfio_stream_t *st, // I - Stream
const char *name) // I - Color space name
{
return (pdfioStreamPrintf(st, "/%s CS\n", name));
}
//
// 'pdfioContentSetTextCharacterSpacing()' - Set the spacing between characters.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetTextCharacterSpacing(
pdfio_stream_t *st, // I - Stream
float spacing) // I - Character spacing
{
return (pdfioStreamPrintf(st, "%g Tc\n", spacing));
}
//
// 'pdfioContentSetTextFont()' - Set the text font and size.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetTextFont(
pdfio_stream_t *st, // I - Stream
const char *name, // I - Font name
float size) // I - Font size
{
return (pdfioStreamPrintf(st, "/%s %g Tf\n", name, size));
}
//
// 'pdfioContentSetTextLeading()' - Set text leading (line height) value.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetTextLeading(
pdfio_stream_t *st, // I - Stream
float leading) // I - Leading (line height) value
{
return (pdfioStreamPrintf(st, "%g TL\n", leading));
}
//
// 'pdfioContentSetTextMatrix()' - Set the text transform matrix.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetTextMatrix(
pdfio_stream_t *st, // I - Stream
pdfio_matrix_t m) // I - Transform matrix
{
return (pdfioStreamPrintf(st, "%g %g %g %g %g %g Tm\n", m[0][0], m[0][1], m[1][0], m[1][1], m[2][0], m[2][1]));
}
//
// 'pdfioContentSetTextRenderingMode()' - Set the text rendering mode.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetTextRenderingMode(
pdfio_stream_t *st, // I - Stream
pdfio_textrendering_t mode) // I - Text rendering mode
{
return (pdfioStreamPrintf(st, "%d Tr\n", mode));
}
//
// 'pdfioContentSetTextRise()' - Set the text baseline offset.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetTextRise(
pdfio_stream_t *st, // I - Stream
float rise) // I - Y offset
{
return (pdfioStreamPrintf(st, "%g Ts\n", rise));
}
//
// 'pdfioContentSetTextWordSpacing()' - Set the inter-word spacing.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetTextWordSpacing(
pdfio_stream_t *st, // I - Stream
float spacing) // I - Spacing between words
{
return (pdfioStreamPrintf(st, "%g Tw\n", spacing));
}
//
// 'pdfioContentSetTextXScaling()' - Set the horizontal scaling value.
//
bool // O - `true` on success, `false` on failure
pdfioContentSetTextXScaling(
pdfio_stream_t *st, // I - Stream
float percent) // I - Horizontal scaling in percent
{
return (pdfioStreamPrintf(st, "%g Tz\n", percent));
}
//
// 'pdfioContentStroke()' - Stroke the current path.
//
bool // O - `true` on success, `false` on failure
pdfioContentStroke(pdfio_stream_t *st) // I - Stream
{
return (pdfioStreamPuts(st, "S\n"));
}
//
// 'pdfioContentTextMoveLine()' - Move to the next line and offset.
//
bool // O - `true` on success, `false` on failure
pdfioContentTextMoveLine(
pdfio_stream_t *st, // I - Stream
float tx, // I - X offset
float ty) // I - Y offset
{
return (pdfioStreamPrintf(st, "%g %g TD\n", tx, ty));
}
//
// 'pdfioContentTextMoveTo()' - Offset within the current line.
//
bool // O - `true` on success, `false` on failure
pdfioContentTextMoveTo(
pdfio_stream_t *st, // I - Stream
float tx, // I - X offset
float ty) // I - Y offset
{
return (pdfioStreamPrintf(st, "%g %g Td\n", tx, ty));
}
//
// 'pdfioContentTextNextLine()' - Move to the next line.
//
bool // O - `true` on success, `false` on failure
pdfioContentTextNextLine(
pdfio_stream_t *st) // I - Stream
{
return (pdfioStreamPuts(st, "T*\n"));
}
//
// 'pdfioContentTextShow()' - Show text.
//
bool // O - `true` on success, `false` on failure
pdfioContentTextShow(
pdfio_stream_t *st, // I - Stream
const char *s, // I - String to show
bool new_line) // I - Advance to the next line afterwards
{
write_string(st, s);
if (new_line)
return (pdfioStreamPuts(st, "\'\n"));
else
return (pdfioStreamPuts(st, "Tj\n"));
}
//
// 'pdfioContentTextShowJustified()' - Show justified text.
//
bool // O - `true` on success, `false` on failure
pdfioContentTextShowJustified(
pdfio_stream_t *st, // I - Stream
size_t num_fragments, // I - Number of text fragments
const float *offsets, // I - Text offsets before fragments
const char * const *fragments) // I - Text fragments
{
size_t i; // Looping var
// Write an array of offsets and string fragments...
if (!pdfioStreamPuts(st, "["))
return (false);
for (i = 0; i < num_fragments; i ++)
{
if (offsets[i] != 0.0f)
{
if (!pdfioStreamPrintf(st, "%g", offsets[i]))
return (false);
}
if (fragments[i])
{
if (!write_string(st, fragments[i]))
return (false);
}
}
return (pdfioStreamPuts(st, "]TJ\n"));
}
//
// 'pdfioPageDictAddICCColorSpace()' - Add an ICC color space to the page
// dictionary.
//
bool // O - `true` on success, `false` on failure
pdfioPageDictAddICCColorSpace(
pdfio_dict_t *dict, // I - Page dictionary
const char *name, // I - Color space name
pdfio_obj_t *obj) // I - ICC profile object
{
(void)dict;
(void)name;
(void)obj;
return (false);
}
//
// 'pdfioPageDictAddCalibratedColorSpace()' - Add a calibrated color space to
// the page dictionary.
//
bool // O - `true` on success, `false` on failure
pdfioPageDictAddCalibratedColorSpace(
pdfio_dict_t *dict, // I - Page dictionary
const char *name, // I - Color space name
size_t num_colors, // I - Number of color components
const float *white_point, // I - CIE XYZ white point
float gamma) // I - Gamma value
{
(void)dict;
(void)name;
(void)num_colors;
(void)white_point;
(void)gamma;
return (false);
}
//
// 'pdfioPageDictAddFont()' - Add a font object to the page dictionary.
//
bool // O - `true` on success, `false` on failure
pdfioPageDictAddFont(
pdfio_dict_t *dict, // I - Page dictionary
const char *name, // I - Font name
pdfio_obj_t *obj) // I - Font object
{
(void)dict;
(void)name;
(void)obj;
return (false);
}
//
// 'pdfioPageDictAddImage()' - Add an image object to the page dictionary.
//
bool // O - `true` on success, `false` on failure
pdfioPageDictAddImage(
pdfio_dict_t *dict, // I - Page dictionary
const char *name, // I - Image name
pdfio_obj_t *obj) // I - Image object
{
pdfio_dict_t *resources; // Resource dictionary
pdfio_dict_t *xobject; // XObject dictionary
// Range check input...
if (!dict || !name || !obj)
return (false);
// Get the images dictionary...
if ((resources = pdfioDictGetDict(dict, "Resources")) == NULL)
{
if ((resources = pdfioDictCreate(dict->pdf)) == NULL)
return (false);
if (!pdfioDictSetDict(dict, "Resources", resources))
return (false);
}
if ((xobject = pdfioDictGetDict(resources, "XObject")) == NULL)
{
if ((xobject = pdfioDictCreate(dict->pdf)) == NULL)
return (false);
if (!pdfioDictSetDict(resources, "XObject", xobject))
return (false);
}
// Now set the image reference in the images resource dictionary and return...
return (pdfioDictSetObject(xobject, name, obj));
}
//
// 'pdfioFileCreateFontObject()' - Add a font object to a PDF file.
//
pdfio_obj_t * // O - Object
pdfioFileCreateFontObject(
pdfio_file_t *pdf, // I - PDF file
const char *filename) // I - Filename
{
(void)pdf;
(void)filename;
return (NULL);
}
//
// 'pdfioFileCreateICCProfileObject()' - Add an ICC profile object to a PDF file.
//
pdfio_obj_t * // O - Object
pdfioFileCreateICCProfileObject(
pdfio_file_t *pdf, // I - PDF file
const char *filename) // I - Filename
{
(void)pdf;
(void)filename;
return (NULL);
}
//
// 'pdfioFileCreateImageObject()' - Add an image object to a PDF file.
//
// Currently only GIF, JPEG, and PNG files are supported.
//
pdfio_obj_t * // O - Object
pdfioFileCreateImageObject(
pdfio_file_t *pdf, // I - PDF file
const char *filename, // I - Filename
bool interpolate) // I - Interpolate image data?
{
pdfio_dict_t *dict; // Image dictionary
pdfio_obj_t *obj; // Image object
int fd; // File
unsigned char buffer[32]; // Read buffer
image_func_t copy_func = NULL; // Image copy function
// Range check input...
if (!pdf || !filename)
return (NULL);
// Try opening the file...
if ((fd = open(filename, O_RDONLY | O_BINARY)) < 0)
{
_pdfioFileError(pdf, "Unable to open image file '%s': %s", filename, strerror(errno));
return (NULL);
}
// Read the file header to determine the file format...
if (read(fd, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer))
{
_pdfioFileError(pdf, "Unable to read header from image file '%s'.", filename);
close(fd);
return (NULL);
}
lseek(fd, 0, SEEK_SET);
if (!memcmp(buffer, "\211PNG\015\012\032\012\000\000\000\015IHDR", 16))
{
// PNG image...
copy_func = copy_png;
}
else if (!memcmp(buffer, "GIF87a", 6) || !memcmp(buffer, "GIF89a", 6))
{
// GIF image...
copy_func = copy_gif;
}
else if (!memcmp(buffer, "\377\330\377", 3))
{
// JPEG image...
copy_func = copy_jpeg;
}
else
{
// Something else that isn't supported...
_pdfioFileError(pdf, "Unsupported image file '%s'.", filename);
close(fd);
return (NULL);
}
// Create the base image dictionary the copy the file into an object...
if ((dict = pdfioDictCreate(pdf)) == NULL)
{
close(fd);
return (NULL);
}
pdfioDictSetName(dict, "Type", "XObject");
pdfioDictSetName(dict, "Subtype", "Image");
pdfioDictSetBoolean(dict, "Interpolate", interpolate);
obj = (copy_func)(dict, fd);
// Close the file and return the object...
close(fd);
return (obj);
}
//
// 'pdfioImageGetHeight()' - Get the height of an image object.
//
float // O - Height in lines
pdfioImageGetHeight(pdfio_obj_t *obj) // I - Image object
{
return (pdfioDictGetNumber(obj->value.value.dict, "Height"));
}
//
// 'pdfioImageGetWidth()' - Get the width of an image object.
//
float // O - Width in columns
pdfioImageGetWidth(pdfio_obj_t *obj) // I - Image object
{
return (pdfioDictGetNumber(obj->value.value.dict, "Width"));
}
//
// 'copy_gif()' - Copy a GIF image.
//
static pdfio_obj_t * // O - Object or `NULL` on error
copy_gif(pdfio_dict_t *dict, // I - Dictionary
int fd) // I - File descriptor
{
// TODO: Implement copy_gif
(void)dict;
(void)fd;
return (NULL);
}
//
// 'copy_jpeg()' - Copy a JPEG image.
//
static pdfio_obj_t * // O - Object or `NULL` on error
copy_jpeg(pdfio_dict_t *dict, // I - Dictionary
int fd) // I - File descriptor
{
pdfio_obj_t *obj; // Object
pdfio_stream_t *st; // Stream for JPEG data
ssize_t bytes; // Bytes read
unsigned char buffer[16384], // Read buffer
*bufptr, // Pointer into buffer
*bufend; // End of buffer
size_t length; // Length of chunk
unsigned width = 0, // Width in columns
height = 0, // Height in lines
num_colors = 0; // Number of colors
// Scan the file for a SOFn marker, then we can get the dimensions...
bytes = read(fd, buffer, sizeof(buffer));
for (bufptr = buffer + 2, bufend = buffer + bytes; bufptr < bufend;)
{
if (*bufptr == 0xff)
{
bufptr ++;
if (bufptr >= bufend)
{
/*
* If we are at the end of the current buffer, re-fill and continue...
*/
if ((bytes = read(fd, buffer, sizeof(buffer))) <= 0)
break;
bufptr = buffer;
bufend = buffer + bytes;
}
if (*bufptr == 0xff)
continue;
if ((bufptr + 16) >= bufend)
{
/*
* Read more of the marker...
*/
bytes = bufend - bufptr;
memmove(buffer, bufptr, (size_t)bytes);
bufptr = buffer;
bufend = buffer + bytes;
if ((bytes = read(fd, bufend, sizeof(buffer) - (size_t)bytes)) <= 0)
break;
bufend += bytes;
}
length = (size_t)((bufptr[1] << 8) | bufptr[2]);
PDFIO_DEBUG("copy_jpeg: JPEG X'FF%02X' (length %u)\n", *bufptr, (unsigned)length);
if ((*bufptr >= 0xc0 && *bufptr <= 0xc3) || (*bufptr >= 0xc5 && *bufptr <= 0xc7) || (*bufptr >= 0xc9 && *bufptr <= 0xcb) || (*bufptr >= 0xcd && *bufptr <= 0xcf))
{
// SOFn marker, look for dimensions...
width = (unsigned)((bufptr[6] << 8) | bufptr[7]);
height = (unsigned)((bufptr[4] << 8) | bufptr[5]);
num_colors = bufptr[8];
break;
}
// Skip past this marker...
bufptr ++;
bytes = bufend - bufptr;
while (length >= (size_t)bytes)
{
length -= (size_t)bytes;
if ((bytes = read(fd, buffer, sizeof(buffer))) <= 0)
break;
bufptr = buffer;
bufend = buffer + bytes;
}
if (length > (size_t)bytes)
break;
bufptr += length;
}
}
if (width == 0 || height == 0 || (num_colors != 1 && num_colors != 3))
return (NULL);
// Create the image object...
pdfioDictSetNumber(dict, "Width", width);
pdfioDictSetNumber(dict, "Height", height);
pdfioDictSetNumber(dict, "BitsPerComponent", 8);
// TODO: Add proper JPEG CalRGB/Gray color spaces
pdfioDictSetName(dict, "ColorSpace", num_colors == 3 ? "DeviceRGB" : "DeviceGray");
pdfioDictSetName(dict, "Filter", "DCTDecode");
obj = pdfioFileCreateObject(dict->pdf, dict);
st = pdfioObjCreateStream(obj, PDFIO_FILTER_NONE);
// Copy the file to a stream...
lseek(fd, 0, SEEK_SET);
while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
{
if (!pdfioStreamWrite(st, buffer, (size_t)bytes))
return (NULL);
}
if (!pdfioStreamClose(st))
return (NULL);
return (obj);
}
//
// 'copy_png()' - Copy a PNG image.
//
static pdfio_obj_t * // O - Object or `NULL` on error
copy_png(pdfio_dict_t *dict, // I - Dictionary
int fd) // I - File descriptor
{
// TODO: Implement copy_png
(void)dict;
(void)fd;
return (NULL);
}
//
// 'write_string()' - Write a PDF string.
//
static bool // O - `true` on success, `false` otherwise
write_string(pdfio_stream_t *st, // I - Stream
const char *s) // I - String
{
const char *ptr; // Pointer into string
// Determine whether this is Unicode or just ASCII...
for (ptr = s; *ptr; ptr ++)
{
if (*ptr & 0x80)
break;
}
if (*ptr)
{
// Unicode string...
int ch; // Unicode character
if (!pdfioStreamPuts(st, "("))
return (false);
for (ptr = s; *ptr; ptr ++)
{
if ((*ptr & 0xe0) == 0xc0)
{
// Two-byte UTF-8
ch = ((ptr[0] & 0x1f) << 6) | (ptr[1] & 0x3f);
ptr ++;
}
else if ((*ptr & 0xf0) == 0xe0)
{
// Three-byte UTF-8
ch = ((ptr[0] & 0x0f) << 12) | ((ptr[1] & 0x3f) << 6) | (ptr[2] & 0x3f);
ptr += 2;
}
else if ((*ptr & 0xf8) == 0xf0)
{
// Four-byte UTF-8
ch = ((ptr[0] & 0x07) << 18) | ((ptr[1] & 0x3f) << 12) | ((ptr[2] & 0x3f) << 6) | (ptr[3] & 0x3f);
ptr += 3;
}
else
ch = *ptr & 255;
// Write a two-byte character...
if (!pdfioStreamPrintf(st, "%04X", ch))
return (false);
}
if (!pdfioStreamPuts(st, ")"))
return (false);
}
else
{
// ASCII string...
const char *start = s; // Start of fragment
int level = 0; // Paren level
if (!pdfioStreamPuts(st, "("))
return (false);
for (ptr = start; *ptr; ptr ++)
{
if (*ptr == '\\' || (*ptr == ')' && level == 0) || *ptr < ' ')
{
if (ptr > start)
{
if (!pdfioStreamWrite(st, start, (size_t)(ptr - start)))
return (false);
start = ptr + 1;
}
if (*ptr < ' ')
{
if (!pdfioStreamPrintf(st, "\\%03o", *ptr))
return (false);
}
else if (!pdfioStreamPrintf(st, "\\%c", *ptr))
return (false);
}
else if (*ptr == '(')
level ++;
else if (*ptr == ')')
level --;
}
if (ptr > start)
{
if (!pdfioStreamPrintf(st, "%s)", start))
return (false);
}
else if (!pdfioStreamPuts(st, ")"))
return (false);
}
return (true);
}