Back to index

texmacs  1.0.7.15
sfnt.c
Go to the documentation of this file.
00001 /*  $Header: /home/cvsroot/dvipdfmx/src/sfnt.c,v 1.14 2008/05/22 10:08:02 matthias Exp $
00002     
00003     This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
00004 
00005     Copyright (C) 2002 by Jin-Hwan Cho and Shunsaku Hirata,
00006     the dvipdfmx project team <dvipdfmx@project.ktug.or.kr>
00007     
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012     
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017     
00018     You should have received a copy of the GNU General Public License
00019     along with this program; if not, write to the Free Software
00020     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00021 */
00022 
00023 /* Based on dvipdfmx-0.13.2c */
00024 
00025 #ifdef  _HAVE_CONFIG_H
00026 #include "config.h"
00027 #endif /* _HAVE_CONFIG_H_ */
00028 
00029 #include <string.h>
00030 
00031 #include "system.h"
00032 
00033 #include "error.h"
00034 #include "mem.h"
00035 #include "mfileio.h"
00036 
00037 #include "sfnt.h"
00038 
00039 
00040 sfnt *
00041 sfnt_open (FILE *fp)
00042 {
00043   sfnt  *sfont;
00044   ULONG  type;
00045 
00046   ASSERT(fp);
00047 
00048   rewind(fp);
00049 
00050   sfont = NEW(1, sfnt);
00051 
00052   sfont->stream = fp;
00053 
00054 /*
00055  * type:
00056  *  `true' (0x74727565): TrueType (Mac)
00057  *  `typ1' (0x74797031) (Mac): PostScript font housed in a sfnt wrapper
00058  *  0x00010000: TrueType (Win)/OpenType
00059  *  `OTTO': PostScript CFF font with OpenType wrapper
00060  *  `ttcf': TrueType Collection
00061 */
00062 #define SFNT_TRUETYPE   0x00010000UL
00063 #define SFNT_OPENTYPE   0x00010000UL
00064 #define SFNT_POSTSCRIPT 0x4f54544fUL
00065 #define SFNT_TTC        0x74746366UL
00066 
00067   type = sfnt_get_ulong(sfont);
00068 
00069   if (type == SFNT_TRUETYPE) {
00070     sfont->type = SFNT_TYPE_TRUETYPE;
00071   } else if (type == SFNT_OPENTYPE) {
00072     sfont->type = SFNT_TYPE_OPENTYPE;
00073   } else if (type == SFNT_POSTSCRIPT) { 
00074     sfont->type = SFNT_TYPE_POSTSCRIPT;
00075   } else if (type == SFNT_TTC) {
00076     sfont->type = SFNT_TYPE_TTC;
00077   }
00078 
00079   rewind(sfont->stream);
00080 
00081   sfont->directory = NULL;
00082   sfont->offset = 0UL;
00083 
00084   return sfont;
00085 }
00086 
00087 sfnt *
00088 dfont_open (FILE *fp, int index)
00089 {
00090   sfnt  *sfont;
00091   ULONG  rdata_pos, map_pos, tags_pos, types_pos, res_pos, tag;
00092   USHORT tags_num, types_num, i;
00093 
00094   ASSERT(fp);
00095 
00096   rewind(fp);
00097 
00098   sfont = NEW(1, sfnt);
00099 
00100   sfont->stream = fp;
00101 
00102   rdata_pos = sfnt_get_ulong(sfont);
00103   map_pos   = sfnt_get_ulong(sfont);
00104   sfnt_seek_set(sfont, map_pos + 0x18);
00105   tags_pos = map_pos + sfnt_get_ushort(sfont);
00106   sfnt_seek_set(sfont, tags_pos);
00107   tags_num = sfnt_get_ushort(sfont);
00108 
00109   for (i = 0; i <= tags_num; i++) {
00110     tag = sfnt_get_ulong(sfont); /* tag name */
00111     types_num = sfnt_get_ushort(sfont); /* typefaces number */
00112     types_pos = tags_pos + sfnt_get_ushort(sfont); /* typefaces position */
00113     if (tag == 0x73666e74UL) /* "sfnt" */
00114       break;
00115   }
00116 
00117   if (i > tags_num) {
00118     RELEASE(sfont);
00119     return NULL;
00120   }
00121 
00122   sfnt_seek_set(sfont, types_pos);
00123   if (index > types_num) {
00124     ERROR("Invalid index %d for dfont.", index);
00125   }
00126 
00127   for (i = 0; i <= types_num; i++) {
00128     (void) sfnt_get_ushort(sfont); /* resource id */
00129     (void) sfnt_get_ushort(sfont); /* resource name position from name_list */
00130     res_pos = sfnt_get_ulong(sfont);   /* resource flag (byte) + resource offset */
00131     sfnt_get_ulong(sfont);  /* mbz */
00132     if (i == index) break;
00133   }
00134 
00135   rewind(sfont->stream);
00136 
00137   sfont->type = SFNT_TYPE_DFONT;
00138   sfont->directory = NULL;
00139   sfont->offset = (res_pos & 0x00ffffffUL) + rdata_pos + 4;
00140 
00141   return sfont;
00142 }
00143 
00144 static void
00145 release_directory (struct sfnt_table_directory *td)
00146 {
00147   long i;
00148 
00149   if (td) {
00150     if (td->tables) {
00151       for (i = 0; i < td->num_tables; i++) {
00152        if (td->tables[i].data)
00153          RELEASE(td->tables[i].data);
00154       }
00155       RELEASE(td->tables);
00156     }
00157     if (td->flags)
00158       RELEASE(td->flags);
00159     RELEASE(td);
00160   }
00161 
00162   return;
00163 }
00164 
00165 void
00166 sfnt_close (sfnt *sfont)
00167 {
00168 
00169   if (sfont) {
00170     if (sfont->directory)
00171       release_directory(sfont->directory);
00172     RELEASE(sfont);
00173   }
00174 
00175   return;
00176 }
00177 
00178 int
00179 put_big_endian (void *s, LONG q, int n)
00180 {
00181   int   i;
00182   char *p;
00183 
00184   p = (char *) s;
00185   for (i = n - 1; i >= 0; i--) {
00186     p[i] = (char) (q & 0xff);
00187     q >>= 8;
00188   }
00189 
00190   return n;
00191 }
00192 
00193 /* Convert four-byte number to big endianess
00194  * in a machine independent way.
00195  */
00196 static void
00197 convert_tag (char *tag, unsigned long u_tag)
00198 {
00199   int i;
00200 
00201   for (i = 3; i >= 0; i--) {
00202     tag[i] = (char) (u_tag % 256);
00203     u_tag /= 256;
00204   }
00205 
00206   return;
00207 }
00208 
00209 /*
00210  * Computes the max power of 2 <= n
00211  */
00212 static unsigned
00213 max2floor (unsigned n)
00214 {
00215   int val = 1;
00216 
00217   while (n > 1) {
00218     n   /= 2;
00219     val *= 2;
00220   }
00221 
00222   return val;
00223 }
00224 
00225 /*
00226  * Computes the log2 of the max power of 2 <= n
00227  */
00228 static unsigned
00229 log2floor (unsigned n)
00230 {
00231   unsigned val = 0;
00232 
00233   while (n > 1) {
00234     n /= 2;
00235     val++;
00236   }
00237 
00238   return val;
00239 }
00240 
00241 static ULONG
00242 sfnt_calc_checksum(void *data, ULONG length)
00243 {
00244   ULONG  chksum = 0;
00245   BYTE  *p, *endptr;
00246   int    count  = 0;
00247 
00248   p      = (BYTE *) data;
00249   endptr = p + length;
00250   while (p < endptr) {
00251     chksum += (p[0] << (8 * ( 3 - count)));
00252     count   = ((count + 1) & 3);
00253     p++;
00254   }
00255 
00256   return chksum;
00257 }
00258 
00259 static int
00260 find_table_index (struct sfnt_table_directory *td, const char *tag)
00261 {
00262   int  idx;
00263 
00264   if (!td)
00265     return -1;
00266 
00267   for (idx = 0; idx < td->num_tables; idx++) {
00268     if (!memcmp(td->tables[idx].tag, tag, 4))
00269       return idx;
00270   }
00271 
00272   return -1;
00273 }
00274 
00275 void
00276 sfnt_set_table (sfnt *sfont, const char *tag, void *data, ULONG length)
00277 {
00278   struct sfnt_table_directory *td;
00279   int    idx;
00280 
00281   ASSERT(sfont);
00282 
00283   td  = sfont->directory;
00284   idx = find_table_index(td, tag);
00285 
00286   if (idx < 0) {
00287     idx = td->num_tables;
00288     td->num_tables++;
00289     td->tables = RENEW(td->tables, td->num_tables, struct sfnt_table);
00290     memcpy(td->tables[idx].tag, tag, 4);
00291   }
00292 
00293   td->tables[idx].check_sum = sfnt_calc_checksum(data, length);
00294   td->tables[idx].offset    = 0L;
00295   td->tables[idx].length    = length;
00296   td->tables[idx].data      = data;
00297 
00298   return;
00299 }
00300 
00301 ULONG
00302 sfnt_find_table_len (sfnt *sfont, const char *tag)
00303 {
00304   ULONG  length;
00305   struct sfnt_table_directory *td;
00306   int    idx;
00307 
00308   ASSERT(sfont && tag);
00309 
00310   td  = sfont->directory;
00311   idx = find_table_index(td, tag);
00312   if (idx < 0)
00313     length = 0;
00314   else {
00315     length = td->tables[idx].length;
00316   }
00317 
00318   return length;
00319 }
00320 
00321 ULONG
00322 sfnt_find_table_pos (sfnt *sfont, const char *tag) 
00323 {
00324   ULONG  offset;
00325   struct sfnt_table_directory *td;
00326   int    idx;
00327 
00328   ASSERT(sfont && tag);
00329 
00330   td  = sfont->directory;
00331   idx = find_table_index(td, tag);
00332   if (idx < 0)
00333     offset = 0;
00334   else {
00335     offset = td->tables[idx].offset;
00336   }
00337 
00338   return offset;
00339 }
00340 
00341 ULONG
00342 sfnt_locate_table (sfnt *sfont, const char *tag)
00343 {
00344   ULONG offset;
00345 
00346   ASSERT(sfont && tag);
00347 
00348   offset = sfnt_find_table_pos(sfont, tag);
00349   if (offset == 0)
00350     ERROR("sfnt: table not found...");
00351 
00352   sfnt_seek_set(sfont, offset);
00353 
00354   return offset;
00355 }
00356 
00357 int
00358 sfnt_read_table_directory (sfnt *sfont, ULONG offset)
00359 {
00360   struct sfnt_table_directory *td;
00361   unsigned long i, u_tag;
00362 
00363   ASSERT(sfont && sfont->stream);
00364 
00365   sfnt_seek_set(sfont, offset);
00366 
00367   if (sfont->directory)
00368     release_directory(sfont->directory);    
00369 
00370   sfont->directory = td = NEW (1, struct sfnt_table_directory);
00371 
00372   td->version      = sfnt_get_ulong(sfont);
00373   td->num_tables   = sfnt_get_ushort(sfont);
00374   td->search_range = sfnt_get_ushort(sfont);
00375   td->entry_selector = sfnt_get_ushort(sfont);
00376   td->range_shift    = sfnt_get_ushort(sfont);
00377 
00378   td->flags  = NEW(td->num_tables, char);
00379   td->tables = NEW(td->num_tables, struct sfnt_table);
00380 
00381   for (i = 0; i < td->num_tables; i++) {
00382     u_tag = sfnt_get_ulong(sfont);
00383 
00384     convert_tag(td->tables[i].tag, u_tag);
00385     td->tables[i].check_sum = sfnt_get_ulong(sfont);
00386     td->tables[i].offset    = sfnt_get_ulong(sfont) + sfont->offset;
00387     td->tables[i].length    = sfnt_get_ulong(sfont);
00388     td->tables[i].data      = NULL;
00389 //fprintf(stderr, "[%4s:%x]", td->tables[i].tag, td->tables[i].offset);
00390 
00391     td->flags[i] = 0;
00392   }
00393   td->num_kept_tables = 0;
00394 
00395   return 0;
00396 }
00397 
00398 int
00399 sfnt_require_table (sfnt *sfont, const char *tag, int must_exist)
00400 {
00401   struct sfnt_table_directory *td;
00402   int    idx;
00403 
00404   ASSERT(sfont && sfont->directory);
00405 
00406   td  = sfont->directory;
00407   idx = find_table_index(td, tag);
00408   if (idx < 0) {
00409     if (must_exist)
00410       return -1;
00411   } else {
00412     td->flags[idx] |= SFNT_TABLE_REQUIRED;
00413     td->num_kept_tables++;
00414   }
00415 
00416   return 0;
00417 }
00418 
00419 #include "pdfobj.h"
00420 
00421 /* 
00422  * o All tables begin on four byte boundries, and pad any remaining space
00423  *   between tables with zeros
00424  *
00425  * o Entries in the Table Directory must be sorted in ascending order by tag
00426  *
00427  * o The head table contains checksum of the whole font file.
00428  *   To compute:  first set it to 0, sum the entire font as ULONG,
00429  *   then store 0xB1B0AFBA - sum.
00430  */
00431 
00432 static unsigned char wbuf[1024], padbytes[4] = {0, 0, 0, 0};
00433 
00434 pdf_obj *
00435 sfnt_create_FontFile_stream (sfnt *sfont)
00436 {
00437   pdf_obj *stream;
00438   pdf_obj *stream_dict;
00439   struct sfnt_table_directory *td;
00440   long     offset, nb_read, length;
00441   int      i, sr;
00442   char    *p;
00443 
00444   ASSERT(sfont && sfont->directory);
00445 
00446   stream = pdf_new_stream(STREAM_COMPRESS);
00447 
00448   td  = sfont->directory;
00449 
00450   /* Header */
00451   p  = (char *) wbuf;
00452   p += sfnt_put_ulong (p, td->version);
00453   p += sfnt_put_ushort(p, td->num_kept_tables);
00454   sr = max2floor(td->num_kept_tables) * 16;
00455   p += sfnt_put_ushort(p, sr);
00456   p += sfnt_put_ushort(p, log2floor(td->num_kept_tables));
00457   p += sfnt_put_ushort(p, td->num_kept_tables * 16 - sr);
00458 
00459   pdf_add_stream(stream, wbuf, 12);
00460 
00461   /*
00462    * Compute start of actual tables (after headers).
00463    */
00464   offset = 12 + 16 * td->num_kept_tables;
00465   for (i = 0; i < td->num_tables; i++) {
00466     /* This table must exist in FontFile */
00467     if (td->flags[i] & SFNT_TABLE_REQUIRED) {
00468       if ((offset % 4) != 0) {
00469        offset += 4 - (offset % 4);
00470       }
00471 
00472       p = (char *) wbuf;
00473       memcpy(p, td->tables[i].tag, 4);
00474       p += 4;
00475       p += sfnt_put_ulong(p, td->tables[i].check_sum);
00476       p += sfnt_put_ulong(p, offset);
00477       p += sfnt_put_ulong(p, td->tables[i].length);
00478       pdf_add_stream(stream, wbuf, 16);
00479 
00480       offset += td->tables[i].length;
00481     }
00482   }
00483 
00484   offset = 12 + 16 * td->num_kept_tables;
00485   for (i = 0; i < td->num_tables; i++) {
00486     if (td->flags[i] & SFNT_TABLE_REQUIRED) {
00487       if ((offset % 4) != 0) {
00488        length  = 4 - (offset % 4);
00489        pdf_add_stream(stream, padbytes, length);
00490        offset += length;
00491       }
00492       if (!td->tables[i].data) {
00493        if (!sfont->stream) {
00494          pdf_release_obj(stream);
00495          ERROR("Font file not opened or already closed...");
00496          return NULL;
00497        }
00498 
00499        length = td->tables[i].length;
00500        sfnt_seek_set(sfont, td->tables[i].offset); 
00501        while (length > 0) {
00502          nb_read = sfnt_read(wbuf, MIN(length, 1024), sfont);
00503          if (nb_read < 0) {
00504            pdf_release_obj(stream);
00505            ERROR("Reading file failed...");
00506            return NULL;
00507          } else if (nb_read > 0) {
00508            pdf_add_stream(stream, wbuf, nb_read);
00509          }
00510          length -= nb_read;
00511        }
00512       } else {
00513        pdf_add_stream(stream,
00514                      td->tables[i].data, td->tables[i].length);
00515        RELEASE(td->tables[i].data);
00516        td->tables[i].data = NULL;
00517       }
00518       /* Set offset for next table */
00519       offset += td->tables[i].length;
00520     }
00521   }
00522 
00523   stream_dict = pdf_stream_dict(stream);
00524   pdf_add_dict(stream_dict,
00525               pdf_new_name("Length1"),
00526               pdf_new_number(offset));
00527 
00528   return stream;
00529 }