forked from KolibriOS/kolibrios
libpng 1.5.1
git-svn-id: svn://kolibrios.org@1897 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,798 @@
|
||||
/* cairo - a vector graphics library with display and print output
|
||||
*
|
||||
* Copyright © 2003 University of Southern California
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it either under the terms of the GNU Lesser General Public
|
||||
* License version 2.1 as published by the Free Software Foundation
|
||||
* (the "LGPL") or, at your option, under the terms of the Mozilla
|
||||
* Public License Version 1.1 (the "MPL"). If you do not alter this
|
||||
* notice, a recipient may use your version of this file under either
|
||||
* the MPL or the LGPL.
|
||||
*
|
||||
* You should have received a copy of the LGPL along with this library
|
||||
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
|
||||
* You should have received a copy of the MPL along with this library
|
||||
* in the file COPYING-MPL-1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.1 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
|
||||
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
|
||||
* the specific language governing rights and limitations.
|
||||
*
|
||||
* The Original Code is the cairo graphics library.
|
||||
*
|
||||
* The Initial Developer of the Original Code is University of Southern
|
||||
* California.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Carl D. Worth <cworth@cworth.org>
|
||||
* Kristian Høgsberg <krh@redhat.com>
|
||||
* Chris Wilson <chris@chris-wilson.co.uk>
|
||||
*/
|
||||
|
||||
#include "cairoint.h"
|
||||
|
||||
#include "cairo-error-private.h"
|
||||
#include "cairo-output-stream-private.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <png.h>
|
||||
|
||||
/**
|
||||
* SECTION:cairo-png
|
||||
* @Title: PNG Support
|
||||
* @Short_Description: Reading and writing PNG images
|
||||
* @See_Also: #cairo_surface_t
|
||||
*
|
||||
* The PNG functions allow reading PNG images into image surfaces, and writing
|
||||
* any surface to a PNG file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* CAIRO_HAS_PNG_FUNCTIONS:
|
||||
*
|
||||
* Defined if the PNG functions are available.
|
||||
* This macro can be used to conditionally compile code using the cairo
|
||||
* PNG functions.
|
||||
*/
|
||||
|
||||
struct png_read_closure_t {
|
||||
cairo_read_func_t read_func;
|
||||
void *closure;
|
||||
cairo_output_stream_t *png_data;
|
||||
};
|
||||
|
||||
|
||||
/* Unpremultiplies data and converts native endian ARGB => RGBA bytes */
|
||||
static void
|
||||
unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < row_info->rowbytes; i += 4) {
|
||||
uint8_t *b = &data[i];
|
||||
uint32_t pixel;
|
||||
uint8_t alpha;
|
||||
|
||||
memcpy (&pixel, b, sizeof (uint32_t));
|
||||
alpha = (pixel & 0xff000000) >> 24;
|
||||
if (alpha == 0) {
|
||||
b[0] = b[1] = b[2] = b[3] = 0;
|
||||
} else {
|
||||
b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
|
||||
b[1] = (((pixel & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
|
||||
b[2] = (((pixel & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
|
||||
b[3] = alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Converts native endian xRGB => RGBx bytes */
|
||||
static void
|
||||
convert_data_to_bytes (png_structp png, png_row_infop row_info, png_bytep data)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < row_info->rowbytes; i += 4) {
|
||||
uint8_t *b = &data[i];
|
||||
uint32_t pixel;
|
||||
|
||||
memcpy (&pixel, b, sizeof (uint32_t));
|
||||
|
||||
b[0] = (pixel & 0xff0000) >> 16;
|
||||
b[1] = (pixel & 0x00ff00) >> 8;
|
||||
b[2] = (pixel & 0x0000ff) >> 0;
|
||||
b[3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Use a couple of simple error callbacks that do not print anything to
|
||||
* stderr and rely on the user to check for errors via the #cairo_status_t
|
||||
* return.
|
||||
*/
|
||||
static void
|
||||
png_simple_error_callback (png_structp png,
|
||||
png_const_charp error_msg)
|
||||
{
|
||||
cairo_status_t *error = png_get_error_ptr (png);
|
||||
|
||||
/* default to the most likely error */
|
||||
if (*error == CAIRO_STATUS_SUCCESS)
|
||||
*error = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
#ifdef PNG_SETJMP_SUPPORTED
|
||||
longjmp (png_jmpbuf (png), 1);
|
||||
#endif
|
||||
|
||||
/* if we get here, then we have to choice but to abort ... */
|
||||
}
|
||||
|
||||
static void
|
||||
png_simple_warning_callback (png_structp png,
|
||||
png_const_charp error_msg)
|
||||
{
|
||||
cairo_status_t *error = png_get_error_ptr (png);
|
||||
|
||||
/* default to the most likely error */
|
||||
if (*error == CAIRO_STATUS_SUCCESS)
|
||||
*error = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
/* png does not expect to abort and will try to tidy up after a warning */
|
||||
}
|
||||
|
||||
|
||||
/* Starting with libpng-1.2.30, we must explicitly specify an output_flush_fn.
|
||||
* Otherwise, we will segfault if we are writing to a stream. */
|
||||
static void
|
||||
png_simple_output_flush_fn (png_structp png_ptr)
|
||||
{
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
write_png (cairo_surface_t *surface,
|
||||
png_rw_ptr write_func,
|
||||
void *closure)
|
||||
{
|
||||
int i;
|
||||
cairo_status_t status;
|
||||
cairo_image_surface_t *image;
|
||||
cairo_image_surface_t * volatile clone;
|
||||
void *image_extra;
|
||||
png_struct *png;
|
||||
png_info *info;
|
||||
png_byte **volatile rows = NULL;
|
||||
png_color_16 white;
|
||||
int png_color_type;
|
||||
int depth;
|
||||
|
||||
status = _cairo_surface_acquire_source_image (surface,
|
||||
&image,
|
||||
&image_extra);
|
||||
|
||||
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
|
||||
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
||||
else if (unlikely (status))
|
||||
return status;
|
||||
|
||||
/* PNG complains about "Image width or height is zero in IHDR" */
|
||||
if (image->width == 0 || image->height == 0) {
|
||||
status = _cairo_error (CAIRO_STATUS_WRITE_ERROR);
|
||||
goto BAIL1;
|
||||
}
|
||||
|
||||
/* Handle the various fallback formats (e.g. low bit-depth XServers)
|
||||
* by coercing them to a simpler format using pixman.
|
||||
*/
|
||||
clone = _cairo_image_surface_coerce (image);
|
||||
status = clone->base.status;
|
||||
if (unlikely (status))
|
||||
goto BAIL1;
|
||||
|
||||
rows = _cairo_malloc_ab (clone->height, sizeof (png_byte*));
|
||||
if (unlikely (rows == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto BAIL2;
|
||||
}
|
||||
|
||||
for (i = 0; i < clone->height; i++)
|
||||
rows[i] = (png_byte *) clone->data + i * clone->stride;
|
||||
|
||||
png = png_create_write_struct (PNG_LIBPNG_VER_STRING, &status,
|
||||
png_simple_error_callback,
|
||||
png_simple_warning_callback);
|
||||
if (unlikely (png == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto BAIL3;
|
||||
}
|
||||
|
||||
info = png_create_info_struct (png);
|
||||
if (unlikely (info == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto BAIL4;
|
||||
}
|
||||
|
||||
#ifdef PNG_SETJMP_SUPPORTED
|
||||
if (setjmp (png_jmpbuf (png)))
|
||||
goto BAIL4;
|
||||
#endif
|
||||
|
||||
png_set_write_fn (png, closure, write_func, png_simple_output_flush_fn);
|
||||
|
||||
switch (clone->format) {
|
||||
case CAIRO_FORMAT_ARGB32:
|
||||
depth = 8;
|
||||
if (_cairo_image_analyze_transparency (clone) == CAIRO_IMAGE_IS_OPAQUE)
|
||||
png_color_type = PNG_COLOR_TYPE_RGB;
|
||||
else
|
||||
png_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
|
||||
break;
|
||||
case CAIRO_FORMAT_RGB24:
|
||||
depth = 8;
|
||||
png_color_type = PNG_COLOR_TYPE_RGB;
|
||||
break;
|
||||
case CAIRO_FORMAT_A8:
|
||||
depth = 8;
|
||||
png_color_type = PNG_COLOR_TYPE_GRAY;
|
||||
break;
|
||||
case CAIRO_FORMAT_A1:
|
||||
depth = 1;
|
||||
png_color_type = PNG_COLOR_TYPE_GRAY;
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
png_set_packswap (png);
|
||||
#endif
|
||||
break;
|
||||
case CAIRO_FORMAT_INVALID:
|
||||
case CAIRO_FORMAT_RGB16_565:
|
||||
default:
|
||||
status = _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
|
||||
goto BAIL4;
|
||||
}
|
||||
|
||||
png_set_IHDR (png, info,
|
||||
clone->width,
|
||||
clone->height, depth,
|
||||
png_color_type,
|
||||
PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
white.gray = (1 << depth) - 1;
|
||||
white.red = white.blue = white.green = white.gray;
|
||||
png_set_bKGD (png, info, &white);
|
||||
|
||||
if (0) { /* XXX extract meta-data from surface (i.e. creation date) */
|
||||
png_time pt;
|
||||
|
||||
png_convert_from_time_t (&pt, time (NULL));
|
||||
png_set_tIME (png, info, &pt);
|
||||
}
|
||||
|
||||
/* We have to call png_write_info() before setting up the write
|
||||
* transformation, since it stores data internally in 'png'
|
||||
* that is needed for the write transformation functions to work.
|
||||
*/
|
||||
png_write_info (png, info);
|
||||
|
||||
if (png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
|
||||
png_set_write_user_transform_fn (png, unpremultiply_data);
|
||||
} else if (png_color_type == PNG_COLOR_TYPE_RGB) {
|
||||
png_set_write_user_transform_fn (png, convert_data_to_bytes);
|
||||
png_set_filler (png, 0, PNG_FILLER_AFTER);
|
||||
}
|
||||
|
||||
png_write_image (png, rows);
|
||||
png_write_end (png, info);
|
||||
|
||||
BAIL4:
|
||||
png_destroy_write_struct (&png, &info);
|
||||
BAIL3:
|
||||
free (rows);
|
||||
BAIL2:
|
||||
cairo_surface_destroy (&clone->base);
|
||||
BAIL1:
|
||||
_cairo_surface_release_source_image (surface, image, image_extra);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
stdio_write_func (png_structp png, png_bytep data, png_size_t size)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
fp = png_get_io_ptr (png);
|
||||
while (size) {
|
||||
size_t ret = fwrite (data, 1, size, fp);
|
||||
size -= ret;
|
||||
data += ret;
|
||||
if (size && ferror (fp)) {
|
||||
cairo_status_t *error = png_get_error_ptr (png);
|
||||
if (*error == CAIRO_STATUS_SUCCESS)
|
||||
*error = _cairo_error (CAIRO_STATUS_WRITE_ERROR);
|
||||
png_error (png, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* cairo_surface_write_to_png:
|
||||
* @surface: a #cairo_surface_t with pixel contents
|
||||
* @filename: the name of a file to write to
|
||||
*
|
||||
* Writes the contents of @surface to a new file @filename as a PNG
|
||||
* image.
|
||||
*
|
||||
* Return value: %CAIRO_STATUS_SUCCESS if the PNG file was written
|
||||
* successfully. Otherwise, %CAIRO_STATUS_NO_MEMORY if memory could not
|
||||
* be allocated for the operation or
|
||||
* %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have
|
||||
* pixel contents, or %CAIRO_STATUS_WRITE_ERROR if an I/O error occurs
|
||||
* while attempting to write the file.
|
||||
**/
|
||||
cairo_status_t
|
||||
cairo_surface_write_to_png (cairo_surface_t *surface,
|
||||
const char *filename)
|
||||
{
|
||||
FILE *fp;
|
||||
cairo_status_t status;
|
||||
|
||||
if (surface->status)
|
||||
return surface->status;
|
||||
|
||||
if (surface->finished)
|
||||
return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
|
||||
|
||||
fp = fopen (filename, "wb");
|
||||
if (fp == NULL) {
|
||||
switch (errno) {
|
||||
case ENOMEM:
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
default:
|
||||
return _cairo_error (CAIRO_STATUS_WRITE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
status = write_png (surface, stdio_write_func, fp);
|
||||
|
||||
if (fclose (fp) && status == CAIRO_STATUS_SUCCESS)
|
||||
status = _cairo_error (CAIRO_STATUS_WRITE_ERROR);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
struct png_write_closure_t {
|
||||
cairo_write_func_t write_func;
|
||||
void *closure;
|
||||
};
|
||||
|
||||
static void
|
||||
stream_write_func (png_structp png, png_bytep data, png_size_t size)
|
||||
{
|
||||
cairo_status_t status;
|
||||
struct png_write_closure_t *png_closure;
|
||||
|
||||
png_closure = png_get_io_ptr (png);
|
||||
status = png_closure->write_func (png_closure->closure, data, size);
|
||||
if (unlikely (status)) {
|
||||
cairo_status_t *error = png_get_error_ptr (png);
|
||||
if (*error == CAIRO_STATUS_SUCCESS)
|
||||
*error = status;
|
||||
png_error (png, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* cairo_surface_write_to_png_stream:
|
||||
* @surface: a #cairo_surface_t with pixel contents
|
||||
* @write_func: a #cairo_write_func_t
|
||||
* @closure: closure data for the write function
|
||||
*
|
||||
* Writes the image surface to the write function.
|
||||
*
|
||||
* Return value: %CAIRO_STATUS_SUCCESS if the PNG file was written
|
||||
* successfully. Otherwise, %CAIRO_STATUS_NO_MEMORY is returned if
|
||||
* memory could not be allocated for the operation,
|
||||
* %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have
|
||||
* pixel contents.
|
||||
**/
|
||||
cairo_status_t
|
||||
cairo_surface_write_to_png_stream (cairo_surface_t *surface,
|
||||
cairo_write_func_t write_func,
|
||||
void *closure)
|
||||
{
|
||||
struct png_write_closure_t png_closure;
|
||||
|
||||
if (surface->status)
|
||||
return surface->status;
|
||||
|
||||
if (surface->finished)
|
||||
return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
|
||||
|
||||
png_closure.write_func = write_func;
|
||||
png_closure.closure = closure;
|
||||
|
||||
return write_png (surface, stream_write_func, &png_closure);
|
||||
}
|
||||
slim_hidden_def (cairo_surface_write_to_png_stream);
|
||||
|
||||
static inline int
|
||||
multiply_alpha (int alpha, int color)
|
||||
{
|
||||
int temp = (alpha * color) + 0x80;
|
||||
return ((temp + (temp >> 8)) >> 8);
|
||||
}
|
||||
|
||||
/* Premultiplies data and converts RGBA bytes => native endian */
|
||||
static void
|
||||
premultiply_data (png_structp png,
|
||||
png_row_infop row_info,
|
||||
png_bytep data)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < row_info->rowbytes; i += 4) {
|
||||
uint8_t *base = &data[i];
|
||||
uint8_t alpha = base[3];
|
||||
uint32_t p;
|
||||
|
||||
if (alpha == 0) {
|
||||
p = 0;
|
||||
} else {
|
||||
uint8_t red = base[0];
|
||||
uint8_t green = base[1];
|
||||
uint8_t blue = base[2];
|
||||
|
||||
if (alpha != 0xff) {
|
||||
red = multiply_alpha (alpha, red);
|
||||
green = multiply_alpha (alpha, green);
|
||||
blue = multiply_alpha (alpha, blue);
|
||||
}
|
||||
p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
|
||||
}
|
||||
memcpy (base, &p, sizeof (uint32_t));
|
||||
}
|
||||
}
|
||||
|
||||
/* Converts RGBx bytes to native endian xRGB */
|
||||
static void
|
||||
convert_bytes_to_data (png_structp png, png_row_infop row_info, png_bytep data)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < row_info->rowbytes; i += 4) {
|
||||
uint8_t *base = &data[i];
|
||||
uint8_t red = base[0];
|
||||
uint8_t green = base[1];
|
||||
uint8_t blue = base[2];
|
||||
uint32_t pixel;
|
||||
|
||||
pixel = (0xff << 24) | (red << 16) | (green << 8) | (blue << 0);
|
||||
memcpy (base, &pixel, sizeof (uint32_t));
|
||||
}
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
stdio_read_func (void *closure, unsigned char *data, unsigned int size)
|
||||
{
|
||||
FILE *file = closure;
|
||||
|
||||
while (size) {
|
||||
size_t ret;
|
||||
|
||||
ret = fread (data, 1, size, file);
|
||||
size -= ret;
|
||||
data += ret;
|
||||
|
||||
if (size && (feof (file) || ferror (file)))
|
||||
return _cairo_error (CAIRO_STATUS_READ_ERROR);
|
||||
}
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
stream_read_func (png_structp png, png_bytep data, png_size_t size)
|
||||
{
|
||||
cairo_status_t status;
|
||||
struct png_read_closure_t *png_closure;
|
||||
|
||||
png_closure = png_get_io_ptr (png);
|
||||
status = png_closure->read_func (png_closure->closure, data, size);
|
||||
if (unlikely (status)) {
|
||||
cairo_status_t *error = png_get_error_ptr (png);
|
||||
if (*error == CAIRO_STATUS_SUCCESS)
|
||||
*error = status;
|
||||
png_error (png, NULL);
|
||||
}
|
||||
|
||||
_cairo_output_stream_write (png_closure->png_data, data, size);
|
||||
}
|
||||
|
||||
static cairo_surface_t *
|
||||
read_png (struct png_read_closure_t *png_closure)
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
png_struct *png = NULL;
|
||||
png_info *info;
|
||||
png_byte *data = NULL;
|
||||
png_byte **row_pointers = NULL;
|
||||
png_uint_32 png_width, png_height;
|
||||
int depth, color_type, interlace, stride;
|
||||
unsigned int i;
|
||||
cairo_format_t format;
|
||||
cairo_status_t status;
|
||||
unsigned char *mime_data;
|
||||
unsigned long mime_data_length;
|
||||
|
||||
png_closure->png_data = _cairo_memory_stream_create ();
|
||||
|
||||
/* XXX: Perhaps we'll want some other error handlers? */
|
||||
png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
|
||||
&status,
|
||||
png_simple_error_callback,
|
||||
png_simple_warning_callback);
|
||||
if (unlikely (png == NULL)) {
|
||||
surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
||||
goto BAIL;
|
||||
}
|
||||
|
||||
info = png_create_info_struct (png);
|
||||
if (unlikely (info == NULL)) {
|
||||
surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
||||
goto BAIL;
|
||||
}
|
||||
|
||||
png_set_read_fn (png, png_closure, stream_read_func);
|
||||
|
||||
status = CAIRO_STATUS_SUCCESS;
|
||||
#ifdef PNG_SETJMP_SUPPORTED
|
||||
if (setjmp (png_jmpbuf (png))) {
|
||||
surface = _cairo_surface_create_in_error (status);
|
||||
goto BAIL;
|
||||
}
|
||||
#endif
|
||||
|
||||
png_read_info (png, info);
|
||||
|
||||
png_get_IHDR (png, info,
|
||||
&png_width, &png_height, &depth,
|
||||
&color_type, &interlace, NULL, NULL);
|
||||
if (unlikely (status)) { /* catch any early warnings */
|
||||
surface = _cairo_surface_create_in_error (status);
|
||||
goto BAIL;
|
||||
}
|
||||
|
||||
/* convert palette/gray image to rgb */
|
||||
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
||||
png_set_palette_to_rgb (png);
|
||||
|
||||
/* expand gray bit depth if needed */
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY) {
|
||||
#if PNG_LIBPNG_VER >= 10209
|
||||
png_set_expand_gray_1_2_4_to_8 (png);
|
||||
#else
|
||||
png_set_gray_1_2_4_to_8 (png);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* transform transparency to alpha */
|
||||
if (png_get_valid (png, info, PNG_INFO_tRNS))
|
||||
png_set_tRNS_to_alpha (png);
|
||||
|
||||
if (depth == 16)
|
||||
png_set_strip_16 (png);
|
||||
|
||||
if (depth < 8)
|
||||
png_set_packing (png);
|
||||
|
||||
/* convert grayscale to RGB */
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY ||
|
||||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||
{
|
||||
png_set_gray_to_rgb (png);
|
||||
}
|
||||
|
||||
if (interlace != PNG_INTERLACE_NONE)
|
||||
png_set_interlace_handling (png);
|
||||
|
||||
png_set_filler (png, 0xff, PNG_FILLER_AFTER);
|
||||
|
||||
/* recheck header after setting EXPAND options */
|
||||
png_read_update_info (png, info);
|
||||
png_get_IHDR (png, info,
|
||||
&png_width, &png_height, &depth,
|
||||
&color_type, &interlace, NULL, NULL);
|
||||
if (depth != 8 ||
|
||||
! (color_type == PNG_COLOR_TYPE_RGB ||
|
||||
color_type == PNG_COLOR_TYPE_RGB_ALPHA))
|
||||
{
|
||||
surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_READ_ERROR));
|
||||
goto BAIL;
|
||||
}
|
||||
|
||||
switch (color_type) {
|
||||
default:
|
||||
ASSERT_NOT_REACHED;
|
||||
/* fall-through just in case ;-) */
|
||||
|
||||
case PNG_COLOR_TYPE_RGB_ALPHA:
|
||||
format = CAIRO_FORMAT_ARGB32;
|
||||
png_set_read_user_transform_fn (png, premultiply_data);
|
||||
break;
|
||||
|
||||
case PNG_COLOR_TYPE_RGB:
|
||||
format = CAIRO_FORMAT_RGB24;
|
||||
png_set_read_user_transform_fn (png, convert_bytes_to_data);
|
||||
break;
|
||||
}
|
||||
|
||||
stride = cairo_format_stride_for_width (format, png_width);
|
||||
if (stride < 0) {
|
||||
surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
|
||||
goto BAIL;
|
||||
}
|
||||
|
||||
data = _cairo_malloc_ab (png_height, stride);
|
||||
if (unlikely (data == NULL)) {
|
||||
surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
||||
goto BAIL;
|
||||
}
|
||||
|
||||
row_pointers = _cairo_malloc_ab (png_height, sizeof (char *));
|
||||
if (unlikely (row_pointers == NULL)) {
|
||||
surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
||||
goto BAIL;
|
||||
}
|
||||
|
||||
for (i = 0; i < png_height; i++)
|
||||
row_pointers[i] = &data[i * stride];
|
||||
|
||||
png_read_image (png, row_pointers);
|
||||
png_read_end (png, info);
|
||||
|
||||
if (unlikely (status)) { /* catch any late warnings - probably hit an error already */
|
||||
surface = _cairo_surface_create_in_error (status);
|
||||
goto BAIL;
|
||||
}
|
||||
|
||||
surface = cairo_image_surface_create_for_data (data, format,
|
||||
png_width, png_height,
|
||||
stride);
|
||||
if (surface->status)
|
||||
goto BAIL;
|
||||
|
||||
_cairo_image_surface_assume_ownership_of_data ((cairo_image_surface_t*)surface);
|
||||
data = NULL;
|
||||
|
||||
_cairo_debug_check_image_surface_is_defined (surface);
|
||||
|
||||
status = _cairo_memory_stream_destroy (png_closure->png_data,
|
||||
&mime_data,
|
||||
&mime_data_length);
|
||||
png_closure->png_data = NULL;
|
||||
if (unlikely (status)) {
|
||||
cairo_surface_destroy (surface);
|
||||
surface = _cairo_surface_create_in_error (status);
|
||||
goto BAIL;
|
||||
}
|
||||
|
||||
status = cairo_surface_set_mime_data (surface,
|
||||
CAIRO_MIME_TYPE_PNG,
|
||||
mime_data,
|
||||
mime_data_length,
|
||||
free,
|
||||
mime_data);
|
||||
if (unlikely (status)) {
|
||||
free (mime_data);
|
||||
cairo_surface_destroy (surface);
|
||||
surface = _cairo_surface_create_in_error (status);
|
||||
goto BAIL;
|
||||
}
|
||||
|
||||
BAIL:
|
||||
if (row_pointers != NULL)
|
||||
free (row_pointers);
|
||||
if (data != NULL)
|
||||
free (data);
|
||||
if (png != NULL)
|
||||
png_destroy_read_struct (&png, &info, NULL);
|
||||
if (png_closure->png_data != NULL) {
|
||||
cairo_status_t status_ignored;
|
||||
|
||||
status_ignored = _cairo_output_stream_destroy (png_closure->png_data);
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
/**
|
||||
* cairo_image_surface_create_from_png:
|
||||
* @filename: name of PNG file to load
|
||||
*
|
||||
* Creates a new image surface and initializes the contents to the
|
||||
* given PNG file.
|
||||
*
|
||||
* Return value: a new #cairo_surface_t initialized with the contents
|
||||
* of the PNG file, or a "nil" surface if any error occurred. A nil
|
||||
* surface can be checked for with cairo_surface_status(surface) which
|
||||
* may return one of the following values:
|
||||
*
|
||||
* %CAIRO_STATUS_NO_MEMORY
|
||||
* %CAIRO_STATUS_FILE_NOT_FOUND
|
||||
* %CAIRO_STATUS_READ_ERROR
|
||||
*
|
||||
* Alternatively, you can allow errors to propagate through the drawing
|
||||
* operations and check the status on the context upon completion
|
||||
* using cairo_status().
|
||||
**/
|
||||
cairo_surface_t *
|
||||
cairo_image_surface_create_from_png (const char *filename)
|
||||
{
|
||||
struct png_read_closure_t png_closure;
|
||||
cairo_surface_t *surface;
|
||||
|
||||
png_closure.closure = fopen (filename, "rb");
|
||||
if (png_closure.closure == NULL) {
|
||||
cairo_status_t status;
|
||||
switch (errno) {
|
||||
case ENOMEM:
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
break;
|
||||
case ENOENT:
|
||||
status = _cairo_error (CAIRO_STATUS_FILE_NOT_FOUND);
|
||||
break;
|
||||
default:
|
||||
status = _cairo_error (CAIRO_STATUS_READ_ERROR);
|
||||
break;
|
||||
}
|
||||
return _cairo_surface_create_in_error (status);
|
||||
}
|
||||
|
||||
png_closure.read_func = stdio_read_func;
|
||||
|
||||
surface = read_png (&png_closure);
|
||||
|
||||
fclose (png_closure.closure);
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
/**
|
||||
* cairo_image_surface_create_from_png_stream:
|
||||
* @read_func: function called to read the data of the file
|
||||
* @closure: data to pass to @read_func.
|
||||
*
|
||||
* Creates a new image surface from PNG data read incrementally
|
||||
* via the @read_func function.
|
||||
*
|
||||
* Return value: a new #cairo_surface_t initialized with the contents
|
||||
* of the PNG file or a "nil" surface if the data read is not a valid PNG image
|
||||
* or memory could not be allocated for the operation. A nil
|
||||
* surface can be checked for with cairo_surface_status(surface) which
|
||||
* may return one of the following values:
|
||||
*
|
||||
* %CAIRO_STATUS_NO_MEMORY
|
||||
* %CAIRO_STATUS_READ_ERROR
|
||||
*
|
||||
* Alternatively, you can allow errors to propagate through the drawing
|
||||
* operations and check the status on the context upon completion
|
||||
* using cairo_status().
|
||||
**/
|
||||
cairo_surface_t *
|
||||
cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func,
|
||||
void *closure)
|
||||
{
|
||||
struct png_read_closure_t png_closure;
|
||||
|
||||
png_closure.read_func = read_func;
|
||||
png_closure.closure = closure;
|
||||
|
||||
return read_png (&png_closure);
|
||||
}
|
||||
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
@@ -0,0 +1,887 @@
|
||||
/* cairo - a vector graphics library with display and print output
|
||||
*
|
||||
* Copyright © 2006 Red Hat, Inc
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it either under the terms of the GNU Lesser General Public
|
||||
* License version 2.1 as published by the Free Software Foundation
|
||||
* (the "LGPL") or, at your option, under the terms of the Mozilla
|
||||
* Public License Version 1.1 (the "MPL"). If you do not alter this
|
||||
* notice, a recipient may use your version of this file under either
|
||||
* the MPL or the LGPL.
|
||||
*
|
||||
* You should have received a copy of the LGPL along with this library
|
||||
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
|
||||
* You should have received a copy of the MPL along with this library
|
||||
* in the file COPYING-MPL-1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.1 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
|
||||
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
|
||||
* the specific language governing rights and limitations.
|
||||
*
|
||||
* The Original Code is the cairo graphics library.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Red Hat, Inc.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Adrian Johnson <ajohnson@redneon.com>
|
||||
*/
|
||||
|
||||
#define _BSD_SOURCE /* for snprintf(), strdup() */
|
||||
#include "cairoint.h"
|
||||
#include "cairo-error-private.h"
|
||||
|
||||
#if CAIRO_HAS_FONT_SUBSET
|
||||
|
||||
#include "cairo-type1-private.h"
|
||||
#include "cairo-scaled-font-subsets-private.h"
|
||||
#include "cairo-path-fixed-private.h"
|
||||
#include "cairo-output-stream-private.h"
|
||||
|
||||
typedef enum {
|
||||
CAIRO_CHARSTRING_TYPE1,
|
||||
CAIRO_CHARSTRING_TYPE2
|
||||
} cairo_charstring_type_t;
|
||||
|
||||
typedef struct _cairo_type1_font {
|
||||
int *widths;
|
||||
|
||||
cairo_scaled_font_subset_t *scaled_font_subset;
|
||||
cairo_scaled_font_t *type1_scaled_font;
|
||||
|
||||
cairo_array_t contents;
|
||||
|
||||
double x_min, y_min, x_max, y_max;
|
||||
|
||||
const char *data;
|
||||
unsigned long header_size;
|
||||
unsigned long data_size;
|
||||
unsigned long trailer_size;
|
||||
int bbox_position;
|
||||
int bbox_max_chars;
|
||||
|
||||
cairo_output_stream_t *output;
|
||||
|
||||
unsigned short eexec_key;
|
||||
cairo_bool_t hex_encode;
|
||||
int hex_column;
|
||||
} cairo_type1_font_t;
|
||||
|
||||
static cairo_status_t
|
||||
cairo_type1_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
|
||||
cairo_type1_font_t **subset_return,
|
||||
cairo_bool_t hex_encode)
|
||||
{
|
||||
cairo_type1_font_t *font;
|
||||
cairo_font_face_t *font_face;
|
||||
cairo_matrix_t font_matrix;
|
||||
cairo_matrix_t ctm;
|
||||
cairo_font_options_t font_options;
|
||||
cairo_status_t status;
|
||||
|
||||
font = calloc (1, sizeof (cairo_type1_font_t));
|
||||
if (unlikely (font == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
font->widths = calloc (scaled_font_subset->num_glyphs, sizeof (int));
|
||||
if (unlikely (font->widths == NULL)) {
|
||||
free (font);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
}
|
||||
|
||||
font->scaled_font_subset = scaled_font_subset;
|
||||
font->hex_encode = hex_encode;
|
||||
|
||||
font_face = cairo_scaled_font_get_font_face (scaled_font_subset->scaled_font);
|
||||
|
||||
cairo_matrix_init_scale (&font_matrix, 1000, -1000);
|
||||
cairo_matrix_init_identity (&ctm);
|
||||
|
||||
_cairo_font_options_init_default (&font_options);
|
||||
cairo_font_options_set_hint_style (&font_options, CAIRO_HINT_STYLE_NONE);
|
||||
cairo_font_options_set_hint_metrics (&font_options, CAIRO_HINT_METRICS_OFF);
|
||||
|
||||
font->type1_scaled_font = cairo_scaled_font_create (font_face,
|
||||
&font_matrix,
|
||||
&ctm,
|
||||
&font_options);
|
||||
status = font->type1_scaled_font->status;
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
|
||||
_cairo_array_init (&font->contents, sizeof (unsigned char));
|
||||
font->output = NULL;
|
||||
|
||||
*subset_return = font;
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
fail:
|
||||
free (font->widths);
|
||||
free (font);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Charstring commands. If the high byte is 0 the command is encoded
|
||||
* with a single byte. */
|
||||
#define CHARSTRING_sbw 0x0c07
|
||||
#define CHARSTRING_rmoveto 0x0015
|
||||
#define CHARSTRING_rlineto 0x0005
|
||||
#define CHARSTRING_rcurveto 0x0008
|
||||
#define CHARSTRING_closepath 0x0009
|
||||
#define CHARSTRING_endchar 0x000e
|
||||
|
||||
/* Before calling this function, the caller must allocate sufficient
|
||||
* space in data (see _cairo_array_grow_by). The maximum number of
|
||||
* bytes that will be used is 2.
|
||||
*/
|
||||
static void
|
||||
charstring_encode_command (cairo_array_t *data, int command)
|
||||
{
|
||||
cairo_status_t status;
|
||||
int orig_size;
|
||||
unsigned char buf[5];
|
||||
unsigned char *p = buf;
|
||||
|
||||
if (command & 0xff00)
|
||||
*p++ = command >> 8;
|
||||
*p++ = command & 0x00ff;
|
||||
|
||||
/* Ensure the array doesn't grow, which allows this function to
|
||||
* have no possibility of failure. */
|
||||
orig_size = _cairo_array_size (data);
|
||||
status = _cairo_array_append_multiple (data, buf, p - buf);
|
||||
|
||||
assert (status == CAIRO_STATUS_SUCCESS);
|
||||
assert (_cairo_array_size (data) == orig_size);
|
||||
}
|
||||
|
||||
/* Before calling this function, the caller must allocate sufficient
|
||||
* space in data (see _cairo_array_grow_by). The maximum number of
|
||||
* bytes that will be used is 5.
|
||||
*/
|
||||
static void
|
||||
charstring_encode_integer (cairo_array_t *data,
|
||||
int i,
|
||||
cairo_charstring_type_t type)
|
||||
{
|
||||
cairo_status_t status;
|
||||
int orig_size;
|
||||
unsigned char buf[10];
|
||||
unsigned char *p = buf;
|
||||
|
||||
if (i >= -107 && i <= 107) {
|
||||
*p++ = i + 139;
|
||||
} else if (i >= 108 && i <= 1131) {
|
||||
i -= 108;
|
||||
*p++ = (i >> 8)+ 247;
|
||||
*p++ = i & 0xff;
|
||||
} else if (i >= -1131 && i <= -108) {
|
||||
i = -i - 108;
|
||||
*p++ = (i >> 8)+ 251;
|
||||
*p++ = i & 0xff;
|
||||
} else {
|
||||
if (type == CAIRO_CHARSTRING_TYPE1) {
|
||||
*p++ = 0xff;
|
||||
*p++ = i >> 24;
|
||||
*p++ = (i >> 16) & 0xff;
|
||||
*p++ = (i >> 8) & 0xff;
|
||||
*p++ = i & 0xff;
|
||||
} else {
|
||||
*p++ = 0xff;
|
||||
*p++ = (i >> 8) & 0xff;
|
||||
*p++ = i & 0xff;
|
||||
*p++ = 0;
|
||||
*p++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure the array doesn't grow, which allows this function to
|
||||
* have no possibility of failure. */
|
||||
orig_size = _cairo_array_size (data);
|
||||
status = _cairo_array_append_multiple (data, buf, p - buf);
|
||||
|
||||
assert (status == CAIRO_STATUS_SUCCESS);
|
||||
assert (_cairo_array_size (data) == orig_size);
|
||||
}
|
||||
|
||||
typedef struct _ps_path_info {
|
||||
cairo_array_t *data;
|
||||
int current_x, current_y;
|
||||
cairo_charstring_type_t type;
|
||||
} t1_path_info_t;
|
||||
|
||||
static cairo_status_t
|
||||
_charstring_move_to (void *closure,
|
||||
const cairo_point_t *point)
|
||||
{
|
||||
t1_path_info_t *path_info = (t1_path_info_t *) closure;
|
||||
int dx, dy;
|
||||
cairo_status_t status;
|
||||
|
||||
status = _cairo_array_grow_by (path_info->data, 12);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
dx = _cairo_fixed_integer_part (point->x) - path_info->current_x;
|
||||
dy = _cairo_fixed_integer_part (point->y) - path_info->current_y;
|
||||
charstring_encode_integer (path_info->data, dx, path_info->type);
|
||||
charstring_encode_integer (path_info->data, dy, path_info->type);
|
||||
path_info->current_x += dx;
|
||||
path_info->current_y += dy;
|
||||
|
||||
charstring_encode_command (path_info->data, CHARSTRING_rmoveto);
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
_charstring_line_to (void *closure,
|
||||
const cairo_point_t *point)
|
||||
{
|
||||
t1_path_info_t *path_info = (t1_path_info_t *) closure;
|
||||
int dx, dy;
|
||||
cairo_status_t status;
|
||||
|
||||
status = _cairo_array_grow_by (path_info->data, 12);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
dx = _cairo_fixed_integer_part (point->x) - path_info->current_x;
|
||||
dy = _cairo_fixed_integer_part (point->y) - path_info->current_y;
|
||||
charstring_encode_integer (path_info->data, dx, path_info->type);
|
||||
charstring_encode_integer (path_info->data, dy, path_info->type);
|
||||
path_info->current_x += dx;
|
||||
path_info->current_y += dy;
|
||||
|
||||
charstring_encode_command (path_info->data, CHARSTRING_rlineto);
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
_charstring_curve_to (void *closure,
|
||||
const cairo_point_t *point1,
|
||||
const cairo_point_t *point2,
|
||||
const cairo_point_t *point3)
|
||||
{
|
||||
t1_path_info_t *path_info = (t1_path_info_t *) closure;
|
||||
int dx1, dy1, dx2, dy2, dx3, dy3;
|
||||
cairo_status_t status;
|
||||
|
||||
status = _cairo_array_grow_by (path_info->data, 32);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
dx1 = _cairo_fixed_integer_part (point1->x) - path_info->current_x;
|
||||
dy1 = _cairo_fixed_integer_part (point1->y) - path_info->current_y;
|
||||
dx2 = _cairo_fixed_integer_part (point2->x) - path_info->current_x - dx1;
|
||||
dy2 = _cairo_fixed_integer_part (point2->y) - path_info->current_y - dy1;
|
||||
dx3 = _cairo_fixed_integer_part (point3->x) - path_info->current_x - dx1 - dx2;
|
||||
dy3 = _cairo_fixed_integer_part (point3->y) - path_info->current_y - dy1 - dy2;
|
||||
charstring_encode_integer (path_info->data, dx1, path_info->type);
|
||||
charstring_encode_integer (path_info->data, dy1, path_info->type);
|
||||
charstring_encode_integer (path_info->data, dx2, path_info->type);
|
||||
charstring_encode_integer (path_info->data, dy2, path_info->type);
|
||||
charstring_encode_integer (path_info->data, dx3, path_info->type);
|
||||
charstring_encode_integer (path_info->data, dy3, path_info->type);
|
||||
path_info->current_x += dx1 + dx2 + dx3;
|
||||
path_info->current_y += dy1 + dy2 + dy3;
|
||||
charstring_encode_command (path_info->data, CHARSTRING_rcurveto);
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
_charstring_close_path (void *closure)
|
||||
{
|
||||
cairo_status_t status;
|
||||
t1_path_info_t *path_info = (t1_path_info_t *) closure;
|
||||
|
||||
if (path_info->type == CAIRO_CHARSTRING_TYPE2)
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
status = _cairo_array_grow_by (path_info->data, 2);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
charstring_encode_command (path_info->data, CHARSTRING_closepath);
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
charstring_encrypt (cairo_array_t *data)
|
||||
{
|
||||
unsigned char *d, *end;
|
||||
uint16_t c, p, r;
|
||||
|
||||
r = CAIRO_TYPE1_CHARSTRING_KEY;
|
||||
d = (unsigned char *) _cairo_array_index (data, 0);
|
||||
end = d + _cairo_array_num_elements (data);
|
||||
while (d < end) {
|
||||
p = *d;
|
||||
c = p ^ (r >> 8);
|
||||
r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2;
|
||||
*d++ = c;
|
||||
}
|
||||
}
|
||||
|
||||
static cairo_int_status_t
|
||||
cairo_type1_font_create_charstring (cairo_type1_font_t *font,
|
||||
int subset_index,
|
||||
int glyph_index,
|
||||
cairo_charstring_type_t type,
|
||||
cairo_array_t *data)
|
||||
{
|
||||
cairo_int_status_t status;
|
||||
cairo_scaled_glyph_t *scaled_glyph;
|
||||
t1_path_info_t path_info;
|
||||
cairo_text_extents_t *metrics;
|
||||
cairo_bool_t emit_path = TRUE;
|
||||
|
||||
/* This call may return CAIRO_INT_STATUS_UNSUPPORTED for bitmap fonts. */
|
||||
status = _cairo_scaled_glyph_lookup (font->type1_scaled_font,
|
||||
glyph_index,
|
||||
CAIRO_SCALED_GLYPH_INFO_METRICS|
|
||||
CAIRO_SCALED_GLYPH_INFO_PATH,
|
||||
&scaled_glyph);
|
||||
|
||||
/* It is ok for the .notdef glyph to not have a path available. We
|
||||
* just need the metrics to emit an empty glyph. */
|
||||
if (glyph_index == 0 && status == CAIRO_INT_STATUS_UNSUPPORTED) {
|
||||
emit_path = FALSE;
|
||||
status = _cairo_scaled_glyph_lookup (font->type1_scaled_font,
|
||||
glyph_index,
|
||||
CAIRO_SCALED_GLYPH_INFO_METRICS,
|
||||
&scaled_glyph);
|
||||
}
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
metrics = &scaled_glyph->metrics;
|
||||
if (subset_index == 0) {
|
||||
font->x_min = metrics->x_bearing;
|
||||
font->y_min = metrics->y_bearing;
|
||||
font->x_max = metrics->x_bearing + metrics->width;
|
||||
font->y_max = metrics->y_bearing + metrics->height;
|
||||
} else {
|
||||
if (metrics->x_bearing < font->x_min)
|
||||
font->x_min = metrics->x_bearing;
|
||||
if (metrics->y_bearing < font->y_min)
|
||||
font->y_min = metrics->y_bearing;
|
||||
if (metrics->x_bearing + metrics->width > font->x_max)
|
||||
font->x_max = metrics->x_bearing + metrics->width;
|
||||
if (metrics->y_bearing + metrics->height > font->y_max)
|
||||
font->y_max = metrics->y_bearing + metrics->height;
|
||||
}
|
||||
font->widths[subset_index] = metrics->x_advance;
|
||||
|
||||
status = _cairo_array_grow_by (data, 30);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
if (type == CAIRO_CHARSTRING_TYPE1) {
|
||||
charstring_encode_integer (data, (int) scaled_glyph->metrics.x_bearing, type);
|
||||
charstring_encode_integer (data, (int) scaled_glyph->metrics.y_bearing, type);
|
||||
charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type);
|
||||
charstring_encode_integer (data, (int) scaled_glyph->metrics.y_advance, type);
|
||||
charstring_encode_command (data, CHARSTRING_sbw);
|
||||
|
||||
path_info.current_x = (int) scaled_glyph->metrics.x_bearing;
|
||||
path_info.current_y = (int) scaled_glyph->metrics.y_bearing;
|
||||
} else {
|
||||
charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type);
|
||||
|
||||
path_info.current_x = 0;
|
||||
path_info.current_y = 0;
|
||||
}
|
||||
path_info.data = data;
|
||||
path_info.type = type;
|
||||
if (emit_path) {
|
||||
status = _cairo_path_fixed_interpret (scaled_glyph->path,
|
||||
CAIRO_DIRECTION_FORWARD,
|
||||
_charstring_move_to,
|
||||
_charstring_line_to,
|
||||
_charstring_curve_to,
|
||||
_charstring_close_path,
|
||||
&path_info);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
}
|
||||
|
||||
status = _cairo_array_grow_by (data, 1);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
charstring_encode_command (path_info.data, CHARSTRING_endchar);
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static cairo_int_status_t
|
||||
cairo_type1_font_write_charstrings (cairo_type1_font_t *font,
|
||||
cairo_output_stream_t *encrypted_output)
|
||||
{
|
||||
cairo_status_t status;
|
||||
unsigned char zeros[] = { 0, 0, 0, 0 };
|
||||
cairo_array_t data;
|
||||
unsigned int i;
|
||||
int length;
|
||||
|
||||
_cairo_array_init (&data, sizeof (unsigned char));
|
||||
status = _cairo_array_grow_by (&data, 1024);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
|
||||
_cairo_output_stream_printf (encrypted_output,
|
||||
"2 index /CharStrings %d dict dup begin\n",
|
||||
font->scaled_font_subset->num_glyphs + 1);
|
||||
|
||||
_cairo_scaled_font_freeze_cache (font->type1_scaled_font);
|
||||
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
|
||||
_cairo_array_truncate (&data, 0);
|
||||
/* four "random" bytes required by encryption algorithm */
|
||||
status = _cairo_array_append_multiple (&data, zeros, 4);
|
||||
if (unlikely (status))
|
||||
break;
|
||||
|
||||
status = cairo_type1_font_create_charstring (font, i,
|
||||
font->scaled_font_subset->glyphs[i],
|
||||
CAIRO_CHARSTRING_TYPE1,
|
||||
&data);
|
||||
if (unlikely (status))
|
||||
break;
|
||||
|
||||
charstring_encrypt (&data);
|
||||
length = _cairo_array_num_elements (&data);
|
||||
if (font->scaled_font_subset->glyph_names != NULL) {
|
||||
_cairo_output_stream_printf (encrypted_output, "/%s %d RD ",
|
||||
font->scaled_font_subset->glyph_names[i],
|
||||
length);
|
||||
} else if (i == 0) {
|
||||
_cairo_output_stream_printf (encrypted_output, "/.notdef %d RD ", length);
|
||||
} else {
|
||||
_cairo_output_stream_printf (encrypted_output, "/g%d %d RD ", i, length);
|
||||
}
|
||||
_cairo_output_stream_write (encrypted_output,
|
||||
_cairo_array_index (&data, 0),
|
||||
length);
|
||||
_cairo_output_stream_printf (encrypted_output, " ND\n");
|
||||
}
|
||||
_cairo_scaled_font_thaw_cache (font->type1_scaled_font);
|
||||
|
||||
fail:
|
||||
_cairo_array_fini (&data);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
cairo_type1_font_write_header (cairo_type1_font_t *font,
|
||||
const char *name)
|
||||
{
|
||||
unsigned int i;
|
||||
const char spaces[50] = " ";
|
||||
|
||||
_cairo_output_stream_printf (font->output,
|
||||
"%%!FontType1-1.1 %s 1.0\n"
|
||||
"11 dict begin\n"
|
||||
"/FontName /%s def\n"
|
||||
"/PaintType 0 def\n"
|
||||
"/FontType 1 def\n"
|
||||
"/FontMatrix [0.001 0 0 0.001 0 0] readonly def\n",
|
||||
name,
|
||||
name);
|
||||
|
||||
/* We don't know the bbox values until after the charstrings have
|
||||
* been generated. Reserve some space and fill in the bbox
|
||||
* later. */
|
||||
|
||||
/* Worst case for four signed ints with spaces between each number */
|
||||
font->bbox_max_chars = 50;
|
||||
|
||||
_cairo_output_stream_printf (font->output, "/FontBBox {");
|
||||
font->bbox_position = _cairo_output_stream_get_position (font->output);
|
||||
_cairo_output_stream_write (font->output, spaces, font->bbox_max_chars);
|
||||
|
||||
_cairo_output_stream_printf (font->output,
|
||||
"} readonly def\n"
|
||||
"/Encoding 256 array\n"
|
||||
"0 1 255 {1 index exch /.notdef put} for\n");
|
||||
for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
|
||||
if (font->scaled_font_subset->glyph_names != NULL) {
|
||||
_cairo_output_stream_printf (font->output, "dup %d /%s put\n",
|
||||
i, font->scaled_font_subset->glyph_names[i]);
|
||||
} else {
|
||||
_cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, i);
|
||||
}
|
||||
}
|
||||
_cairo_output_stream_printf (font->output,
|
||||
"readonly def\n"
|
||||
"currentdict end\n"
|
||||
"currentfile eexec\n");
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
cairo_type1_write_stream_encrypted (void *closure,
|
||||
const unsigned char *data,
|
||||
unsigned int length)
|
||||
{
|
||||
const unsigned char *in, *end;
|
||||
uint16_t c, p;
|
||||
static const char hex_digits[16] = "0123456789abcdef";
|
||||
char digits[3];
|
||||
cairo_type1_font_t *font = closure;
|
||||
|
||||
in = (const unsigned char *) data;
|
||||
end = (const unsigned char *) data + length;
|
||||
while (in < end) {
|
||||
p = *in++;
|
||||
c = p ^ (font->eexec_key >> 8);
|
||||
font->eexec_key = (c + font->eexec_key) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2;
|
||||
|
||||
if (font->hex_encode) {
|
||||
digits[0] = hex_digits[c >> 4];
|
||||
digits[1] = hex_digits[c & 0x0f];
|
||||
digits[2] = '\n';
|
||||
font->hex_column += 2;
|
||||
|
||||
if (font->hex_column == 78) {
|
||||
_cairo_output_stream_write (font->output, digits, 3);
|
||||
font->hex_column = 0;
|
||||
} else {
|
||||
_cairo_output_stream_write (font->output, digits, 2);
|
||||
}
|
||||
} else {
|
||||
digits[0] = c;
|
||||
_cairo_output_stream_write (font->output, digits, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static cairo_int_status_t
|
||||
cairo_type1_font_write_private_dict (cairo_type1_font_t *font,
|
||||
const char *name)
|
||||
{
|
||||
cairo_int_status_t status;
|
||||
cairo_status_t status2;
|
||||
cairo_output_stream_t *encrypted_output;
|
||||
|
||||
font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY;
|
||||
font->hex_column = 0;
|
||||
encrypted_output = _cairo_output_stream_create (
|
||||
cairo_type1_write_stream_encrypted,
|
||||
NULL,
|
||||
font);
|
||||
if (_cairo_output_stream_get_status (encrypted_output))
|
||||
return _cairo_output_stream_destroy (encrypted_output);
|
||||
|
||||
/* Note: the first four spaces at the start of this private dict
|
||||
* are the four "random" bytes of plaintext required by the
|
||||
* encryption algorithm */
|
||||
_cairo_output_stream_printf (encrypted_output,
|
||||
" dup /Private 9 dict dup begin\n"
|
||||
"/RD {string currentfile exch readstring pop}"
|
||||
" bind executeonly def\n"
|
||||
"/ND {noaccess def} executeonly def\n"
|
||||
"/NP {noaccess put} executeonly def\n"
|
||||
"/BlueValues [] def\n"
|
||||
"/MinFeature {16 16} def\n"
|
||||
"/lenIV 4 def\n"
|
||||
"/password 5839 def\n");
|
||||
|
||||
status = cairo_type1_font_write_charstrings (font, encrypted_output);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
|
||||
_cairo_output_stream_printf (encrypted_output,
|
||||
"end\n"
|
||||
"end\n"
|
||||
"readonly put\n"
|
||||
"noaccess put\n"
|
||||
"dup /FontName get exch definefont pop\n"
|
||||
"mark currentfile closefile\n");
|
||||
|
||||
fail:
|
||||
status2 = _cairo_output_stream_destroy (encrypted_output);
|
||||
if (status == CAIRO_STATUS_SUCCESS)
|
||||
status = status2;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
cairo_type1_font_write_trailer(cairo_type1_font_t *font)
|
||||
{
|
||||
int i;
|
||||
static const char zeros[65] =
|
||||
"0000000000000000000000000000000000000000000000000000000000000000\n";
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
_cairo_output_stream_write (font->output, zeros, sizeof zeros);
|
||||
|
||||
_cairo_output_stream_printf (font->output, "cleartomark\n");
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
cairo_type1_write_stream (void *closure,
|
||||
const unsigned char *data,
|
||||
unsigned int length)
|
||||
{
|
||||
cairo_type1_font_t *font = closure;
|
||||
|
||||
return _cairo_array_append_multiple (&font->contents, data, length);
|
||||
}
|
||||
|
||||
static cairo_int_status_t
|
||||
cairo_type1_font_write (cairo_type1_font_t *font,
|
||||
const char *name)
|
||||
{
|
||||
cairo_int_status_t status;
|
||||
|
||||
cairo_type1_font_write_header (font, name);
|
||||
font->header_size = _cairo_output_stream_get_position (font->output);
|
||||
|
||||
status = cairo_type1_font_write_private_dict (font, name);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
font->data_size = _cairo_output_stream_get_position (font->output) -
|
||||
font->header_size;
|
||||
|
||||
cairo_type1_font_write_trailer (font);
|
||||
font->trailer_size =
|
||||
_cairo_output_stream_get_position (font->output) -
|
||||
font->header_size - font->data_size;
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static cairo_int_status_t
|
||||
cairo_type1_font_generate (cairo_type1_font_t *font, const char *name)
|
||||
{
|
||||
cairo_int_status_t status;
|
||||
|
||||
status = _cairo_array_grow_by (&font->contents, 4096);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
font->output = _cairo_output_stream_create (cairo_type1_write_stream, NULL, font);
|
||||
if (_cairo_output_stream_get_status (font->output))
|
||||
return _cairo_output_stream_destroy (font->output);
|
||||
|
||||
status = cairo_type1_font_write (font, name);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
font->data = _cairo_array_index (&font->contents, 0);
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
cairo_type1_font_destroy (cairo_type1_font_t *font)
|
||||
{
|
||||
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
||||
|
||||
free (font->widths);
|
||||
cairo_scaled_font_destroy (font->type1_scaled_font);
|
||||
_cairo_array_fini (&font->contents);
|
||||
if (font->output)
|
||||
status = _cairo_output_stream_destroy (font->output);
|
||||
free (font);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
_cairo_type1_fallback_init_internal (cairo_type1_subset_t *type1_subset,
|
||||
const char *name,
|
||||
cairo_scaled_font_subset_t *scaled_font_subset,
|
||||
cairo_bool_t hex_encode)
|
||||
{
|
||||
cairo_type1_font_t *font;
|
||||
cairo_status_t status;
|
||||
unsigned long length;
|
||||
unsigned int i, len;
|
||||
|
||||
status = cairo_type1_font_create (scaled_font_subset, &font, hex_encode);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
status = cairo_type1_font_generate (font, name);
|
||||
if (unlikely (status))
|
||||
goto fail1;
|
||||
|
||||
type1_subset->base_font = strdup (name);
|
||||
if (unlikely (type1_subset->base_font == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
type1_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs);
|
||||
if (unlikely (type1_subset->widths == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail2;
|
||||
}
|
||||
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
|
||||
type1_subset->widths[i] = (double)font->widths[i]/1000;
|
||||
|
||||
type1_subset->x_min = (double)font->x_min/1000;
|
||||
type1_subset->y_min = (double)font->y_min/1000;
|
||||
type1_subset->x_max = (double)font->x_max/1000;
|
||||
type1_subset->y_max = (double)font->y_max/1000;
|
||||
type1_subset->ascent = (double)font->y_max/1000;
|
||||
type1_subset->descent = (double)font->y_min/1000;
|
||||
|
||||
length = font->header_size + font->data_size +
|
||||
font->trailer_size;
|
||||
type1_subset->data = malloc (length);
|
||||
if (unlikely (type1_subset->data == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail3;
|
||||
}
|
||||
memcpy (type1_subset->data,
|
||||
_cairo_array_index (&font->contents, 0), length);
|
||||
|
||||
len = snprintf(type1_subset->data + font->bbox_position,
|
||||
font->bbox_max_chars,
|
||||
"%d %d %d %d",
|
||||
(int)font->x_min,
|
||||
(int)font->y_min,
|
||||
(int)font->x_max,
|
||||
(int)font->y_max);
|
||||
type1_subset->data[font->bbox_position + len] = ' ';
|
||||
|
||||
type1_subset->header_length = font->header_size;
|
||||
type1_subset->data_length = font->data_size;
|
||||
type1_subset->trailer_length = font->trailer_size;
|
||||
|
||||
return cairo_type1_font_destroy (font);
|
||||
|
||||
fail3:
|
||||
free (type1_subset->widths);
|
||||
fail2:
|
||||
free (type1_subset->base_font);
|
||||
fail1:
|
||||
/* status is already set, ignore further errors */
|
||||
cairo_type1_font_destroy (font);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
cairo_status_t
|
||||
_cairo_type1_fallback_init_binary (cairo_type1_subset_t *type1_subset,
|
||||
const char *name,
|
||||
cairo_scaled_font_subset_t *scaled_font_subset)
|
||||
{
|
||||
return _cairo_type1_fallback_init_internal (type1_subset,
|
||||
name,
|
||||
scaled_font_subset, FALSE);
|
||||
}
|
||||
|
||||
cairo_status_t
|
||||
_cairo_type1_fallback_init_hex (cairo_type1_subset_t *type1_subset,
|
||||
const char *name,
|
||||
cairo_scaled_font_subset_t *scaled_font_subset)
|
||||
{
|
||||
return _cairo_type1_fallback_init_internal (type1_subset,
|
||||
name,
|
||||
scaled_font_subset, TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
_cairo_type1_fallback_fini (cairo_type1_subset_t *subset)
|
||||
{
|
||||
free (subset->base_font);
|
||||
free (subset->widths);
|
||||
free (subset->data);
|
||||
}
|
||||
|
||||
cairo_status_t
|
||||
_cairo_type2_charstrings_init (cairo_type2_charstrings_t *type2_subset,
|
||||
cairo_scaled_font_subset_t *scaled_font_subset)
|
||||
{
|
||||
cairo_type1_font_t *font;
|
||||
cairo_status_t status;
|
||||
unsigned int i;
|
||||
cairo_array_t charstring;
|
||||
|
||||
status = cairo_type1_font_create (scaled_font_subset, &font, FALSE);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
_cairo_array_init (&type2_subset->charstrings, sizeof (cairo_array_t));
|
||||
|
||||
type2_subset->widths = calloc (sizeof (int), font->scaled_font_subset->num_glyphs);
|
||||
if (unlikely (type2_subset->widths == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
_cairo_scaled_font_freeze_cache (font->type1_scaled_font);
|
||||
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
|
||||
_cairo_array_init (&charstring, sizeof (unsigned char));
|
||||
status = _cairo_array_grow_by (&charstring, 32);
|
||||
if (unlikely (status))
|
||||
goto fail2;
|
||||
|
||||
status = cairo_type1_font_create_charstring (font, i,
|
||||
font->scaled_font_subset->glyphs[i],
|
||||
CAIRO_CHARSTRING_TYPE2,
|
||||
&charstring);
|
||||
if (unlikely (status))
|
||||
goto fail2;
|
||||
|
||||
status = _cairo_array_append (&type2_subset->charstrings, &charstring);
|
||||
if (unlikely (status))
|
||||
goto fail2;
|
||||
}
|
||||
_cairo_scaled_font_thaw_cache (font->type1_scaled_font);
|
||||
|
||||
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
|
||||
type2_subset->widths[i] = font->widths[i];
|
||||
|
||||
type2_subset->x_min = (int) font->x_min;
|
||||
type2_subset->y_min = (int) font->y_min;
|
||||
type2_subset->x_max = (int) font->x_max;
|
||||
type2_subset->y_max = (int) font->y_max;
|
||||
type2_subset->ascent = (int) font->y_max;
|
||||
type2_subset->descent = (int) font->y_min;
|
||||
|
||||
return cairo_type1_font_destroy (font);
|
||||
|
||||
fail2:
|
||||
_cairo_scaled_font_thaw_cache (font->type1_scaled_font);
|
||||
_cairo_array_fini (&charstring);
|
||||
_cairo_type2_charstrings_fini (type2_subset);
|
||||
fail1:
|
||||
cairo_type1_font_destroy (font);
|
||||
return status;
|
||||
}
|
||||
|
||||
void
|
||||
_cairo_type2_charstrings_fini (cairo_type2_charstrings_t *type2_subset)
|
||||
{
|
||||
unsigned int i, num_charstrings;
|
||||
cairo_array_t *charstring;
|
||||
|
||||
num_charstrings = _cairo_array_num_elements (&type2_subset->charstrings);
|
||||
for (i = 0; i < num_charstrings; i++) {
|
||||
charstring = _cairo_array_index (&type2_subset->charstrings, i);
|
||||
_cairo_array_fini (charstring);
|
||||
}
|
||||
_cairo_array_fini (&type2_subset->charstrings);
|
||||
|
||||
free (type2_subset->widths);
|
||||
}
|
||||
|
||||
#endif /* CAIRO_HAS_FONT_SUBSET */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,563 @@
|
||||
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
|
||||
/* cairo - a vector graphics library with display and print output
|
||||
*
|
||||
* Copyright © 2008 Adrian Johnson
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it either under the terms of the GNU Lesser General Public
|
||||
* License version 2.1 as published by the Free Software Foundation
|
||||
* (the "LGPL") or, at your option, under the terms of the Mozilla
|
||||
* Public License Version 1.1 (the "MPL"). If you do not alter this
|
||||
* notice, a recipient may use your version of this file under either
|
||||
* the MPL or the LGPL.
|
||||
*
|
||||
* You should have received a copy of the LGPL along with this library
|
||||
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
|
||||
* You should have received a copy of the MPL along with this library
|
||||
* in the file COPYING-MPL-1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.1 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
|
||||
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
|
||||
* the specific language governing rights and limitations.
|
||||
*
|
||||
* The Original Code is the cairo graphics library.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Adrian Johnson.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Adrian Johnson <ajohnson@redneon.com>
|
||||
*/
|
||||
|
||||
#include "cairoint.h"
|
||||
|
||||
#if CAIRO_HAS_FONT_SUBSET
|
||||
|
||||
#include "cairo-type3-glyph-surface-private.h"
|
||||
#include "cairo-output-stream-private.h"
|
||||
#include "cairo-recording-surface-private.h"
|
||||
#include "cairo-analysis-surface-private.h"
|
||||
#include "cairo-error-private.h"
|
||||
#include "cairo-surface-clipper-private.h"
|
||||
|
||||
static const cairo_surface_backend_t cairo_type3_glyph_surface_backend;
|
||||
|
||||
static cairo_status_t
|
||||
_cairo_type3_glyph_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
|
||||
cairo_path_fixed_t *path,
|
||||
cairo_fill_rule_t fill_rule,
|
||||
double tolerance,
|
||||
cairo_antialias_t antialias)
|
||||
{
|
||||
cairo_type3_glyph_surface_t *surface = cairo_container_of (clipper,
|
||||
cairo_type3_glyph_surface_t,
|
||||
clipper);
|
||||
|
||||
if (path == NULL) {
|
||||
_cairo_output_stream_printf (surface->stream, "Q q\n");
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return _cairo_pdf_operators_clip (&surface->pdf_operators,
|
||||
path,
|
||||
fill_rule);
|
||||
}
|
||||
|
||||
cairo_surface_t *
|
||||
_cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font,
|
||||
cairo_output_stream_t *stream,
|
||||
cairo_type3_glyph_surface_emit_image_t emit_image,
|
||||
cairo_scaled_font_subsets_t *font_subsets)
|
||||
{
|
||||
cairo_type3_glyph_surface_t *surface;
|
||||
cairo_matrix_t invert_y_axis;
|
||||
|
||||
if (unlikely (stream != NULL && stream->status))
|
||||
return _cairo_surface_create_in_error (stream->status);
|
||||
|
||||
surface = malloc (sizeof (cairo_type3_glyph_surface_t));
|
||||
if (unlikely (surface == NULL))
|
||||
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
||||
|
||||
_cairo_surface_init (&surface->base,
|
||||
&cairo_type3_glyph_surface_backend,
|
||||
NULL, /* device */
|
||||
CAIRO_CONTENT_COLOR_ALPHA);
|
||||
|
||||
surface->scaled_font = scaled_font;
|
||||
surface->stream = stream;
|
||||
surface->emit_image = emit_image;
|
||||
|
||||
/* Setup the transform from the user-font device space to Type 3
|
||||
* font space. The Type 3 font space is defined by the FontMatrix
|
||||
* entry in the Type 3 dictionary. In the PDF backend this is an
|
||||
* identity matrix. */
|
||||
surface->cairo_to_pdf = scaled_font->scale_inverse;
|
||||
cairo_matrix_init_scale (&invert_y_axis, 1, -1);
|
||||
cairo_matrix_multiply (&surface->cairo_to_pdf, &surface->cairo_to_pdf, &invert_y_axis);
|
||||
|
||||
_cairo_pdf_operators_init (&surface->pdf_operators,
|
||||
surface->stream,
|
||||
&surface->cairo_to_pdf,
|
||||
font_subsets);
|
||||
|
||||
_cairo_surface_clipper_init (&surface->clipper,
|
||||
_cairo_type3_glyph_surface_clipper_intersect_clip_path);
|
||||
|
||||
return &surface->base;
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
_cairo_type3_glyph_surface_emit_image (cairo_type3_glyph_surface_t *surface,
|
||||
cairo_image_surface_t *image,
|
||||
cairo_matrix_t *image_matrix)
|
||||
{
|
||||
cairo_status_t status;
|
||||
|
||||
/* The only image type supported by Type 3 fonts are 1-bit masks */
|
||||
image = _cairo_image_surface_coerce_to_format (image, CAIRO_FORMAT_A1);
|
||||
status = image->base.status;
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
_cairo_output_stream_printf (surface->stream,
|
||||
"q %f %f %f %f %f %f cm\n",
|
||||
image_matrix->xx,
|
||||
image_matrix->xy,
|
||||
image_matrix->yx,
|
||||
image_matrix->yy,
|
||||
image_matrix->x0,
|
||||
image_matrix->y0);
|
||||
|
||||
status = surface->emit_image (image, surface->stream);
|
||||
cairo_surface_destroy (&image->base);
|
||||
|
||||
_cairo_output_stream_printf (surface->stream,
|
||||
"Q\n");
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
_cairo_type3_glyph_surface_emit_image_pattern (cairo_type3_glyph_surface_t *surface,
|
||||
cairo_image_surface_t *image,
|
||||
const cairo_matrix_t *pattern_matrix)
|
||||
{
|
||||
cairo_matrix_t mat, upside_down;
|
||||
cairo_status_t status;
|
||||
|
||||
if (image->width == 0 || image->height == 0)
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
mat = *pattern_matrix;
|
||||
|
||||
/* Get the pattern space to user space matrix */
|
||||
status = cairo_matrix_invert (&mat);
|
||||
|
||||
/* cairo_pattern_set_matrix ensures the matrix is invertible */
|
||||
assert (status == CAIRO_STATUS_SUCCESS);
|
||||
|
||||
/* Make this a pattern space to Type 3 font space matrix */
|
||||
cairo_matrix_multiply (&mat, &mat, &surface->cairo_to_pdf);
|
||||
|
||||
/* PDF images are in a 1 unit by 1 unit image space. Turn the 1 by
|
||||
* 1 image upside down to convert to flip the Y-axis going from
|
||||
* cairo to PDF. Then scale the image up to the required size. */
|
||||
cairo_matrix_scale (&mat, image->width, image->height);
|
||||
cairo_matrix_init (&upside_down, 1, 0, 0, -1, 0, 1);
|
||||
cairo_matrix_multiply (&mat, &upside_down, &mat);
|
||||
|
||||
return _cairo_type3_glyph_surface_emit_image (surface, image, &mat);
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
_cairo_type3_glyph_surface_finish (void *abstract_surface)
|
||||
{
|
||||
cairo_type3_glyph_surface_t *surface = abstract_surface;
|
||||
|
||||
return _cairo_pdf_operators_fini (&surface->pdf_operators);
|
||||
}
|
||||
|
||||
static cairo_int_status_t
|
||||
_cairo_type3_glyph_surface_paint (void *abstract_surface,
|
||||
cairo_operator_t op,
|
||||
const cairo_pattern_t *source,
|
||||
cairo_clip_t *clip)
|
||||
{
|
||||
cairo_type3_glyph_surface_t *surface = abstract_surface;
|
||||
const cairo_surface_pattern_t *pattern;
|
||||
cairo_image_surface_t *image;
|
||||
void *image_extra;
|
||||
cairo_status_t status;
|
||||
|
||||
if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
|
||||
return CAIRO_INT_STATUS_IMAGE_FALLBACK;
|
||||
|
||||
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
pattern = (const cairo_surface_pattern_t *) source;
|
||||
status = _cairo_surface_acquire_source_image (pattern->surface,
|
||||
&image, &image_extra);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
|
||||
status = _cairo_type3_glyph_surface_emit_image_pattern (surface,
|
||||
image,
|
||||
&pattern->base.matrix);
|
||||
|
||||
fail:
|
||||
_cairo_surface_release_source_image (pattern->surface, image, image_extra);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static cairo_int_status_t
|
||||
_cairo_type3_glyph_surface_mask (void *abstract_surface,
|
||||
cairo_operator_t op,
|
||||
const cairo_pattern_t *source,
|
||||
const cairo_pattern_t *mask,
|
||||
cairo_clip_t *clip)
|
||||
{
|
||||
return _cairo_type3_glyph_surface_paint (abstract_surface,
|
||||
op, mask,
|
||||
clip);
|
||||
}
|
||||
|
||||
static cairo_int_status_t
|
||||
_cairo_type3_glyph_surface_stroke (void *abstract_surface,
|
||||
cairo_operator_t op,
|
||||
const cairo_pattern_t *source,
|
||||
cairo_path_fixed_t *path,
|
||||
const cairo_stroke_style_t *style,
|
||||
const cairo_matrix_t *ctm,
|
||||
const cairo_matrix_t *ctm_inverse,
|
||||
double tolerance,
|
||||
cairo_antialias_t antialias,
|
||||
cairo_clip_t *clip)
|
||||
{
|
||||
cairo_type3_glyph_surface_t *surface = abstract_surface;
|
||||
cairo_int_status_t status;
|
||||
|
||||
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
return _cairo_pdf_operators_stroke (&surface->pdf_operators,
|
||||
path,
|
||||
style,
|
||||
ctm,
|
||||
ctm_inverse);
|
||||
}
|
||||
|
||||
static cairo_int_status_t
|
||||
_cairo_type3_glyph_surface_fill (void *abstract_surface,
|
||||
cairo_operator_t op,
|
||||
const cairo_pattern_t *source,
|
||||
cairo_path_fixed_t *path,
|
||||
cairo_fill_rule_t fill_rule,
|
||||
double tolerance,
|
||||
cairo_antialias_t antialias,
|
||||
cairo_clip_t *clip)
|
||||
{
|
||||
cairo_type3_glyph_surface_t *surface = abstract_surface;
|
||||
cairo_int_status_t status;
|
||||
|
||||
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
return _cairo_pdf_operators_fill (&surface->pdf_operators,
|
||||
path,
|
||||
fill_rule);
|
||||
}
|
||||
|
||||
static cairo_int_status_t
|
||||
_cairo_type3_glyph_surface_show_glyphs (void *abstract_surface,
|
||||
cairo_operator_t op,
|
||||
const cairo_pattern_t *source,
|
||||
cairo_glyph_t *glyphs,
|
||||
int num_glyphs,
|
||||
cairo_scaled_font_t *scaled_font,
|
||||
cairo_clip_t *clip,
|
||||
int *remaining_glyphs)
|
||||
{
|
||||
cairo_type3_glyph_surface_t *surface = abstract_surface;
|
||||
cairo_int_status_t status;
|
||||
cairo_scaled_font_t *font;
|
||||
cairo_matrix_t new_ctm, invert_y_axis;
|
||||
|
||||
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
cairo_matrix_init_scale (&invert_y_axis, 1, -1);
|
||||
cairo_matrix_multiply (&new_ctm, &invert_y_axis, &scaled_font->ctm);
|
||||
cairo_matrix_multiply (&new_ctm, &surface->cairo_to_pdf, &new_ctm);
|
||||
font = cairo_scaled_font_create (scaled_font->font_face,
|
||||
&scaled_font->font_matrix,
|
||||
&new_ctm,
|
||||
&scaled_font->options);
|
||||
if (unlikely (font->status))
|
||||
return font->status;
|
||||
|
||||
status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators,
|
||||
NULL, 0,
|
||||
glyphs, num_glyphs,
|
||||
NULL, 0,
|
||||
FALSE,
|
||||
font);
|
||||
|
||||
cairo_scaled_font_destroy (font);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static const cairo_surface_backend_t cairo_type3_glyph_surface_backend = {
|
||||
CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH,
|
||||
NULL, /* _cairo_type3_glyph_surface_create_similar */
|
||||
_cairo_type3_glyph_surface_finish,
|
||||
NULL, /* acquire_source_image */
|
||||
NULL, /* release_source_image */
|
||||
NULL, /* acquire_dest_image */
|
||||
NULL, /* release_dest_image */
|
||||
NULL, /* clone_similar */
|
||||
NULL, /* composite */
|
||||
NULL, /* fill_rectangles */
|
||||
NULL, /* composite_trapezoids */
|
||||
NULL, /* create_span_renderer */
|
||||
NULL, /* check_span_renderer */
|
||||
NULL, /* cairo_type3_glyph_surface_copy_page */
|
||||
NULL, /* _cairo_type3_glyph_surface_show_page */
|
||||
NULL, /* _cairo_type3_glyph_surface_get_extents */
|
||||
NULL, /* old_show_glyphs */
|
||||
NULL, /* _cairo_type3_glyph_surface_get_font_options */
|
||||
NULL, /* flush */
|
||||
NULL, /* mark_dirty_rectangle */
|
||||
NULL, /* scaled_font_fini */
|
||||
NULL, /* scaled_glyph_fini */
|
||||
_cairo_type3_glyph_surface_paint,
|
||||
_cairo_type3_glyph_surface_mask,
|
||||
_cairo_type3_glyph_surface_stroke,
|
||||
_cairo_type3_glyph_surface_fill,
|
||||
_cairo_type3_glyph_surface_show_glyphs,
|
||||
NULL, /* snapshot */
|
||||
};
|
||||
|
||||
static void
|
||||
_cairo_type3_glyph_surface_set_stream (cairo_type3_glyph_surface_t *surface,
|
||||
cairo_output_stream_t *stream)
|
||||
{
|
||||
surface->stream = stream;
|
||||
_cairo_pdf_operators_set_stream (&surface->pdf_operators, stream);
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
_cairo_type3_glyph_surface_emit_fallback_image (cairo_type3_glyph_surface_t *surface,
|
||||
unsigned long glyph_index)
|
||||
{
|
||||
cairo_scaled_glyph_t *scaled_glyph;
|
||||
cairo_status_t status;
|
||||
cairo_image_surface_t *image;
|
||||
cairo_matrix_t mat;
|
||||
double x, y;
|
||||
|
||||
status = _cairo_scaled_glyph_lookup (surface->scaled_font,
|
||||
glyph_index,
|
||||
CAIRO_SCALED_GLYPH_INFO_METRICS |
|
||||
CAIRO_SCALED_GLYPH_INFO_SURFACE,
|
||||
&scaled_glyph);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
image = scaled_glyph->surface;
|
||||
if (image->width == 0 || image->height == 0)
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
x = _cairo_fixed_to_double (scaled_glyph->bbox.p1.x);
|
||||
y = _cairo_fixed_to_double (scaled_glyph->bbox.p2.y);
|
||||
mat.xx = image->width;
|
||||
mat.xy = 0;
|
||||
mat.yx = 0;
|
||||
mat.yy = image->height;
|
||||
mat.x0 = x;
|
||||
mat.y0 = y;
|
||||
cairo_matrix_multiply (&mat, &mat, &surface->scaled_font->scale_inverse);
|
||||
mat.y0 *= -1;
|
||||
|
||||
return _cairo_type3_glyph_surface_emit_image (surface, image, &mat);
|
||||
}
|
||||
|
||||
void
|
||||
_cairo_type3_glyph_surface_set_font_subsets_callback (void *abstract_surface,
|
||||
cairo_pdf_operators_use_font_subset_t use_font_subset,
|
||||
void *closure)
|
||||
{
|
||||
cairo_type3_glyph_surface_t *surface = abstract_surface;
|
||||
|
||||
if (unlikely (surface->base.status))
|
||||
return;
|
||||
|
||||
_cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators,
|
||||
use_font_subset,
|
||||
closure);
|
||||
}
|
||||
|
||||
cairo_status_t
|
||||
_cairo_type3_glyph_surface_analyze_glyph (void *abstract_surface,
|
||||
unsigned long glyph_index)
|
||||
{
|
||||
cairo_type3_glyph_surface_t *surface = abstract_surface;
|
||||
cairo_scaled_glyph_t *scaled_glyph;
|
||||
cairo_status_t status, status2;
|
||||
cairo_output_stream_t *null_stream;
|
||||
|
||||
if (unlikely (surface->base.status))
|
||||
return surface->base.status;
|
||||
|
||||
null_stream = _cairo_null_stream_create ();
|
||||
if (unlikely (null_stream->status))
|
||||
return null_stream->status;
|
||||
|
||||
_cairo_type3_glyph_surface_set_stream (surface, null_stream);
|
||||
|
||||
_cairo_scaled_font_freeze_cache (surface->scaled_font);
|
||||
status = _cairo_scaled_glyph_lookup (surface->scaled_font,
|
||||
glyph_index,
|
||||
CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE,
|
||||
&scaled_glyph);
|
||||
|
||||
if (_cairo_status_is_error (status))
|
||||
goto cleanup;
|
||||
|
||||
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
|
||||
status = CAIRO_STATUS_SUCCESS;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
status = _cairo_recording_surface_replay (scaled_glyph->recording_surface,
|
||||
&surface->base);
|
||||
if (unlikely (status))
|
||||
goto cleanup;
|
||||
|
||||
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
|
||||
if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK)
|
||||
status = CAIRO_STATUS_SUCCESS;
|
||||
|
||||
cleanup:
|
||||
_cairo_scaled_font_thaw_cache (surface->scaled_font);
|
||||
|
||||
status2 = _cairo_output_stream_destroy (null_stream);
|
||||
if (status == CAIRO_STATUS_SUCCESS)
|
||||
status = status2;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
cairo_status_t
|
||||
_cairo_type3_glyph_surface_emit_glyph (void *abstract_surface,
|
||||
cairo_output_stream_t *stream,
|
||||
unsigned long glyph_index,
|
||||
cairo_box_t *bbox,
|
||||
double *width)
|
||||
{
|
||||
cairo_type3_glyph_surface_t *surface = abstract_surface;
|
||||
cairo_scaled_glyph_t *scaled_glyph;
|
||||
cairo_status_t status, status2;
|
||||
double x_advance, y_advance;
|
||||
cairo_matrix_t font_matrix_inverse;
|
||||
|
||||
if (unlikely (surface->base.status))
|
||||
return surface->base.status;
|
||||
|
||||
_cairo_type3_glyph_surface_set_stream (surface, stream);
|
||||
|
||||
_cairo_scaled_font_freeze_cache (surface->scaled_font);
|
||||
status = _cairo_scaled_glyph_lookup (surface->scaled_font,
|
||||
glyph_index,
|
||||
CAIRO_SCALED_GLYPH_INFO_METRICS |
|
||||
CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE,
|
||||
&scaled_glyph);
|
||||
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
|
||||
status = _cairo_scaled_glyph_lookup (surface->scaled_font,
|
||||
glyph_index,
|
||||
CAIRO_SCALED_GLYPH_INFO_METRICS,
|
||||
&scaled_glyph);
|
||||
if (status == CAIRO_STATUS_SUCCESS)
|
||||
status = CAIRO_INT_STATUS_IMAGE_FALLBACK;
|
||||
}
|
||||
if (_cairo_status_is_error (status)) {
|
||||
_cairo_scaled_font_thaw_cache (surface->scaled_font);
|
||||
return status;
|
||||
}
|
||||
|
||||
x_advance = scaled_glyph->metrics.x_advance;
|
||||
y_advance = scaled_glyph->metrics.y_advance;
|
||||
font_matrix_inverse = surface->scaled_font->font_matrix;
|
||||
status2 = cairo_matrix_invert (&font_matrix_inverse);
|
||||
|
||||
/* The invertability of font_matrix is tested in
|
||||
* pdf_operators_show_glyphs before any glyphs are mapped to the
|
||||
* subset. */
|
||||
assert (status2 == CAIRO_STATUS_SUCCESS);
|
||||
|
||||
cairo_matrix_transform_distance (&font_matrix_inverse, &x_advance, &y_advance);
|
||||
*width = x_advance;
|
||||
|
||||
*bbox = scaled_glyph->bbox;
|
||||
_cairo_matrix_transform_bounding_box_fixed (&surface->scaled_font->scale_inverse,
|
||||
bbox, NULL);
|
||||
|
||||
_cairo_output_stream_printf (surface->stream,
|
||||
"%f 0 %f %f %f %f d1\n",
|
||||
x_advance,
|
||||
_cairo_fixed_to_double (bbox->p1.x),
|
||||
- _cairo_fixed_to_double (bbox->p2.y),
|
||||
_cairo_fixed_to_double (bbox->p2.x),
|
||||
- _cairo_fixed_to_double (bbox->p1.y));
|
||||
|
||||
if (status == CAIRO_STATUS_SUCCESS) {
|
||||
cairo_output_stream_t *mem_stream;
|
||||
|
||||
mem_stream = _cairo_memory_stream_create ();
|
||||
status = mem_stream->status;
|
||||
if (unlikely (status))
|
||||
goto FAIL;
|
||||
|
||||
_cairo_type3_glyph_surface_set_stream (surface, mem_stream);
|
||||
|
||||
_cairo_output_stream_printf (surface->stream, "q\n");
|
||||
status = _cairo_recording_surface_replay (scaled_glyph->recording_surface,
|
||||
&surface->base);
|
||||
|
||||
status2 = _cairo_pdf_operators_flush (&surface->pdf_operators);
|
||||
if (status == CAIRO_STATUS_SUCCESS)
|
||||
status = status2;
|
||||
|
||||
_cairo_output_stream_printf (surface->stream, "Q\n");
|
||||
|
||||
_cairo_type3_glyph_surface_set_stream (surface, stream);
|
||||
if (status == CAIRO_STATUS_SUCCESS)
|
||||
_cairo_memory_stream_copy (mem_stream, stream);
|
||||
|
||||
status2 = _cairo_output_stream_destroy (mem_stream);
|
||||
if (status == CAIRO_STATUS_SUCCESS)
|
||||
status = status2;
|
||||
}
|
||||
|
||||
if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK)
|
||||
status = _cairo_type3_glyph_surface_emit_fallback_image (surface, glyph_index);
|
||||
|
||||
FAIL:
|
||||
_cairo_scaled_font_thaw_cache (surface->scaled_font);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#endif /* CAIRO_HAS_FONT_SUBSET */
|
||||
Reference in New Issue
Block a user