1
0
mirror of synced 2026-01-12 00:02:50 +00:00
brouhaha.tumble/tumble_png.c
2022-05-06 01:51:04 -06:00

241 lines
5.7 KiB
C

/*
* tumble: build a PDF file from image files
*
* Copyright 2004 Daniel Gloeckner
*
* Derived from tumble_jpeg.c written 2003 by Eric Smith <spacewar@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. Note that permission is
* not granted to redistribute this program under the terms of any
* other version of the General Public License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
*
* 2009-03-13 [JDB] New module to add PNG image support.
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <strings.h> /* strcasecmp() is a BSDism */
#include "semantics.h"
#include "tumble.h"
#include "bitblt.h"
#include "pdf.h"
#include "tumble_input.h"
static FILE *png_f;
static struct {
uint32_t palent;
uint8_t bpp;
uint8_t color;
char pal[256*3];
} cinfo;
static bool match_png_suffix (char *suffix)
{
return strcasecmp (suffix, ".png") == 0;
}
static bool close_png_input_file (void)
{
return true;
}
#define BENUM(p) (((p)[0]<<24)+((p)[1]<<16)+((p)[2]<<8)+(p)[3])
static bool open_png_input_file (FILE *f, char *name)
{
const char sig [8]="\211PNG\r\n\032\n";
uint8_t buf [8];
int l;
l = fread (buf, 1, sizeof (sig), f);
if (l != sizeof (sig) || memcmp(buf,sig,sizeof(sig))) {
rewind(f);
return 0;
}
png_f = f;
return 1;
}
static bool last_png_input_page (void)
{
return 1;
}
static bool get_png_image_info (int image,
input_attributes_t input_attributes,
image_info_t *image_info)
{
uint8_t buf [20];
uint8_t unit;
uint32_t width = 0; // compiler isn't smart enough to tell that there's
uint32_t height = 0; // no way past the for(;;) loop that doesn't init these
uint32_t xppu;
uint32_t yppu;
size_t l;
bool seen_IHDR;
bool seen_PLTE;
bool seen_pHYs;
seen_IHDR=seen_PLTE=seen_pHYs=false;
memset(&cinfo,0,sizeof(cinfo));
unit=0;
xppu=yppu=1;
for(;;)
{
l = fread (buf, 1, 8, png_f);
if(l != 8)
return 0;
l=BENUM(buf);
if(!memcmp(buf+4,"IHDR",4)) {
if(seen_IHDR || l!=13)
return 0;
seen_IHDR=true;
l = fread (buf, 1, 17, png_f);
if(l!=17)
return 0;
width=BENUM(buf);
height=BENUM(buf+4);
cinfo.bpp=buf[8];
cinfo.color=buf[9];
if(buf[8]>8 || buf[10] || buf[11] || buf[12])
return 0;
continue;
}
if(!seen_IHDR)
return 0;
if(!memcmp(buf+4,"PLTE",4)) {
size_t i;
if(seen_PLTE || l>256*3 || l%3 || !cinfo.color)
return 0;
seen_PLTE=true;
i = fread (cinfo.pal, 1, l, png_f);
if(i != l)
return 0;
cinfo.palent=l/3;
fseek(png_f,4,SEEK_CUR);
} else if(!memcmp(buf+4,"pHYs",4)) {
if(seen_pHYs || l!=9)
return 0;
seen_pHYs=true;
l = fread (buf, 1, 13, png_f);
if(l != 13)
return 0;
xppu=BENUM(buf);
yppu=BENUM(buf+4);
unit=buf[8];
} else if(!memcmp(buf+4,"IDAT",4)) {
fseek(png_f,-8,SEEK_CUR);
break;
} else {
fseek(png_f,l+4,SEEK_CUR);
}
}
if(cinfo.color==3 && !seen_PLTE)
return 0;
#ifdef DEBUG_JPEG
printf ("color type: %d\n", cinfo.color);
printf ("bit depth: %d\n", cinfo.bpp);
printf ("density unit: %d\n", unit);
printf ("x density: %d\n", xppu);
printf ("y density: %d\n", yppu);
printf ("width: %d\n", width);
printf ("height: %d\n", height);
#endif
switch (cinfo.color)
{
case 0:
image_info->color = 0;
break;
case 2:
case 3:
image_info->color = 1;
break;
default:
fprintf (stderr, "PNG color type %d not supported\n", cinfo.color);
return false;
}
image_info->width_samples = width;
image_info->height_samples = height;
if (unit == 1) {
image_info->width_points = ((image_info->width_samples * POINTS_PER_INCH) /
(xppu * 0.0254));
image_info->height_points = ((image_info->height_samples * POINTS_PER_INCH) /
(yppu * 0.0254));
} else if (unit == 0) {
/* assume 300 DPI - not great, but what else can we do? */
image_info->width_points = (image_info->width_samples * POINTS_PER_INCH) / 300.0;
image_info->height_points = ((double) yppu * image_info->height_samples * POINTS_PER_INCH) / ( 300.0 * xppu);
} else {
fprintf (stderr, "PNG pHYs unit %d not supported\n", unit);
}
return 1;
}
static bool process_png_image (int image, /* range 1 .. n */
input_attributes_t input_attributes,
image_info_t *image_info,
pdf_page_handle page,
output_attributes_t output_attributes)
{
pdf_write_png_image (page,
output_attributes.position.x, output_attributes.position.y,
image_info->width_points,
image_info->height_points,
cinfo.color,
cinfo.color==3?cinfo.pal:NULL,
cinfo.palent,
cinfo.bpp,
image_info->width_samples,
image_info->height_samples,
input_attributes.transparency,
png_f);
return true;
}
input_handler_t png_handler =
{
match_png_suffix,
open_png_input_file,
close_png_input_file,
last_png_input_page,
get_png_image_info,
process_png_image
};
void init_png_handler (void)
{
install_input_handler (& png_handler);
}