Mon, 14 Dec 2009 16:16:03 +0000
Added tag V0_33_philpem1 for changeset ad0b9a8990ac
philpem@166 | 1 | /* |
philpem@166 | 2 | * tumble: build a PDF file from image files |
philpem@166 | 3 | * |
philpem@166 | 4 | * Copyright 2004 Daniel Gloeckner |
philpem@166 | 5 | * |
philpem@166 | 6 | * Derived from tumble_jpeg.c written 2003 by Eric Smith <eric at brouhaha.com> |
philpem@166 | 7 | * |
philpem@166 | 8 | * This program is free software; you can redistribute it and/or modify |
philpem@166 | 9 | * it under the terms of the GNU General Public License version 2 as |
philpem@166 | 10 | * published by the Free Software Foundation. Note that permission is |
philpem@166 | 11 | * not granted to redistribute this program under the terms of any |
philpem@166 | 12 | * other version of the General Public License. |
philpem@166 | 13 | * |
philpem@166 | 14 | * This program is distributed in the hope that it will be useful, |
philpem@166 | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
philpem@166 | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
philpem@166 | 17 | * GNU General Public License for more details. |
philpem@166 | 18 | * |
philpem@166 | 19 | * You should have received a copy of the GNU General Public License |
philpem@166 | 20 | * along with this program; if not, write to the Free Software |
philpem@166 | 21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA |
philpem@166 | 22 | */ |
philpem@166 | 23 | |
philpem@166 | 24 | |
philpem@166 | 25 | #include <stdbool.h> |
philpem@166 | 26 | #include <stdint.h> |
philpem@166 | 27 | #include <stdio.h> |
philpem@166 | 28 | #include <math.h> |
philpem@166 | 29 | #include <strings.h> /* strcasecmp() is a BSDism */ |
philpem@166 | 30 | |
philpem@166 | 31 | |
philpem@166 | 32 | #include "semantics.h" |
philpem@166 | 33 | #include "tumble.h" |
philpem@166 | 34 | #include "bitblt.h" |
philpem@166 | 35 | #include "pdf.h" |
philpem@166 | 36 | #include "tumble_input.h" |
philpem@166 | 37 | |
philpem@166 | 38 | |
philpem@166 | 39 | static bool match_jp2_suffix (char *suffix) |
philpem@166 | 40 | { |
philpem@166 | 41 | return (strcasecmp (suffix, ".jp2") == 0); |
philpem@166 | 42 | } |
philpem@166 | 43 | |
philpem@166 | 44 | static bool close_jp2_input_file (void) |
philpem@166 | 45 | { |
philpem@166 | 46 | return (1); |
philpem@166 | 47 | } |
philpem@166 | 48 | |
philpem@166 | 49 | static struct { |
philpem@166 | 50 | FILE *f; |
philpem@166 | 51 | uint32_t width,height; |
philpem@166 | 52 | struct { |
philpem@166 | 53 | double VR,HR; |
philpem@166 | 54 | } res[2]; |
philpem@166 | 55 | } jp2info; |
philpem@166 | 56 | |
philpem@166 | 57 | struct box { |
philpem@166 | 58 | char TBox[4]; |
philpem@166 | 59 | bool (*func)(uint64_t size, void *cparam); |
philpem@166 | 60 | void *cparam; |
philpem@166 | 61 | }; |
philpem@166 | 62 | |
philpem@166 | 63 | #define BE32(p) (((p)[0]<<24)+((p)[1]<<16)+((p)[2]<<8)+(p)[3]) |
philpem@166 | 64 | #define BE16(p) (((p)[2]<<8)+(p)[3]) |
philpem@166 | 65 | |
philpem@166 | 66 | static bool skipbox(uint64_t size, void *cparam) |
philpem@166 | 67 | { |
philpem@166 | 68 | if(size==~0) |
philpem@166 | 69 | fseek(jp2info.f,0,SEEK_END); |
philpem@166 | 70 | else { |
philpem@166 | 71 | if(cparam) |
philpem@166 | 72 | size-=*(int *)cparam; |
philpem@166 | 73 | fseek(jp2info.f,size,SEEK_CUR); /*FIXME: size is 64bit*/ |
philpem@166 | 74 | } |
philpem@166 | 75 | return 1; |
philpem@166 | 76 | } |
philpem@166 | 77 | |
philpem@166 | 78 | static bool read_res(uint64_t size, void *cparam) |
philpem@166 | 79 | { |
philpem@166 | 80 | int read=0; |
philpem@166 | 81 | bool ret=1; |
philpem@166 | 82 | if(size>=10) { |
philpem@166 | 83 | int i; |
philpem@166 | 84 | unsigned char buf[10]; |
philpem@166 | 85 | |
philpem@166 | 86 | ret=0; |
philpem@166 | 87 | i=(int)cparam; |
philpem@166 | 88 | read=fread(buf,1,10,jp2info.f); |
philpem@166 | 89 | if(read==10) { |
philpem@166 | 90 | uint16_t vrn,vrd,hrn,hrd; |
philpem@166 | 91 | int8_t vre,hre; |
philpem@166 | 92 | vrn=BE16(buf); |
philpem@166 | 93 | vrd=BE16(buf+2); |
philpem@166 | 94 | hrn=BE16(buf+4); |
philpem@166 | 95 | hrd=BE16(buf+6); |
philpem@166 | 96 | vre=((signed char *)buf)[8]; |
philpem@166 | 97 | hre=((signed char *)buf)[9]; |
philpem@166 | 98 | if(vrn && vrd && hrn && hrd) { |
philpem@166 | 99 | jp2info.res[i].VR=vrn*pow(10.0,vre)/vrd; |
philpem@166 | 100 | jp2info.res[i].HR=hrn*pow(10.0,hre)/hrd; |
philpem@166 | 101 | ret=1; |
philpem@166 | 102 | } |
philpem@166 | 103 | } |
philpem@166 | 104 | } |
philpem@166 | 105 | skipbox(size,&read); |
philpem@166 | 106 | return ret; |
philpem@166 | 107 | } |
philpem@166 | 108 | |
philpem@166 | 109 | static bool read_ihdr(uint64_t size, void *cparam) |
philpem@166 | 110 | { |
philpem@166 | 111 | int read=0; |
philpem@166 | 112 | bool ret=1; |
philpem@166 | 113 | if(size>=8) { |
philpem@166 | 114 | unsigned char buf[8]; |
philpem@166 | 115 | read=fread(buf,1,8,jp2info.f); |
philpem@166 | 116 | if(read==8) { |
philpem@166 | 117 | jp2info.height=BE32(buf); |
philpem@166 | 118 | jp2info.width=BE32(buf+4); |
philpem@166 | 119 | } else |
philpem@166 | 120 | ret=0; |
philpem@166 | 121 | } |
philpem@166 | 122 | skipbox(size,&read); |
philpem@166 | 123 | return ret; |
philpem@166 | 124 | } |
philpem@166 | 125 | |
philpem@166 | 126 | static bool superbox(uint64_t size, void *cparam) |
philpem@166 | 127 | { |
philpem@166 | 128 | while(size>=8) { |
philpem@166 | 129 | static unsigned char buf[12]; |
philpem@166 | 130 | int r; |
philpem@166 | 131 | uint64_t s; |
philpem@166 | 132 | struct box *l; |
philpem@166 | 133 | |
philpem@166 | 134 | r=fread(buf+4,1,8,jp2info.f); |
philpem@166 | 135 | if(r<8) |
philpem@166 | 136 | return (size==~0 && r==0); |
philpem@166 | 137 | |
philpem@166 | 138 | s=BE32(buf+4); |
philpem@166 | 139 | if(s==1 && size>=16) { |
philpem@166 | 140 | r=fread(buf,1,8,jp2info.f); |
philpem@166 | 141 | if(r<8) |
philpem@166 | 142 | return 0; |
philpem@166 | 143 | s=((uint64_t)BE32(buf)<<32)+BE32(buf+4); |
philpem@166 | 144 | } |
philpem@166 | 145 | if(s && s<8) |
philpem@166 | 146 | return 0; |
philpem@166 | 147 | if(size!=~0) { |
philpem@166 | 148 | if(!s) |
philpem@166 | 149 | return 0; |
philpem@166 | 150 | size-=s; |
philpem@166 | 151 | } else if(!s) |
philpem@166 | 152 | size=0; |
philpem@166 | 153 | s=s?s-8:~0; |
philpem@166 | 154 | |
philpem@166 | 155 | for(l=(struct box *)cparam;l->func;l++) |
philpem@166 | 156 | if(!memcmp(l->TBox,buf+8,4)) |
philpem@166 | 157 | break; |
philpem@166 | 158 | if(l->func) { |
philpem@166 | 159 | if(!l->func(s,l->cparam)) |
philpem@166 | 160 | return 0; |
philpem@166 | 161 | }else |
philpem@166 | 162 | if(!skipbox(s,NULL)) |
philpem@166 | 163 | return 0; |
philpem@166 | 164 | } |
philpem@166 | 165 | |
philpem@166 | 166 | return size==0; |
philpem@166 | 167 | } |
philpem@166 | 168 | |
philpem@166 | 169 | static const struct box res_boxes[]={ |
philpem@166 | 170 | {"resc",read_res,(void *)0}, |
philpem@166 | 171 | {"resd",read_res,(void *)1}, |
philpem@166 | 172 | {"",NULL,NULL} |
philpem@166 | 173 | }; |
philpem@166 | 174 | |
philpem@166 | 175 | static const struct box jp2h_boxes[]={ |
philpem@166 | 176 | {"ihdr",read_ihdr,NULL}, |
philpem@166 | 177 | {"res ",superbox,(void *)res_boxes}, |
philpem@166 | 178 | {"",NULL,NULL} |
philpem@166 | 179 | }; |
philpem@166 | 180 | |
philpem@166 | 181 | static const struct box root[]={ |
philpem@166 | 182 | {"jp2h",superbox,(void *)jp2h_boxes}, |
philpem@166 | 183 | {"",NULL,NULL} |
philpem@166 | 184 | }; |
philpem@166 | 185 | |
philpem@166 | 186 | static bool open_jp2_input_file (FILE *f, char *name) |
philpem@166 | 187 | { |
philpem@166 | 188 | int s; |
philpem@166 | 189 | const char sig[12]="\0\0\0\xCjP \r\n\x87\n"; |
philpem@166 | 190 | char buf[12]; |
philpem@166 | 191 | |
philpem@166 | 192 | memset(&jp2info,0,sizeof(jp2info)); |
philpem@166 | 193 | jp2info.f=f; |
philpem@166 | 194 | |
philpem@166 | 195 | s=fread(buf,1,12,f); |
philpem@166 | 196 | rewind(f); |
philpem@166 | 197 | return (s==12 && !memcmp(buf,sig,12)); |
philpem@166 | 198 | } |
philpem@166 | 199 | |
philpem@166 | 200 | |
philpem@166 | 201 | static bool last_jp2_input_page (void) |
philpem@166 | 202 | { |
philpem@166 | 203 | return 1; |
philpem@166 | 204 | } |
philpem@166 | 205 | |
philpem@166 | 206 | |
philpem@166 | 207 | static bool get_jp2_image_info (int image, |
philpem@166 | 208 | input_attributes_t input_attributes, |
philpem@166 | 209 | image_info_t *image_info) |
philpem@166 | 210 | { |
philpem@166 | 211 | int i; |
philpem@166 | 212 | |
philpem@166 | 213 | if(!superbox(~0,(void *)root)) |
philpem@166 | 214 | return 0; |
philpem@166 | 215 | rewind(jp2info.f); |
philpem@166 | 216 | |
philpem@166 | 217 | #ifdef DEBUG_JPEG |
philpem@166 | 218 | printf ("capture x density: %d pixel/m\n", jp2info.res[0].HR); |
philpem@166 | 219 | printf ("capture y density: %d pixel/m\n", jp2info.res[0].VR); |
philpem@166 | 220 | printf ("display x density: %d pixel/m\n", jp2info.res[1].HR); |
philpem@166 | 221 | printf ("display y density: %d pixel/m\n", jp2info.res[1].VR); |
philpem@166 | 222 | printf ("width: %d\n", jp2info.width); |
philpem@166 | 223 | printf ("height: %d\n", jp2info.height); |
philpem@166 | 224 | #endif |
philpem@166 | 225 | |
philpem@166 | 226 | image_info->color = 1; |
philpem@166 | 227 | image_info->width_samples = jp2info.width; |
philpem@166 | 228 | image_info->height_samples = jp2info.height; |
philpem@166 | 229 | |
philpem@166 | 230 | image_info->width_points = (image_info->width_samples * POINTS_PER_INCH) / 300.0; |
philpem@166 | 231 | image_info->height_points = (image_info->height_samples * POINTS_PER_INCH) / 300.0; |
philpem@166 | 232 | |
philpem@166 | 233 | for(i=2;i--;) |
philpem@166 | 234 | if(jp2info.res[i].VR > 0.0 && jp2info.res[i].HR > 0.0) { |
philpem@166 | 235 | image_info->width_points = (image_info->width_samples * POINTS_PER_INCH * 10000)/(jp2info.res[i].HR * 254); |
philpem@166 | 236 | image_info->height_points = (image_info->height_samples * POINTS_PER_INCH * 10000)/(jp2info.res[i].VR * 254); |
philpem@166 | 237 | break; |
philpem@166 | 238 | } |
philpem@166 | 239 | |
philpem@166 | 240 | return 1; |
philpem@166 | 241 | } |
philpem@166 | 242 | |
philpem@166 | 243 | |
philpem@166 | 244 | static bool process_jp2_image (int image, /* range 1 .. n */ |
philpem@166 | 245 | input_attributes_t input_attributes, |
philpem@166 | 246 | image_info_t *image_info, |
philpem@166 | 247 | pdf_page_handle page) |
philpem@166 | 248 | { |
philpem@166 | 249 | pdf_write_jp2_image (page, |
philpem@166 | 250 | 0, 0, /* x, y */ |
philpem@166 | 251 | image_info->width_points, |
philpem@166 | 252 | image_info->height_points, |
philpem@166 | 253 | image_info->width_samples, |
philpem@166 | 254 | image_info->height_samples, |
philpem@166 | 255 | jp2info.f); |
philpem@166 | 256 | |
philpem@166 | 257 | return (1); |
philpem@166 | 258 | } |
philpem@166 | 259 | |
philpem@166 | 260 | |
philpem@166 | 261 | input_handler_t jp2_handler = |
philpem@166 | 262 | { |
philpem@166 | 263 | match_jp2_suffix, |
philpem@166 | 264 | open_jp2_input_file, |
philpem@166 | 265 | close_jp2_input_file, |
philpem@166 | 266 | last_jp2_input_page, |
philpem@166 | 267 | get_jp2_image_info, |
philpem@166 | 268 | process_jp2_image |
philpem@166 | 269 | }; |
philpem@166 | 270 | |
philpem@166 | 271 | |
philpem@166 | 272 | void init_jp2_handler (void) |
philpem@166 | 273 | { |
philpem@166 | 274 | install_input_handler (& jp2_handler); |
philpem@166 | 275 | } |